Skip to content

Commit

Permalink
docs(testing, components): clarify testing and focusable usage (#29424)
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
liamdebeasi committed May 1, 2024
1 parent bd8d065 commit a01c3d4
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 3 deletions.
12 changes: 9 additions & 3 deletions docs/component-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,6 @@ The focused state should be enabled for elements with actions when tabbed to via
> [!WARNING]
> 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.
> [!NOTE]
> 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).
> [!IMPORTANT]
> Make sure the component has the correct [component structure](#component-structure) before continuing.
Expand Down Expand Up @@ -215,6 +212,15 @@ ion-button {
}
```

#### When to use `.ion-focusable` versus `:focus-visible`

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).

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.

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.

### Hover

Expand Down
13 changes: 13 additions & 0 deletions docs/core/testing/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,19 @@ configs().forEach(({ config, title }) => {
});
```

#### `spyOnEvent` with Locators

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`:

```typescript
import type { E2ELocator } from '@utils/test/playwright';

...

const alert = page.locator('ion-alert') as E2ELocator;
const ionAlertDidPresent = await alert.spyOnEvent('ionAlertDidPresent');
```

### Using `setIonViewport`

`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).
Expand Down
File renamed without changes

0 comments on commit a01c3d4

Please sign in to comment.