mirror of https://github.com/hashicorp/consul
ui: Disclosure Component amends plus DisclosureMenu Component (#12304)
* ui: Disclosure amends plus DisclosureMenu Co-authored-by: Jamie White <jamie@jgwhite.co.uk>pull/12318/head
parent
0ac32122f1
commit
0e730328bd
|
@ -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
|
# 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
|
The disclosure exports an Action component already configured for use as a
|
||||||
you want to contruct your own trigger, disclosure has all the properties to
|
clickable action. But if you want to contruct your own trigger, disclosure has
|
||||||
enable you to do so.
|
all the properties to enable you to do so.
|
||||||
|
|
||||||
You should make use of the `disclosure.panel` property in order to 'tag' the
|
You should make use of the `disclosure.Details` property in order control the
|
||||||
disclosure panel you are using.
|
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
|
Clicking outside will not close the disclosure by default, if you require this
|
||||||
functionality please combine with our `{{on-outside 'click'}}` modifier (see example).
|
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
|
```hbs preview-template
|
||||||
<Disclosure>
|
<Disclosure as |disclosure|>
|
||||||
<:button as |disclosure|>
|
<disclosure.Action
|
||||||
<disclosure.Action
|
{{on-outside 'click' disclosure.close}}
|
||||||
{{on-outside 'click' disclosure.close}}
|
{{on 'click' disclosure.toggle}}
|
||||||
{{on 'click' disclosure.toggle}}
|
>
|
||||||
>
|
{{if disclosure.expanded "Close" "Open"}}
|
||||||
{{if disclosure.expanded "Close" "Open"}}
|
</disclosure.Action>
|
||||||
</disclosure.Action>
|
<disclosure.Details as |details|>
|
||||||
</:button>
|
|
||||||
<:panel as |disclosure|>
|
|
||||||
<p
|
<p
|
||||||
id={{disclosure.panel}}
|
id={{details.id}}
|
||||||
>
|
>
|
||||||
Disclose Me!
|
Disclose Me!
|
||||||
</p>
|
</p>
|
||||||
</:panel>
|
</disclosure.Details>
|
||||||
</Disclosure>
|
</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
|
## Exported API
|
||||||
|
|
||||||
| Name | Type | Description |
|
| 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 |
|
| `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 |
|
| `open` | `Function` | Open the disclosure if its not already open |
|
||||||
| `close` | `Function` | Close the disclosure if its not already closed |
|
| `close` | `Function` | Close the disclosure if its not already closed |
|
||||||
| `toggle` | `Function` | Toggle the open/close state of the disclosure |
|
| `toggle` | `Function` | Toggle the open/close state of the disclosure |
|
||||||
| `expanded` | `Boolean` | Whether the disclosure is 'expanded' or not |
|
| `expanded` | `Boolean` | Whether the disclosure is 'expanded' or not |
|
||||||
| `event` | `Boolean` | The event used to change the state of the disclosure |
|
| `event` | `Boolean` | The event used to change the state of the disclosure |
|
||||||
| `button` | `string` | An id to use on the trigger for the disclosure |
|
| `button` | `string` | A unique id reference to reference the an Action with if required for a11y reasons |
|
||||||
| `panel` | `string` | An id to use on the panel for the disclosure |
|
| `controls` | `string` | An id to use on the panel for the disclosure |
|
||||||
|
|
||||||
## Slots
|
|
||||||
|
|
||||||
| Name | Description |
|
### disclosure.Action
|
||||||
| --- | --- |
|
|
||||||
| `button` | Provides a configurable slot in which to add your open/close trigger |
|
An `<Action />` component with the correct aria attributes added.
|
||||||
| `panel` | Provides a configurable slot in which to add your disclosed content |
|
|
||||||
|
### 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
|
## See
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<Action
|
<Action
|
||||||
aria-expanded={{if @disclosure.expanded 'true' 'false'}}
|
aria-expanded={{if @disclosure.expanded 'true' 'false'}}
|
||||||
aria-controls={{@disclosure.panel}}
|
aria-controls={{@disclosure.controls}}
|
||||||
id={{@disclosure.button}}
|
|
||||||
...attributes
|
...attributes
|
||||||
>
|
>
|
||||||
{{yield}}
|
{{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
|
<StateChart
|
||||||
@src={{state-chart 'boolean'}}
|
@src={{state-chart 'boolean'}}
|
||||||
|
@initial={{if @expanded 'true' 'false'}}
|
||||||
as |State Guard Action dispatch state|>
|
as |State Guard Action dispatch state|>
|
||||||
{{#let (hash
|
{{#let (hash
|
||||||
toggle=(fn dispatch 'TOGGLE')
|
toggle=(fn dispatch 'TOGGLE')
|
||||||
|
@ -8,22 +9,17 @@ as |State Guard Action dispatch state|>
|
||||||
expanded=(state-matches state 'true')
|
expanded=(state-matches state 'true')
|
||||||
event=state.context
|
event=state.context
|
||||||
button=(unique-id)
|
button=(unique-id)
|
||||||
panel=(unique-id)
|
controls=this.ids
|
||||||
) as |_api|}}
|
) as |_api|}}
|
||||||
{{#let (assign _api (hash
|
{{#let (assign _api (hash
|
||||||
Action=(component 'disclosure/action' disclosure=_api)
|
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|}}
|
)) as |api|}}
|
||||||
<div
|
{{yield api}}
|
||||||
class={{class-map
|
|
||||||
'disclosure'
|
|
||||||
}}
|
|
||||||
...attributes
|
|
||||||
>
|
|
||||||
{{yield api to="button"}}
|
|
||||||
<State @matches="true">
|
|
||||||
{{yield api to="panel"}}
|
|
||||||
</State>
|
|
||||||
</div>
|
|
||||||
{{/let}}
|
{{/let}}
|
||||||
{{/let}}
|
{{/let}}
|
||||||
</StateChart>
|
</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/confirmation-dialog';
|
||||||
@import 'consul-ui/components/copy-button';
|
@import 'consul-ui/components/copy-button';
|
||||||
@import 'consul-ui/components/definition-table';
|
@import 'consul-ui/components/definition-table';
|
||||||
|
@import 'consul-ui/components/disclosure-menu';
|
||||||
@import 'consul-ui/components/display-toggle';
|
@import 'consul-ui/components/display-toggle';
|
||||||
@import 'consul-ui/components/dom-recycling-table';
|
@import 'consul-ui/components/dom-recycling-table';
|
||||||
@import 'consul-ui/components/empty-state';
|
@import 'consul-ui/components/empty-state';
|
||||||
|
|
Loading…
Reference in New Issue