Skip to content

Commit

Permalink
fix: allow Picker to be reparented
Browse files Browse the repository at this point in the history
  • Loading branch information
Westbrook Johnson authored and Westbrook committed Apr 21, 2022
1 parent dbf8471 commit 39e7309
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 49 deletions.
6 changes: 5 additions & 1 deletion packages/overlay/src/ActiveOverlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,12 +295,16 @@ export class ActiveOverlay extends SpectrumElement {
this.childrenReady = Promise.all(actions);
}

public async openCallback(): Promise<void> {
public async openCallback(
livecycleCallback: () => Promise<void> | void
): Promise<void> {
await this.updateComplete;
if (this.receivesFocus) {
await this.focus();
}

await livecycleCallback();

this.trigger.dispatchEvent(
new CustomEvent<OverlayOpenCloseDetail>('sp-opened', {
bubbles: true,
Expand Down
10 changes: 7 additions & 3 deletions packages/overlay/src/overlay-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,11 +300,15 @@ export class OverlayStack {
if (typeof contentWithLifecycle.open !== 'undefined') {
contentWithLifecycle.open = true;
}
let cb: () => Promise<void> | void = () => {
return;
};
if (contentWithLifecycle.overlayOpenCallback) {
const { trigger } = activeOverlay;
contentWithLifecycle.overlayOpenCallback({ trigger });
const { overlayOpenCallback } = contentWithLifecycle;
cb = async () => await overlayOpenCallback({ trigger });
}
activeOverlay.openCallback();
await activeOverlay.openCallback(cb);
return false;
}
);
Expand Down Expand Up @@ -495,7 +499,7 @@ export class OverlayStack {
}
if (contentWithLifecycle.overlayCloseCallback) {
const { trigger } = overlay;
contentWithLifecycle.overlayCloseCallback({ trigger });
await contentWithLifecycle.overlayCloseCallback({ trigger });
}

if (overlay.state != 'dispose') return;
Expand Down
61 changes: 29 additions & 32 deletions packages/picker/src/Picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,19 +260,21 @@ export class PickerBase extends SizedMixin(Focusable) {
this.open = false;
}

public overlayCloseCallback = (): void => {
this.open = false;
public overlayOpenCallback = async (): Promise<void> => {
this.updateMenuItems();
await this.itemsUpdated;
await this.optionsMenu.updateComplete;
requestAnimationFrame(() => this.menuStateResolver());
};

protected onOverlayClosed(): void {
this.close();
public overlayCloseCallback = async (): Promise<void> => {
if (this.restoreChildren) {
this.restoreChildren();
this.restoreChildren = undefined;
}

this.menuStateResolver();
}
this.close();
requestAnimationFrame(() => this.menuStateResolver());
};

private popoverFragment!: DocumentFragment;

Expand Down Expand Up @@ -326,18 +328,6 @@ export class PickerBase extends SizedMixin(Focusable) {
});

this.sizePopover(this.popover);
this.addEventListener(
'sp-opened',
async () => {
this.updateMenuItems();
await Promise.all([
this.itemsUpdated,
this.optionsMenu.updateComplete,
]);
this.menuStateResolver();
},
{ once: true }
);
this.closeOverlay = Picker.openOverlay(this, 'modal', this.popover, {
placement: this.isMobile.matches ? 'none' : this.placement,
receivesFocus: 'auto',
Expand Down Expand Up @@ -471,7 +461,7 @@ export class PickerBase extends SizedMixin(Focusable) {
id="popover"
role="dialog"
@sp-menu-item-added-or-updated=${this.updateMenuItems}
@sp-overlay-closed=${this.onOverlayClosed}
.overlayOpenCallback=${this.overlayOpenCallback}
.overlayCloseCallback=${this.overlayCloseCallback}
>
${content}
Expand All @@ -483,7 +473,7 @@ export class PickerBase extends SizedMixin(Focusable) {
id="popover"
role="dialog"
@sp-menu-item-added-or-updated=${this.updateMenuItems}
@sp-overlay-closed=${this.onOverlayClosed}
.overlayOpenCallback=${this.overlayOpenCallback}
.overlayCloseCallback=${this.overlayCloseCallback}
>
${content}
Expand Down Expand Up @@ -532,12 +522,6 @@ export class PickerBase extends SizedMixin(Focusable) {

protected updated(changedProperties: PropertyValues): void {
super.updated(changedProperties);
if (
changedProperties.has('value') &&
!changedProperties.has('selectedItem')
) {
this.updateMenuItems();
}
if (changedProperties.has('disabled') && this.disabled) {
this.open = false;
}
Expand All @@ -554,9 +538,19 @@ export class PickerBase extends SizedMixin(Focusable) {
this.closeMenu();
}
}
if (
changedProperties.has('value') &&
!changedProperties.has('selectedItem')
) {
this.updateMenuItems();
}
}

protected manageSelection(): void {
protected async manageSelection(): Promise<void> {
await this.menuStatePromise;
this.selectionPromise = new Promise(
(res) => (this.selectionResolver = res)
);
let selectedItem: MenuItem | undefined;
this.menuItems.forEach((item) => {
if (this.value === item.value && !item.disabled) {
Expand All @@ -573,19 +567,22 @@ export class PickerBase extends SizedMixin(Focusable) {
this.selectedItem = undefined;
}
if (this.open) {
this.optionsMenu.updateComplete.then(() => {
this.optionsMenu.updateSelectedItemIndex();
});
await this.optionsMenu.updateComplete;
this.optionsMenu.updateSelectedItemIndex();
}
this.selectionResolver();
}

private menuStatePromise = Promise.resolve();
private menuStateResolver!: () => void;
private selectionPromise = Promise.resolve();
private selectionResolver!: () => void;

protected async getUpdateComplete(): Promise<boolean> {
const complete = (await super.getUpdateComplete()) as boolean;
await this.menuStatePromise;
await this.itemsUpdated;
await this.selectionPromise;
return complete;
}

Expand All @@ -600,7 +597,7 @@ export class PickerBase extends SizedMixin(Focusable) {
}

public disconnectedCallback(): void {
this.open = false;
this.close();

super.disconnectedCallback();
}
Expand Down
16 changes: 16 additions & 0 deletions packages/picker/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,22 @@ export function runPickerTests(): void {
it('loads accessibly', async () => {
await expect(el).to.be.accessible();
});
it('closes accessibly', async () => {
const opened = oneEvent(el, 'sp-opened');
el.open = true;
await opened;

expect(el.open).to.be.true;
const accessibleCloseButton = document.querySelector(
'.visually-hidden button'
) as HTMLButtonElement;

const closed = oneEvent(el, 'sp-closed');
accessibleCloseButton.click();
await closed;

expect(el.open).to.be.false;
});
it('accepts new selected item content', async () => {
const option2 = el.querySelector('[value="option-2"') as MenuItem;
el.value = 'option-2';
Expand Down
26 changes: 13 additions & 13 deletions packages/split-button/src/SplitButton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,26 +186,26 @@ export class SplitButton extends SizedMixin(PickerBase) {
}
}

protected manageSelection(): void {
protected async manageSelection(): Promise<void> {
super.manageSelection();
this.manageSplitButtonItems();
}

private async manageSplitButtonItems(): Promise<void> {
if (this.menuItems.length) {
if (this.type === 'more') {
this.menuItems[0].hidden = true;
this.menuItems.forEach((el) => (el.selected = false));
this.selectedItem = this.menuItems[0];
} else {
this.selectedItem = this.selectedItem || this.menuItems[0];
if (!this.menuItems.length) {
await this.updateComplete;
if (!this.menuItems.length) {
return;
}
this.value = this.selectedItem.value;
return;
}
await this.updateComplete;
if (this.menuItems.length) {
this.manageSplitButtonItems();

if (this.type === 'more') {
this.menuItems[0].hidden = true;
this.menuItems.forEach((el) => (el.selected = false));
this.selectedItem = this.menuItems[0];
} else {
this.selectedItem = this.selectedItem || this.menuItems[0];
}
this.value = this.selectedItem.value;
}
}

0 comments on commit 39e7309

Please sign in to comment.