Skip to content

Commit 171297c

Browse files
author
Helen Le
committed
chore(overlay): revert overlay refactor
1 parent 75e8d07 commit 171297c

File tree

17 files changed

+579
-268
lines changed

17 files changed

+579
-268
lines changed

packages/action-group/test/action-group.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ describe('ActionGroup', () => {
260260
expect(el.children[3]).to.equal(document.activeElement);
261261
});
262262

263-
it.skip('action-group with action-menu manages tabIndex correctly while using mouse', async () => {
263+
it('action-group with action-menu manages tabIndex correctly while using mouse', async () => {
264264
const el = await fixture<ActionGroup>(
265265
HasActionMenuAsChild({ label: 'Action Group' })
266266
);

packages/overlay/README.md

Lines changed: 140 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ Some Overlays will always be passed focus (e.g. modal or page Overlays). When th
269269

270270
The `trigger` option accepts an `HTMLElement` or a `VirtualTrigger` from which to position the Overlay.
271271

272-
- You can import the `VirtualTrigger` class from the overlay package to create a virtual trigger that can be used to position an Overlay. This is useful when you want to position an Overlay relative to a point on the screen that is not an element in the DOM, like the mouse cursor.
272+
- You can import the `VirtualTrigger` class from the overlay package to create a virtual trigger that can be used to position an Overlay. This is useful when you want to position an Overlay relative to a point on the screen that is not an element in the DOM, like the mouse cursor.
273273

274274
The `type` of an Overlay outlines a number of things about the interaction model within which it works:
275275

@@ -408,8 +408,8 @@ The `overlay` value in this case will hold a reference to the actual `<sp-overla
408408

409409
"Fully" in this context means that all CSS transitions that have dispatched `transitionrun` events on the direct children of the `<sp-overlay>` element have successfully dispatched their `transitionend` or `transitioncancel` event. Keep in mind the following:
410410

411-
- `transition*` events bubble; this means that while transition events on light DOM content of those direct children will be heard, those events will not be taken into account
412-
- `transition*` events are not composed; this means that transition events on shadow DOM content of the direct children will not propagate to a level in the DOM where they can be heard
411+
- `transition*` events bubble; this means that while transition events on light DOM content of those direct children will be heard, those events will not be taken into account
412+
- `transition*` events are not composed; this means that transition events on shadow DOM content of the direct children will not propagate to a level in the DOM where they can be heard
413413

414414
This means that in both cases, if the transition is meant to be a part of the opening or closing of the overlay in question you will need to re-dispatch the `transitionrun`, `transitionend`, and `transitioncancel` events from that transition from the closest direct child of the `<sp-overlay>`.
415415

@@ -573,7 +573,136 @@ Common in `modal`/`page` overlays for full-screen content</sp-table-cell>
573573
</sp-table-body>
574574
</sp-table>
575575

576-
#### Styling
576+
The `type` of an Overlay outlines a number of things about the interaction model within which is works.
577+
578+
### Modal
579+
580+
`'modal'` Overlays are opened with the `showModal()` method on a `<dialog>` element, which causes the Overlay to accept focus and trap the tab stop within the content of said Overlay.
581+
582+
They should be used when you need to ensure that the user has interacted with the content of the Overlay before continuing with their work. This is commonly used for dialogs that require a user to confirm or cancel an action before continuing.
583+
584+
```html
585+
<sp-button id="trigger">open modal</sp-button>
586+
<sp-overlay trigger="trigger@click" type="modal">
587+
<sp-dialog-wrapper headline="Signin form" dismissable underlay>
588+
<p>I am a modal type overlay.</p>
589+
<sp-field-label>Enter your email</sp-field-label>
590+
<sp-textfield placeholder="test@gmail.com"></sp-textfield>
591+
<sp-action-button
592+
onClick="
593+
this.dispatchEvent(
594+
new Event('close', {
595+
bubbles: true,
596+
composed: true,
597+
})
598+
);
599+
"
600+
>
601+
Sign in
602+
</sp-action-button>
603+
</sp-dialog-wrapper>
604+
</sp-overlay>
605+
```
606+
607+
### Page
608+
609+
`'page'` Overlays will act in a similar manner to a `'modal'` Overlay, however they will not be allowed to close via the "light dismiss" algorithm (e.g. the Escape key).
610+
611+
A page overlay could be used for a full-screen menu on a mobile website. When the user clicks on the menu button, the entire screen is covered with the menu options.
612+
613+
```html
614+
<sp-button id="trigger">open page</sp-button>
615+
<sp-overlay trigger="trigger@click" type="page">
616+
<sp-dialog-wrapper
617+
headline="Full screen menu"
618+
mode="fullscreenTakeover"
619+
cancel-label="Close"
620+
>
621+
<p>I am a page type overlay.</p>
622+
</sp-dialog-wrapper>
623+
</sp-overlay>
624+
```
625+
626+
### Hint
627+
628+
`'hint'` Overlays are much like tooltips so they are not just ephemeral, but they are delivered primarily as a visual helper and exist outside of the tab order. In this way, be sure _not_ to place interactive content within this type of Overlay.
629+
630+
This overlay type does not accept focus and does not interfere with the user's interaction with the rest of the page.
631+
632+
```html
633+
<sp-button id="trigger">open hint</sp-button>
634+
<sp-overlay trigger="trigger@hover" type="hint">
635+
<sp-tooltip>
636+
I am a hint type overlay. I am not interactive and will close when the
637+
user interacts with the page.
638+
</sp-tooltip>
639+
</sp-overlay>
640+
```
641+
642+
### Auto
643+
644+
`'auto'` Overlays provide a place for content that is ephemeral _and_ interactive. These Overlays can accept focus and remain open while interacting with their content. They will close when focus moves outside the overlay or when clicking elsewhere on the page.
645+
646+
```html
647+
<sp-button id="trigger">Open Overlay</sp-button>
648+
<sp-overlay trigger="trigger@click" type="auto" placement="bottom">
649+
<sp-popover dialog>
650+
<p>
651+
My slider in overlay element:
652+
<sp-slider label="Slider Label - Editable" editable></sp-slider>
653+
</p>
654+
</sp-popover>
655+
</sp-overlay>
656+
```
657+
658+
### Manual
659+
660+
`'manual'` Overlays act much like `'auto'` Overlays, but do not close when losing focus or interacting with other parts of the page.
661+
662+
Note: When a `'manual'` Overlay is at the top of the "overlay stack", it will still respond to the Escape key and close.
663+
664+
```html
665+
<style>
666+
.chat-container {
667+
position: fixed;
668+
bottom: 1em;
669+
left: 1em;
670+
}
671+
</style>
672+
<sp-button id="trigger">open manual</sp-button>
673+
<sp-overlay trigger="trigger@click" type="manual">
674+
<sp-popover class="chat-container">
675+
<sp-dialog dismissable>
676+
<span slot="heading">Chat Window</span>
677+
<sp-textfield placeholder="Enter your message"></sp-textfield>
678+
<sp-action-button>Send</sp-action-button>
679+
</sp-dialog>
680+
</sp-popover>
681+
</sp-overlay>
682+
```
683+
684+
### Events
685+
686+
When fully open the `<sp-overlay>` element will dispatch the `sp-opened` event, and when fully closed the `sp-closed` event will be dispatched. Both of these events are of type:
687+
688+
```ts
689+
type OverlayStateEvent = Event & {
690+
overlay: Overlay;
691+
};
692+
```
693+
694+
The `overlay` value in this case will hold a reference to the actual `<sp-overlay>` that is opening or closing to trigger this event. Remember that some `<sp-overlay>` element (like those creates via the imperative API) can be transiently available in the DOM, so if you choose to build a cache of Overlay elements to some end, be sure to leverage a weak reference so that the `<sp-overlay>` can be garbage collected as desired by the browser.
695+
696+
#### When it is "fully" open or closed?
697+
698+
"Fully" in this context means that all CSS transitions that have dispatched `transitionrun` events on the direct children of the `<sp-overlay>` element have successfully dispatched their `transitionend` or `transitioncancel` event. Keep in mind the following:
699+
700+
- `transition*` events bubble; this means that while transition events on light DOM content of those direct children will be heard, those events will not be taken into account
701+
- `transition*` events are not composed; this means that transition events on shadow DOM content of the direct children will not propagate to a level in the DOM where they can be heard
702+
703+
This means that in both cases, if the transition is meant to be a part of the opening or closing of the overlay in question you will need to redispatch the `transitionrun`, `transitionend`, and `transitioncancel` events from that transition from the closest direct child of the `<sp-overlay>`.
704+
705+
## Styling
577706

578707
`<sp-overlay>` element will use the `<dialog>` element or `popover` attribute to project your content onto the top-layer of the browser, without being moved in the DOM tree. That means that you can style your overlay content with whatever techniques you are already leveraging to style the content that doesn't get overlaid. This means standard CSS selectors, CSS Custom Properties, and CSS Parts applied in your parent context will always apply to your overlaid content.
579708

@@ -772,9 +901,9 @@ When nesting multiple overlays, it is important to ensure that the nested overla
772901

773902
The overlay manages focus based on its type:
774903

775-
- For `modal` and `page` types, focus is always trapped within the overlay
776-
- For `auto` and `manual` types, focus behavior is controlled by the `receives-focus` attribute
777-
- For `hint` type, focus remains on the trigger element
904+
- For `modal` and `page` types, focus is always trapped within the overlay
905+
- For `auto` and `manual` types, focus behavior is controlled by the `receives-focus` attribute
906+
- For `hint` type, focus remains on the trigger element
778907

779908
Example of proper focus management:
780909

@@ -840,10 +969,10 @@ Example of proper focus management:
840969

841970
#### Screen reader considerations
842971

843-
- Use `aria-haspopup` on trigger elements to indicate the type of overlay
844-
- Provide descriptive labels using `aria-label` or `aria-labelledby`
845-
- Use proper heading structure within overlays
846-
- Ensure error messages are announced using `aria-live`
972+
- Use `aria-haspopup` on trigger elements to indicate the type of overlay
973+
- Provide descriptive labels using `aria-label` or `aria-labelledby`
974+
- Use proper heading structure within overlays
975+
- Ensure error messages are announced using `aria-live`
847976

848977
Example of a tooltip with proper screen reader support:
849978

packages/overlay/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,7 @@
170170
"@spectrum-web-components/base": "1.9.0",
171171
"@spectrum-web-components/reactive-controllers": "1.9.0",
172172
"@spectrum-web-components/shared": "1.9.0",
173-
"@spectrum-web-components/theme": "1.9.0",
174-
"focus-trap": "7.6.5"
173+
"@spectrum-web-components/theme": "1.9.0"
175174
},
176175
"types": "./src/index.d.ts",
177176
"customElements": "custom-elements.json",

packages/overlay/src/AbstractOverlay.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ export class AbstractOverlay extends SpectrumElement {
162162
return;
163163
}
164164
/* c8 ignore next 3 */
165+
protected async manageDialogOpen(): Promise<void> {
166+
return;
167+
}
168+
/* c8 ignore next 3 */
165169
protected async managePopoverOpen(): Promise<void> {
166170
return;
167171
}

packages/overlay/src/HoverController.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
InteractionController,
1919
InteractionTypes,
2020
lastInteractionType,
21+
SAFARI_FOCUS_RING_CLASS,
2122
} from './InteractionController.js';
2223

2324
const HOVER_DELAY = 300;
@@ -36,6 +37,7 @@ export class HoverController extends InteractionController {
3637
handleKeyup(event: KeyboardEvent): void {
3738
if (event.code === 'Tab' || event.code === 'Escape') {
3839
this.open = true;
40+
this.removeSafariFocusRingClass();
3941
}
4042
}
4143

@@ -48,14 +50,17 @@ export class HoverController extends InteractionController {
4850
isWebKit() &&
4951
this.target[lastInteractionType] === InteractionTypes.click
5052
) {
53+
this.target.classList.add(SAFARI_FOCUS_RING_CLASS);
5154
return;
5255
}
5356

5457
this.open = true;
5558
this.focusedin = true;
59+
this.removeSafariFocusRingClass();
5660
}
5761

5862
handleTargetFocusout(): void {
63+
this.removeSafariFocusRingClass();
5964
this.focusedin = false;
6065
if (this.pointerentered) return;
6166
this.open = false;
@@ -199,4 +204,12 @@ export class HoverController extends InteractionController {
199204
{ signal }
200205
);
201206
}
207+
208+
private removeSafariFocusRingClass(): void {
209+
if (
210+
isWebKit() &&
211+
this.target.classList.contains(SAFARI_FOCUS_RING_CLASS)
212+
)
213+
this.target.classList.remove(SAFARI_FOCUS_RING_CLASS);
214+
}
202215
}

packages/overlay/src/InteractionController.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export enum InteractionTypes {
2020
}
2121

2222
export const lastInteractionType = Symbol('lastInteractionType');
23+
export const SAFARI_FOCUS_RING_CLASS = 'remove-focus-ring-safari-hack';
2324

2425
export type ControllerOptions = {
2526
overlay?: AbstractOverlay;
@@ -74,7 +75,7 @@ export class InteractionController implements ReactiveController {
7475
this.overlay.open = true;
7576
this.target[lastInteractionType] = this.type;
7677
});
77-
import('@spectrum-web-components/overlay/sp-overlay.js');
78+
import('../sp-overlay.js');
7879
}
7980

8081
public get overlay(): AbstractOverlay {

0 commit comments

Comments
 (0)