ui: Disclosure Component amends plus DisclosureMenu Component (#12304)
* ui: Disclosure amends plus DisclosureMenu Co-authored-by: Jamie White <jamie@jgwhite.co.uk>
This commit is contained in:
parent
ab3b765a88
commit
961f144b1c
|
@ -0,0 +1,91 @@
|
|||
# DisclosureMenu
|
||||
|
||||
A component to be used for making dropup/down/left/right menus i.e. Disclosure
|
||||
Menus. Please see both Disclosure and Menu components for more details.
|
||||
|
||||
The component does not make any guesses around whether you want the panel to
|
||||
be on another DOM layer/absolutely positioned so you should apply that layout
|
||||
yourself, but it's root node is relatively positioned to help for the fairly
|
||||
common usecase of having a floating menu.
|
||||
|
||||
```hbs preview-template
|
||||
<figure>
|
||||
<figcaption>
|
||||
Non-floating Menu
|
||||
</figcaption>
|
||||
<DisclosureMenu as |disclosure|>
|
||||
<disclosure.Action
|
||||
{{on 'click' disclosure.toggle}}
|
||||
>
|
||||
{{if disclosure.expanded 'Close' 'Open'}}
|
||||
</disclosure.Action>
|
||||
<disclosure.Menu as |menu|>
|
||||
<menu.Item>
|
||||
<menu.Action>Item 1</menu.Action>
|
||||
</menu.Item>
|
||||
<menu.Item>
|
||||
<menu.Action>Item 2</menu.Action>
|
||||
</menu.Item>
|
||||
</disclosure.Menu>
|
||||
</DisclosureMenu>
|
||||
</figure>
|
||||
<figure>
|
||||
<figcaption>
|
||||
Floating Menu
|
||||
</figcaption>
|
||||
<DisclosureMenu as |disclosure|>
|
||||
<disclosure.Action
|
||||
{{on 'click' disclosure.toggle}}
|
||||
{{css-prop 'height' returns=(set this 'height')}}
|
||||
>
|
||||
{{if disclosure.expanded 'Close' 'Open'}}
|
||||
</disclosure.Action>
|
||||
<disclosure.Menu
|
||||
style={{style-map
|
||||
(array 'position' 'absolute')
|
||||
(array 'top' this.height)
|
||||
(array 'background-color' 'rgb(var(--tone-gray-000))')
|
||||
}}
|
||||
as |menu|>
|
||||
<menu.Item>
|
||||
<menu.Action>Item 1</menu.Action>
|
||||
</menu.Item>
|
||||
<menu.Item>
|
||||
<menu.Action>Item 2</menu.Action>
|
||||
</menu.Item>
|
||||
</disclosure.Menu>
|
||||
</DisclosureMenu>
|
||||
</figure>
|
||||
```
|
||||
|
||||
## Arguments
|
||||
|
||||
| Argument | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `expanded` | `Boolean` | false | The _initial_ state of the disclosure. Please note: this is the _initial_ state only, please use the `disclosure.open` and `disclosure.close` for controling the state. |
|
||||
|
||||
## Exported API
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `Action` | `GlimmerComponent` | A contextual '<Action />' component with aria attributes correctly applied, please note you still need to add an 'on' modifier here so you can control whether it opens on click/hover etc |
|
||||
| `Menu` | `MenuComponent` | A contextual '<Menu />' component already wrapped in a disclosure.Details component |
|
||||
| `toggle` | `Function` | Toggle the open/close state of the disclosure |
|
||||
| `expanded` | `Boolean` | Whether the disclosure is 'expanded' or not |
|
||||
| `disclosure` | `DisclosureComponentAPI` | A reference to the full DisclosureComponentAPI |
|
||||
|
||||
|
||||
### menu.Action
|
||||
|
||||
An `<Action />` component with the correct aria attributes added.
|
||||
|
||||
### menu.Menu
|
||||
|
||||
A `<Menu />` component with the correct aria attributes added.
|
||||
|
||||
## See
|
||||
|
||||
- [Template Source Code](./index.hbs)
|
||||
|
||||
---
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<@disclosure.Action
|
||||
aria-haspopup="menu"
|
||||
...attributes
|
||||
>
|
||||
{{yield}}
|
||||
</@disclosure.Action>
|
|
@ -0,0 +1,16 @@
|
|||
<div
|
||||
class={{class-map
|
||||
"disclosure-menu"
|
||||
}}
|
||||
...attributes
|
||||
>
|
||||
<Disclosure as |disclosure|>
|
||||
{{yield (hash
|
||||
Action=(component 'disclosure-menu/action' disclosure=disclosure)
|
||||
Menu=(component 'disclosure-menu/menu' disclosure=disclosure)
|
||||
disclosure=disclosure
|
||||
toggle=disclosure.toggle
|
||||
expanded=disclosure.expanded
|
||||
)}}
|
||||
</Disclosure>
|
||||
</div>
|
|
@ -0,0 +1,3 @@
|
|||
.disclosure-menu {
|
||||
position: relative;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<@disclosure.Details as |details|>
|
||||
<Menu
|
||||
{{on-outside 'click' @disclosure.close}}
|
||||
@disclosure={{@disclosure}}
|
||||
...attributes
|
||||
as |menu|>
|
||||
{{yield (hash
|
||||
items=menu.items
|
||||
Item=menu.Item
|
||||
Action=menu.Action
|
||||
Separator=menu.Separator
|
||||
)}}
|
||||
</Menu>
|
||||
</@disclosure.Details>
|
||||
|
|
@ -1,56 +1,213 @@
|
|||
# Disclosure
|
||||
|
||||
A component which can be used to implement an aria Disclosure pattern.
|
||||
A renderless component which can be used to implement an aria Disclosure pattern.
|
||||
|
||||
The disclosure exports an Action component already configured for use. But if
|
||||
you want to contruct your own trigger, disclosure has all the properties to
|
||||
enable you to do so.
|
||||
The disclosure exports an Action component already configured for use as a
|
||||
clickable action. But if you want to contruct your own trigger, disclosure has
|
||||
all the properties to enable you to do so.
|
||||
|
||||
You should make use of the `disclosure.panel` property in order to 'tag' the
|
||||
disclosure panel you are using.
|
||||
You should make use of the `disclosure.Details` property in order control the
|
||||
disclosure of the disclosure's content. By default it will automatically do
|
||||
this for you. But if you need to control this yourself you can make use of the
|
||||
`@auto` argument (see below for details).
|
||||
|
||||
You can use multiple `Details` components which lets you control multiple
|
||||
areas with a single trigger/button.
|
||||
|
||||
Clicking outside will not close the disclosure by default, if you require this
|
||||
functionality please combine with our `{{on-outside 'click'}}` modifier (see example).
|
||||
|
||||
By default, there are no aria attributes that you need to add or think about
|
||||
as a consumer of the component, but you **should** make use of the
|
||||
`details.id` property to set the `id=""` on the DOM element you are
|
||||
disclosing. Every `Details` component has its `id` added to the `Action`
|
||||
`aria-controls` attribute by default so you don't need to do this yourself if
|
||||
using the `Details` component.
|
||||
|
||||
```hbs preview-template
|
||||
<Disclosure>
|
||||
<:button as |disclosure|>
|
||||
<disclosure.Action
|
||||
{{on-outside 'click' disclosure.close}}
|
||||
{{on 'click' disclosure.toggle}}
|
||||
>
|
||||
{{if disclosure.expanded "Close" "Open"}}
|
||||
</disclosure.Action>
|
||||
</:button>
|
||||
<:panel as |disclosure|>
|
||||
<Disclosure as |disclosure|>
|
||||
<disclosure.Action
|
||||
{{on-outside 'click' disclosure.close}}
|
||||
{{on 'click' disclosure.toggle}}
|
||||
>
|
||||
{{if disclosure.expanded "Close" "Open"}}
|
||||
</disclosure.Action>
|
||||
<disclosure.Details as |details|>
|
||||
<p
|
||||
id={{disclosure.panel}}
|
||||
id={{details.id}}
|
||||
>
|
||||
Disclose Me!
|
||||
</p>
|
||||
</:panel>
|
||||
</disclosure.Details>
|
||||
</Disclosure>
|
||||
```
|
||||
|
||||
You can also use multiple Details components for a single Discloure Action component to control multiple areas.
|
||||
|
||||
```hbs preview-template
|
||||
<Disclosure as |disclosure|>
|
||||
<disclosure.Action
|
||||
{{on-outside 'click' disclosure.close}}
|
||||
{{on 'click' disclosure.toggle}}
|
||||
>
|
||||
{{if disclosure.expanded "Close" "Open"}}
|
||||
</disclosure.Action>
|
||||
<disclosure.Details as |details|>
|
||||
<p
|
||||
id={{details.id}}
|
||||
>
|
||||
Disclose Me!
|
||||
</p>
|
||||
</disclosure.Details>
|
||||
<disclosure.Details as |details|>
|
||||
<p
|
||||
id={{details.id}}
|
||||
>
|
||||
Disclose Me also!
|
||||
</p>
|
||||
</disclosure.Details>
|
||||
</Disclosure>
|
||||
```
|
||||
|
||||
Or use two buttons/Actions to control one Detail (or multiple Details).
|
||||
|
||||
```hbs preview-template
|
||||
<Disclosure as |disclosure|>
|
||||
<div
|
||||
{{on-outside 'click' disclosure.close}}
|
||||
>
|
||||
<disclosure.Action
|
||||
{{on 'click' disclosure.toggle}}
|
||||
>
|
||||
{{if disclosure.expanded "1 Close" "1 Open"}}
|
||||
</disclosure.Action>
|
||||
<disclosure.Action
|
||||
{{on 'click' disclosure.toggle}}
|
||||
>
|
||||
{{if disclosure.expanded "2 Close" "2 Open"}}
|
||||
</disclosure.Action>
|
||||
<disclosure.Details as |details|>
|
||||
<p
|
||||
id={{details.id}}
|
||||
>
|
||||
Disclose Me!
|
||||
</p>
|
||||
</disclosure.Details>
|
||||
</div>
|
||||
</Disclosure>
|
||||
```
|
||||
|
||||
If you don't want to use the automatic hiding/showing (and therefore removal from the DOM) of the Details component, you can pass a `@auto={{false}}` argument to the Details component, which allows you to either specify a Handlebars conditional yourself or use CSS to hide/show the content to be disclosed.
|
||||
|
||||
**Please note:** We use a `style` attribute here just for illustrative purposes, you should consider using a `class` attribute instead if you want to use CSS for disclosure.
|
||||
|
||||
```hbs preview-template
|
||||
<Disclosure as |disclosure|>
|
||||
<disclosure.Action
|
||||
{{on-outside 'click' disclosure.close}}
|
||||
{{on 'click' disclosure.toggle}}
|
||||
>
|
||||
{{if disclosure.expanded "Close" "Open"}}
|
||||
</disclosure.Action>
|
||||
<disclosure.Details
|
||||
@auto={{false}}
|
||||
as |details|>
|
||||
{{#if details.expanded}}
|
||||
<p
|
||||
id={{details.id}}
|
||||
>
|
||||
Disclose Me with a hbs conditional!
|
||||
</p>
|
||||
{{/if}}
|
||||
</disclosure.Details>
|
||||
<disclosure.Details
|
||||
@auto={{false}}
|
||||
as |details|>
|
||||
<p
|
||||
id={{details.id}}
|
||||
aria-hidden={{if (not details.expanded) 'false'}}
|
||||
style={{style-map
|
||||
(array 'display' (if (not details.expanded) 'none'))
|
||||
}}
|
||||
>
|
||||
Disclose Me via CSS!
|
||||
</p>
|
||||
</disclosure.Details>
|
||||
</Disclosure>
|
||||
```
|
||||
|
||||
By making use of `@auto={{false}}` and hand-rolling your show/hide logic you
|
||||
can use disclosure to implement slightly more complex UI.
|
||||
|
||||
```hbs preview-template
|
||||
<Disclosure as |disclosure|>
|
||||
<disclosure.Action
|
||||
{{on-outside 'click' disclosure.close}}
|
||||
{{on 'click' disclosure.toggle}}
|
||||
>
|
||||
{{if disclosure.expanded "Close" "Open"}}
|
||||
</disclosure.Action>
|
||||
<disclosure.Details
|
||||
as |details|>
|
||||
<p
|
||||
id={{details.id}}
|
||||
>
|
||||
Disclose Me!
|
||||
</p>
|
||||
</disclosure.Details>
|
||||
<disclosure.Details
|
||||
@auto={{false}}
|
||||
as |details|>
|
||||
{{#if (not details.expanded)}}
|
||||
<p
|
||||
id={{details.id}}
|
||||
>
|
||||
Disclose Me by default but hide when the other details is disclosed!
|
||||
</p>
|
||||
{{/if}}
|
||||
</disclosure.Details>
|
||||
</Disclosure>
|
||||
```
|
||||
|
||||
## Arguments
|
||||
|
||||
| Argument | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `expanded` | `Boolean` | false | The _initial_ state of the disclosure. Please note: this is the _initial_ state only, please use the `disclosure.open` and `disclosure.close` for controling the state. |
|
||||
|
||||
## Exported API
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `Action` | `GlimmerComponent` | A contextual '<Action />' component with aria attributes correctly applied, please note you still need to add an 'on' modifier here so you can control whether it opens on click/hover etc |
|
||||
| `Details` | `GlimmerComponent` | A contextual '<Action />' component with aria attributes correctly applied, please note you still need to add an 'on' modifier here so you can control whether it opens on click/hover etc |
|
||||
| `open` | `Function` | Open the disclosure if its not already open |
|
||||
| `close` | `Function` | Close the disclosure if its not already closed |
|
||||
| `toggle` | `Function` | Toggle the open/close state of the disclosure |
|
||||
| `expanded` | `Boolean` | Whether the disclosure is 'expanded' or not |
|
||||
| `event` | `Boolean` | The event used to change the state of the disclosure |
|
||||
| `button` | `string` | An id to use on the trigger for the disclosure |
|
||||
| `panel` | `string` | An id to use on the panel for the disclosure |
|
||||
| `button` | `string` | A unique id reference to reference the an Action with if required for a11y reasons |
|
||||
| `controls` | `string` | An id to use on the panel for the disclosure |
|
||||
|
||||
## Slots
|
||||
|
||||
| Name | Description |
|
||||
| --- | --- |
|
||||
| `button` | Provides a configurable slot in which to add your open/close trigger |
|
||||
| `panel` | Provides a configurable slot in which to add your disclosed content |
|
||||
### disclosure.Action
|
||||
|
||||
An `<Action />` component with the correct aria attributes added.
|
||||
|
||||
### disclosure.Details
|
||||
|
||||
#### Arguments
|
||||
|
||||
| Argument | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `auto` | `Boolean` | true | Whether to automatically control the disclosure of the details component. Set to false to control this yourself. Please be aware of using `aria-hidden` if using CSS to control the visibility of disclosure (see examples above). |
|
||||
|
||||
#### Exported API
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | `String` | A unique id which you **should** (for aria reasons) use for the root DOM element you are controlling with the disclosure |
|
||||
| `expanded` | `Boolean` | An alias of `disclosure.expanded`. Whether the disclosure is 'expanded' or not. If disclosure of the `Details` is controlled via CSS you **should** use this to set/unset `aria-hidden` |
|
||||
|
||||
## See
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Action
|
||||
aria-expanded={{if @disclosure.expanded 'true' 'false'}}
|
||||
aria-controls={{@disclosure.panel}}
|
||||
id={{@disclosure.button}}
|
||||
aria-controls={{@disclosure.controls}}
|
||||
...attributes
|
||||
>
|
||||
{{yield}}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
{{#let
|
||||
(unique-id)
|
||||
as |id|}}
|
||||
{{#if (or
|
||||
(and (eq @auto undefined) @disclosure.expanded)
|
||||
(and (not-eq @auto undefined) (eq @auto false))
|
||||
)
|
||||
}}
|
||||
{{yield (hash
|
||||
id=id
|
||||
expanded=@disclosure.expanded
|
||||
)}}
|
||||
{{/if}}
|
||||
{{did-insert (fn @disclosure.add id)}}
|
||||
{{will-destroy (fn @disclosure.remove id)}}
|
||||
{{/let}}
|
|
@ -1,5 +1,6 @@
|
|||
<StateChart
|
||||
@src={{state-chart 'boolean'}}
|
||||
@initial={{if @expanded 'true' 'false'}}
|
||||
as |State Guard Action dispatch state|>
|
||||
{{#let (hash
|
||||
toggle=(fn dispatch 'TOGGLE')
|
||||
|
@ -8,22 +9,17 @@ as |State Guard Action dispatch state|>
|
|||
expanded=(state-matches state 'true')
|
||||
event=state.context
|
||||
button=(unique-id)
|
||||
panel=(unique-id)
|
||||
controls=this.ids
|
||||
) as |_api|}}
|
||||
{{#let (assign _api (hash
|
||||
Action=(component 'disclosure/action' disclosure=_api)
|
||||
Details=(component 'disclosure/details' disclosure=(hash
|
||||
add=this.add
|
||||
remove=this.remove
|
||||
expanded=(state-matches state 'true')
|
||||
))
|
||||
)) as |api|}}
|
||||
<div
|
||||
class={{class-map
|
||||
'disclosure'
|
||||
}}
|
||||
...attributes
|
||||
>
|
||||
{{yield api to="button"}}
|
||||
<State @matches="true">
|
||||
{{yield api to="panel"}}
|
||||
</State>
|
||||
</div>
|
||||
{{yield api}}
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
</StateChart>
|
|
@ -0,0 +1,23 @@
|
|||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { schedule } from '@ember/runloop';
|
||||
|
||||
export default class DisclosureComponent extends Component {
|
||||
@tracked ids = '';
|
||||
|
||||
@action
|
||||
add(id) {
|
||||
schedule('afterRender', () => {
|
||||
this.ids = `${this.ids}${this.ids.length > 0 ? ` ` : ``}${id}`;
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
remove(id) {
|
||||
this.ids = this.ids
|
||||
.split(' ')
|
||||
.filter(item => item !== id)
|
||||
.join(' ');
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@
|
|||
@import 'consul-ui/components/confirmation-dialog';
|
||||
@import 'consul-ui/components/copy-button';
|
||||
@import 'consul-ui/components/definition-table';
|
||||
@import 'consul-ui/components/disclosure-menu';
|
||||
@import 'consul-ui/components/display-toggle';
|
||||
@import 'consul-ui/components/dom-recycling-table';
|
||||
@import 'consul-ui/components/empty-state';
|
||||
|
|
Loading…
Reference in New Issue