Skip to content

Commit 177c887

Browse files
committed
fix: split-button tests & lots of cleanup based on review feedback
I'd do more on the manageSelection changes here, but I think we can remove a lot of this by using new options from #1189
1 parent e826b3c commit 177c887

File tree

5 files changed

+97
-162
lines changed

5 files changed

+97
-162
lines changed

packages/picker/src/Picker.ts

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import pickerStyles from './picker.css.js';
2727
import chevronStyles from '@spectrum-web-components/icon/src/spectrum-icon-chevron.css.js';
2828

2929
import { Focusable } from '@spectrum-web-components/shared/src/focusable.js';
30-
import reparentChildren from '@spectrum-web-components/shared/src/reparent-children.js';
30+
import { reparentChildren } from '@spectrum-web-components/shared/src/reparent-children.js';
3131
import '@spectrum-web-components/icon/sp-icon.js';
3232
import '@spectrum-web-components/icons-workflow/icons/sp-icon-alert.js';
3333
import '@spectrum-web-components/menu/sp-menu.js';
@@ -38,6 +38,7 @@ import {
3838
MenuQueryRoleEventDetail,
3939
} from '@spectrum-web-components/menu';
4040
import '@spectrum-web-components/popover/sp-popover.js';
41+
import { Popover } from '@spectrum-web-components/popover/src/popover.js';
4142
import {
4243
Placement,
4344
openOverlay,
@@ -94,9 +95,10 @@ export class PickerBase extends SizedMixin(Focusable) {
9495
@property({ type: Boolean, reflect: true })
9596
public open = false;
9697

97-
private reparentableChildren?: Element[];
98+
public menuItems: MenuItem[] = [];
9899
private restoreChildren?: Function;
99-
public optionsMenu?: Menu;
100+
101+
public optionsMenu!: Menu;
100102

101103
/**
102104
* @type {"auto" | "auto-start" | "auto-end" | "top" | "bottom" | "right" | "left" | "top-start" | "top-end" | "bottom-start" | "bottom-end" | "right-start" | "right-end" | "left-start" | "left-end" | "none"}
@@ -118,7 +120,7 @@ export class PickerBase extends SizedMixin(Focusable) {
118120
private closeOverlay?: () => void;
119121

120122
@query('sp-popover')
121-
private popover?: HTMLElement;
123+
private popover!: Popover;
122124

123125
protected listRole = 'listbox';
124126
protected itemRole = 'option';
@@ -144,7 +146,7 @@ export class PickerBase extends SizedMixin(Focusable) {
144146
}
145147

146148
public get focusElement(): HTMLElement {
147-
if (this.open && this.optionsMenu) {
149+
if (this.open) {
148150
return this.optionsMenu;
149151
}
150152
return this.button;
@@ -192,10 +194,6 @@ export class PickerBase extends SizedMixin(Focusable) {
192194
return;
193195
}
194196
event.preventDefault();
195-
/* c8 ignore next 3 */
196-
if (!this.optionsMenu) {
197-
return;
198-
}
199197
this.open = true;
200198
};
201199

@@ -243,18 +241,26 @@ export class PickerBase extends SizedMixin(Focusable) {
243241

244242
private async openMenu(): Promise<void> {
245243
/* c8 ignore next 9 */
246-
if (
247-
!this.popover ||
248-
!this.optionsMenu ||
249-
!this.reparentableChildren ||
250-
this.reparentableChildren.length === 0
251-
) {
244+
let reparentableChildren: Element[] = [];
245+
246+
const deprecatedMenu = this.querySelector('sp-menu');
247+
if (deprecatedMenu) {
248+
reparentableChildren = Array.from(deprecatedMenu.children);
249+
} else {
250+
reparentableChildren = Array.from(this.children).filter(
251+
(element) => {
252+
return !element.hasAttribute('slot');
253+
}
254+
);
255+
}
256+
257+
if (reparentableChildren.length === 0) {
252258
this.menuStateResolver();
253259
return;
254260
}
255261

256262
this.restoreChildren = reparentChildren(
257-
this.reparentableChildren,
263+
reparentableChildren,
258264
this.optionsMenu
259265
);
260266

@@ -331,21 +337,27 @@ export class PickerBase extends SizedMixin(Focusable) {
331337

332338
protected render(): TemplateResult {
333339
return html`
334-
${this.renderButton}
340+
${this.renderButton} ${this.renderPopover}
341+
`;
342+
}
343+
344+
protected get renderPopover(): TemplateResult {
345+
return html`
335346
<sp-popover
336347
open
337348
id="popover"
338349
@click=${this.onClick}
339350
@sp-overlay-closed=${this.onOverlayClosed}
340351
>
341-
<sp-menu id="menu" role="listbox"></sp-menu>
352+
<sp-menu id="menu" role="${this.listRole}"></sp-menu>
342353
</sp-popover>
343354
`;
344355
}
345356

346357
protected firstUpdated(changedProperties: PropertyValues): void {
347358
super.firstUpdated(changedProperties);
348359

360+
// Since the sp-menu gets reparented by the popover, initialize it here
349361
this.optionsMenu = this.shadowRoot.querySelector('sp-menu') as Menu;
350362

351363
const deprecatedMenu = this.querySelector('sp-menu');
@@ -354,18 +366,14 @@ export class PickerBase extends SizedMixin(Focusable) {
354366
`Deprecation Notice: You no longer need to provide an sp-menu child to ${this.tagName.toLowerCase()}. Any styling or attributes on the sp-menu will be ignored.`
355367
);
356368
}
357-
this.reparentableChildren = Array.from(
358-
(deprecatedMenu || this).children
359-
);
369+
this.menuItems = [
370+
...this.querySelectorAll(`sp-menu-item`),
371+
] as MenuItem[];
360372
}
361373

362374
protected updated(changedProperties: PropertyValues): void {
363375
super.updated(changedProperties);
364-
if (
365-
changedProperties.has('value') &&
366-
!changedProperties.has('selectedItem') &&
367-
this.reparentableChildren
368-
) {
376+
if (changedProperties.has('value') && !changedProperties.has('selectedItem')) {
369377
this.manageSelection();
370378
}
371379
if (changedProperties.has('disabled') && this.disabled) {
@@ -386,20 +394,15 @@ export class PickerBase extends SizedMixin(Focusable) {
386394
}
387395
}
388396

389-
private async manageSelection(): Promise<void> {
397+
protected async manageSelection(): Promise<void> {
390398
/* c8 ignore next 3 */
391-
if (!this.reparentableChildren) {
392-
return;
393-
}
394-
if (this.reparentableChildren.length) {
399+
if (this.menuItems.length > 0) {
395400
let selectedItem: MenuItem | undefined;
396-
this.reparentableChildren.map((item) => {
397-
if (item instanceof MenuItem) {
398-
if (this.value === item.value && !item.disabled) {
399-
selectedItem = item;
400-
} else {
401-
item.selected = false;
402-
}
401+
this.menuItems.forEach((item) => {
402+
if (this.value === item.value && !item.disabled) {
403+
selectedItem = item;
404+
} else {
405+
item.selected = false;
403406
}
404407
});
405408
if (selectedItem) {
@@ -409,12 +412,12 @@ export class PickerBase extends SizedMixin(Focusable) {
409412
this.value = '';
410413
this.selectedItem = undefined;
411414
}
412-
if (this.open && this.optionsMenu) {
415+
if (this.open) {
413416
this.optionsMenu.updateSelectedItemIndex();
414417
}
415418
return;
416419
}
417-
if (this.open && this.optionsMenu) {
420+
if (this.open) {
418421
await this.optionsMenu.updateComplete;
419422
if (this.optionsMenu.menuItems.length) {
420423
this.manageSelection();

packages/picker/test/picker.test.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -488,11 +488,9 @@ describe('Picker', () => {
488488
expect(el.open).to.be.true;
489489
expect(isMenuActiveElement()).to.be.true;
490490

491-
const menu = document.activeElement;
492-
if (menu) {
493-
// we know this is defined, but typescript doesn't
494-
menu.dispatchEvent(tabEvent);
495-
}
491+
const menu = document.activeElement as Menu;
492+
menu.dispatchEvent(tabEvent);
493+
496494
await elementUpdated(el);
497495
await waitUntil(() => !el.open);
498496

packages/shared/src/reparent-children.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,6 @@ function restoreChildren(
22
placeholderItems: Comment[],
33
srcElements: Element[]
44
): void {
5-
if (srcElements.length != placeholderItems.length) {
6-
throw new Error(
7-
'Unexpected length mismatch attempting to restore parent elements'
8-
);
9-
}
105
for (let index = 0; index < srcElements.length; ++index) {
116
const srcElement = srcElements[index];
127
const placeholderItem = placeholderItems[index];
@@ -17,7 +12,10 @@ function restoreChildren(
1712
}
1813
}
1914

20-
export default function (srcElements: Element[], newParent: Element): Function {
15+
export const reparentChildren = (
16+
srcElements: Element[],
17+
newParent: Element
18+
): Function => {
2119
let placeholderItems: Comment[] = [];
2220

2321
for (let index = 0; index < srcElements.length; ++index) {
@@ -36,4 +34,4 @@ export default function (srcElements: Element[], newParent: Element): Function {
3634
return function () {
3735
restoreChildren(placeholderItems, srcElements);
3836
};
39-
}
37+
};

0 commit comments

Comments
 (0)