Skip to content

Commit a01c3d4

Browse files
authored
docs(testing, components): clarify testing and focusable usage (#29424)
This PR makes the following changes: 1. Clarifies when `.ion-focusable` should be used versus `:focus-visible`. 2. Clarifies that `Locator` needs to be typecast when using `Locator.spyOnEvent`.
1 parent bd8d065 commit a01c3d4

File tree

3 files changed

+22
-3
lines changed

3 files changed

+22
-3
lines changed

docs/component-guide.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,6 @@ The focused state should be enabled for elements with actions when tabbed to via
152152
> [!WARNING]
153153
> Do not use `:focus` because that will cause the focus to apply even when an element is tapped (because the element is now focused). Instead, we only want the focus state to be shown when it makes sense which is what the `.ion-focusable` utility mentioned below does.
154154
155-
> [!NOTE]
156-
> The [`:focus-visible`](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible) pseudo-class mostly does the same thing as our JavaScript-driven utility. However, it does not work well with Shadow DOM components as the element that receives focus is typically inside of the Shadow DOM, but we usually want to set the `:focus-visible` state on the host so we can style other parts of the component. Using other combinations such as `:has(:focus-visible)` does not work because `:has` does not pierce the Shadow DOM (as that would leak implementation details about the Shadow DOM contents). `:focus-within` does work with the Shadow DOM, but that has the same problem as `:focus` that was mentioned before. Unfortunately, a [`:focus-visible-within` pseudo-class does not exist yet](https://github.com/WICG/focus-visible/issues/151).
157-
158155
> [!IMPORTANT]
159156
> Make sure the component has the correct [component structure](#component-structure) before continuing.
160157
@@ -215,6 +212,15 @@ ion-button {
215212
}
216213
```
217214

215+
#### When to use `.ion-focusable` versus `:focus-visible`
216+
217+
The [`:focus-visible`](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible) pseudo-class mostly does the same thing as our JavaScript-driven utility. However, it does not work well with Shadow DOM components as the element that receives focus is typically inside of the Shadow DOM, but we usually want to set the `:focus-visible` state on the host so we can style other parts of the component.
218+
219+
Using other combinations such as `:has(:focus-visible)` does not work because `:has` does not pierce the Shadow DOM (as that would leak implementation details about the Shadow DOM contents). `:focus-within` does work with the Shadow DOM, but that has the same problem as `:focus` that was mentioned before. Unfortunately, a [`:focus-visible-within` pseudo-class does not exist yet](https://github.com/WICG/focus-visible/issues/151).
220+
221+
The `.ion-focusable` class should be used when you want to style Element A based on the state of Element B. For example, the Button component styles the host of the component (Element A) when the native `button` inside the Shadow DOM (Element B) has focus.
222+
223+
On the other hand, the `:focus-visible` pseudo-class can be used when you want to style the element based on its own state. For example, we could use `:focus-visible` to style the clear icon on Input when the icon itself is focused.
218224

219225
### Hover
220226

docs/core/testing/api.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,19 @@ configs().forEach(({ config, title }) => {
174174
});
175175
```
176176

177+
#### `spyOnEvent` with Locators
178+
179+
Locators have been updated with a `spyOnEvent` method which allows you to listen for an event on the element that the locator matches. Note that Playwright does not support changing the type of an existing fixture, so Locators that use `spyOnEvent` need to be manually cast as `E2ELocator`:
180+
181+
```typescript
182+
import type { E2ELocator } from '@utils/test/playwright';
183+
184+
...
185+
186+
const alert = page.locator('ion-alert') as E2ELocator;
187+
const ionAlertDidPresent = await alert.spyOnEvent('ionAlertDidPresent');
188+
```
189+
177190
### Using `setIonViewport`
178191

179192
`setIonViewport` is only needed when a) you are using `ion-content` and b) you need to take a screenshot of the full page (including content that may overflow offscreen).

0 commit comments

Comments
 (0)