Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion addon/components/fluid-lab/expanding-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { action } from '@ember/object';
* <FluidLab::ExpandingList as |list| >
* <list.Header>
* <h1>Ice Cream Flavors</h1>
* <list.Toggle />
* <list.Toggle @label="Ice cream flavors" />
* </list.Header>
*
* <list.Content>
Expand All @@ -28,6 +28,16 @@ import { action } from '@ember/object';
* </FluidLab::ExpandingList>
* ```
*
* ## Accessibility
*
* `list.Toggle` is the keyboard-operable control — it renders a `<button>` carrying
* `aria-expanded`. Always render one; `list.Header`'s own click handler is a mouse
* convenience and is not reachable by keyboard, so a header without a toggle gives
* keyboard and screen-reader users no way to expand the list.
*
* The chevron is decorative, so pass `@label` to name the button whenever a page has
* more than one list. It falls back to a generic "Toggle section".
*
* For more advanced usage, the `expanded` property and `onChange`
* actions are also yielded out of the component to be made available to
* components rendered inside the `expanding-list`.
Expand Down
91 changes: 91 additions & 0 deletions addon/components/fluid-lab/expanding-list/docs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { Story } from "@storybook/addon-docs";

# Expanding List

A contextual accordion. It yields three subcomponents — `list.Header`, `list.Toggle`
and `list.Content` — plus the `expanded` property and an `onChange` action for DDAU use.

#### Default

<Story id="components-fluid-lab-expanding-list--default" height="150px"/>

```hbs
<FluidLab::ExpandingList as |list|>
<list.Header class='flex items-center'>
<list.Toggle @label='Ice cream flavors' />

<h6>Ice Cream Flavors</h6>
</list.Header>

<list.Content>
<ul>
<li>Chocolate</li>
<li>Vanilla</li>
<li>Mango</li>
</ul>
</list.Content>
</FluidLab::ExpandingList>
```

#### Collapsed

<Story id="components-fluid-lab-expanding-list--collapsed" height="75px"/>

```hbs
<FluidLab::ExpandingList @expanded={{false}} as |list|>
{{! ... }}
</FluidLab::ExpandingList>
```

#### Disabled

`@disabled` hides the toggle and stops the header from expanding the list.

<Story id="components-fluid-lab-expanding-list--disabled" height="150px"/>

```hbs
<FluidLab::ExpandingList @disabled={{true}} as |list|>
{{! ... }}
</FluidLab::ExpandingList>
```

#### Toggle outside the header

The toggle does not have to be nested in the header — position it as a sibling when the
layout calls for it.

<Story id="components-fluid-lab-expanding-list--toggle-outside-header" height="75px"/>

```hbs
<FluidLab::ExpandingList @expanded={{false}} as |list|>
<list.Toggle @label='Pizza toppings' class='absolute' />

<list.Header>
<h6 class='pl-8'>Pizza Toppings</h6>
</list.Header>

{{! ... }}
</FluidLab::ExpandingList>
```

## Accessibility

**Always render a `list.Toggle`.** It is the only keyboard-operable control — it renders
a `<button>` carrying `aria-expanded`. `list.Header` has a click handler too, but that is
a mouse convenience: a header is not focusable, so a list without a toggle cannot be
expanded by keyboard or reported to a screen reader.

The header is deliberately *not* a button. Because the toggle is usually nested inside it,
making both interactive would nest a button in a button.

**Pass `@label` whenever a page has more than one list.** The chevron is decorative
(`aria-hidden`), so the button's accessible name comes from a visually-hidden label that
falls back to a generic "Toggle section". Below, the first toggle is labelled and the
second is not — turn on a screen reader and tab between them to hear the difference.

<Story id="components-fluid-lab-expanding-list--labelled-and-unlabelled" height="100px"/>

```hbs
<list.Toggle @label='Ice cream flavors' /> {{! announces "Ice cream flavors" }}
<list.Toggle /> {{! announces "Toggle section" }}
```
5 changes: 4 additions & 1 deletion addon/components/fluid-lab/expanding-list/header.hbs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{{! Mouse-only convenience. `<list.Toggle>` is the keyboard path, and it usually
renders inside this header — making the header interactive too would nest a
button in a button. }}
{{! template-lint-disable no-invalid-interactive }}
<header
class="expanding-list-header {{if this.disabled "expanding-list-header__disabled"}}"
role="button"
data-test-fluid-lab-expanding-list-header
...attributes
{{on "click" (stop-propagation this.toggle)}}
Expand Down
119 changes: 119 additions & 0 deletions addon/components/fluid-lab/expanding-list/index.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { hbs } from 'ember-cli-htmlbars';

import ExpandingListDocs from './docs.mdx';

export default {
title: 'Components/Fluid Lab/Expanding List',
parameters: {
docs: {
page: ExpandingListDocs,
},
actions: {
handles: ['click', 'click .btn'],
},
},
};

const NestedToggleTemplate = (args) => ({
template: hbs`
<FluidLab::ExpandingList @expanded={{expanded}} @disabled={{disabled}} as |list|>
<list.Header class='flex items-center'>
<list.Toggle @label={{label}} />

<h6>Ice Cream Flavors</h6>
</list.Header>

<list.Content>
<ul class='pl-8'>
<li>Chocolate</li>
<li>Vanilla</li>
<li>Mango</li>
</ul>
</list.Content>
</FluidLab::ExpandingList>
`,
context: { ...args },
});

const SiblingToggleTemplate = (args) => ({
template: hbs`
<FluidLab::ExpandingList @expanded={{expanded}} as |list|>
<list.Toggle @label={{label}} class='absolute' />

<list.Header>
<h6 class='pl-8'>Pizza Toppings</h6>
</list.Header>

<list.Content>
<ul class='pl-8'>
<li>Mushrooms</li>
<li>Anchovies</li>
<li>Ricotta</li>
</ul>
</list.Content>
</FluidLab::ExpandingList>
`,
context: { ...args },
});

const MultipleTemplate = (args) => ({
template: hbs`
<FluidLab::ExpandingList @expanded={{false}} as |list|>
<list.Header class='flex items-center'>
<list.Toggle @label='Ice cream flavors' />

<h6>Ice Cream Flavors</h6>
</list.Header>

<list.Content>
<ul class='pl-8'>
<li>Chocolate</li>
</ul>
</list.Content>
</FluidLab::ExpandingList>

<FluidLab::ExpandingList @expanded={{false}} as |list|>
<list.Header class='flex items-center'>
<list.Toggle />

<h6>Pizza Toppings</h6>
</list.Header>

<list.Content>
<ul class='pl-8'>
<li>Mushrooms</li>
</ul>
</list.Content>
</FluidLab::ExpandingList>
`,
context: { ...args },
});

export const Default = NestedToggleTemplate.bind({});
Default.args = {
expanded: true,
disabled: false,
label: 'Ice cream flavors',
};

export const Collapsed = NestedToggleTemplate.bind({});
Collapsed.args = {
expanded: false,
disabled: false,
label: 'Ice cream flavors',
};

export const Disabled = NestedToggleTemplate.bind({});
Disabled.args = {
expanded: true,
disabled: true,
label: 'Ice cream flavors',
};

export const ToggleOutsideHeader = SiblingToggleTemplate.bind({});
ToggleOutsideHeader.args = {
expanded: false,
label: 'Pizza toppings',
};

export const LabelledAndUnlabelled = MultipleTemplate.bind({});
14 changes: 10 additions & 4 deletions addon/components/fluid-lab/expanding-list/toggle.hbs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
<div
<button
type="button"
class="expanding-list-toggle
{{if this.disabled "expanding-list-toggle__disabled"}}
{{if this.expanded "expanded"}}
{{this.position}}"
role="button"
disabled={{this.disabled}}
aria-expanded={{if this.expanded "true" "false"}}
data-test-fluid-lab-expanding-list-toggle
...attributes
{{on "click" (stop-propagation this.toggle)}}
>
{{svg-jar "icon-dropdown-arrow"}}
</div>
{{svg-jar "icon-dropdown-arrow" aria-hidden="true"}}

<span class="sr-only">
{{or this.label "Toggle section"}}
</span>
</button>
2 changes: 2 additions & 0 deletions addon/components/fluid-lab/expanding-list/toggle.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export default class FluidLabExpandingListToggle extends Component {
disabled = false;
position = 'left';

label = null;

@action
toggle() {
this.ontoggle();
Expand Down
3 changes: 2 additions & 1 deletion addon/components/fluid-modal.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
>
<div
role="dialog"
aria-modal="true"
class="flex flex-col max-h-modal"
aria-labelledby={{this.titleId}}
aria-labelledby={{if (or @title (has-block "header")) this.titleId}}
...attributes
{{focus-trap
isPaused=@disableFocusTrap
Expand Down
5 changes: 4 additions & 1 deletion app/styles/fluid/components/classy-select.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@
-webkit-appearance: textfield;
padding: 1.5rem 2.5rem 0.5rem 0.7rem;
border: 0;
outline: none;
width: 100%;
background: transparent;
position: relative;
z-index: 1;

&:focus:not(:focus-visible) {
outline: none;
}
}

svg {
Expand Down
3 changes: 2 additions & 1 deletion app/styles/fluid/components/fluid-checkbox.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
box-sizing: border-box;
display: inline-flex;

&:focus {
// Inert today: the focusable element is the inner `__box` button, not this wrapper.
&:focus:not(:focus-visible) {
outline: none;
}

Expand Down
6 changes: 6 additions & 0 deletions app/styles/fluid/components/fluid-lab/expanding-list.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
.expanding-list-toggle {
background: none;
border: 0;
padding: 0;
color: inherit;
font: inherit;

text-align: center;
transition: transform 0.1s linear;
display: flex;
Expand Down
4 changes: 3 additions & 1 deletion app/styles/fluid/components/fluid-select.scss
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@
}

&__option {
outline: none;
&:focus:not(:focus-visible) {
outline: none;
}

&:hover,
&--highlighted {
Expand Down
5 changes: 4 additions & 1 deletion app/styles/fluid/components/input-field.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
@apply shadow-none;
@apply bg-neutral-100;
@apply resize-y;
outline: none;

&:focus:not(:focus-visible) {
outline: none;
}
}

&.flex-2 {
Expand Down
5 changes: 4 additions & 1 deletion app/styles/fluid/components/on-off-switch.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
border-radius: 0.9rem;
border: 0.1rem solid theme('colors.neutral.500');
transition: all 120ms;
outline: none;

&:focus:not(:focus-visible) {
outline: none;
}

* {
pointer-events: none;
Expand Down
2 changes: 1 addition & 1 deletion app/styles/fluid/components/token-field.scss
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
line-height: inherit;
white-space: nowrap;

&:focus {
&:focus:not(:focus-visible) {
outline-style: none;
}
}
Expand Down
Loading
Loading