diff --git a/ui/packages/consul-ui/app/components/disclosure-menu/README.mdx b/ui/packages/consul-ui/app/components/disclosure-menu/README.mdx new file mode 100644 index 0000000000..be5381c2df --- /dev/null +++ b/ui/packages/consul-ui/app/components/disclosure-menu/README.mdx @@ -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 +
+
+ Non-floating Menu +
+ + + {{if disclosure.expanded 'Close' 'Open'}} + + + + Item 1 + + + Item 2 + + + +
+
+
+ Floating Menu +
+ + + {{if disclosure.expanded 'Close' 'Open'}} + + + + Item 1 + + + Item 2 + + + +
+``` + +## 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 '' 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 '' 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 `` component with the correct aria attributes added. + +### menu.Menu + +A `` component with the correct aria attributes added. + +## See + +- [Template Source Code](./index.hbs) + +--- + diff --git a/ui/packages/consul-ui/app/components/disclosure-menu/action/index.hbs b/ui/packages/consul-ui/app/components/disclosure-menu/action/index.hbs new file mode 100644 index 0000000000..71175776d5 --- /dev/null +++ b/ui/packages/consul-ui/app/components/disclosure-menu/action/index.hbs @@ -0,0 +1,6 @@ +<@disclosure.Action + aria-haspopup="menu" + ...attributes +> + {{yield}} + diff --git a/ui/packages/consul-ui/app/components/disclosure-menu/index.hbs b/ui/packages/consul-ui/app/components/disclosure-menu/index.hbs new file mode 100644 index 0000000000..040113e41c --- /dev/null +++ b/ui/packages/consul-ui/app/components/disclosure-menu/index.hbs @@ -0,0 +1,16 @@ +
+ + {{yield (hash + Action=(component 'disclosure-menu/action' disclosure=disclosure) + Menu=(component 'disclosure-menu/menu' disclosure=disclosure) + disclosure=disclosure + toggle=disclosure.toggle + expanded=disclosure.expanded + )}} + +
diff --git a/ui/packages/consul-ui/app/components/disclosure-menu/index.scss b/ui/packages/consul-ui/app/components/disclosure-menu/index.scss new file mode 100644 index 0000000000..f9f374c2cd --- /dev/null +++ b/ui/packages/consul-ui/app/components/disclosure-menu/index.scss @@ -0,0 +1,3 @@ +.disclosure-menu { + position: relative; +} diff --git a/ui/packages/consul-ui/app/components/disclosure-menu/menu/index.hbs b/ui/packages/consul-ui/app/components/disclosure-menu/menu/index.hbs new file mode 100644 index 0000000000..4287d66b9d --- /dev/null +++ b/ui/packages/consul-ui/app/components/disclosure-menu/menu/index.hbs @@ -0,0 +1,15 @@ +<@disclosure.Details as |details|> + + {{yield (hash + items=menu.items + Item=menu.Item + Action=menu.Action + Separator=menu.Separator + )}} + + + diff --git a/ui/packages/consul-ui/app/components/disclosure/README.mdx b/ui/packages/consul-ui/app/components/disclosure/README.mdx index 2f7e35d56c..9e272e64c8 100644 --- a/ui/packages/consul-ui/app/components/disclosure/README.mdx +++ b/ui/packages/consul-ui/app/components/disclosure/README.mdx @@ -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 - - <:button as |disclosure|> - - {{if disclosure.expanded "Close" "Open"}} - - - <:panel as |disclosure|> + + + {{if disclosure.expanded "Close" "Open"}} + +

Disclose Me!

- +
``` +You can also use multiple Details components for a single Discloure Action component to control multiple areas. + +```hbs preview-template + + + {{if disclosure.expanded "Close" "Open"}} + + +

+ Disclose Me! +

+
+ +

+ Disclose Me also! +

+
+
+``` + +Or use two buttons/Actions to control one Detail (or multiple Details). + +```hbs preview-template + +
+ + {{if disclosure.expanded "1 Close" "1 Open"}} + + + {{if disclosure.expanded "2 Close" "2 Open"}} + + +

+ Disclose Me! +

+
+
+
+``` + +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 + + + {{if disclosure.expanded "Close" "Open"}} + + + {{#if details.expanded}} +

+ Disclose Me with a hbs conditional! +

+ {{/if}} +
+ +

+ Disclose Me via CSS! +

+
+
+``` + +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 + + + {{if disclosure.expanded "Close" "Open"}} + + +

+ Disclose Me! +

+
+ + {{#if (not details.expanded)}} +

+ Disclose Me by default but hide when the other details is disclosed! +

+ {{/if}} +
+
+``` + +## 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 '' 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 '' 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 `` 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 diff --git a/ui/packages/consul-ui/app/components/disclosure/action/index.hbs b/ui/packages/consul-ui/app/components/disclosure/action/index.hbs index 07265e48cb..d1285db156 100644 --- a/ui/packages/consul-ui/app/components/disclosure/action/index.hbs +++ b/ui/packages/consul-ui/app/components/disclosure/action/index.hbs @@ -1,7 +1,6 @@ {{yield}} diff --git a/ui/packages/consul-ui/app/components/disclosure/details/index.hbs b/ui/packages/consul-ui/app/components/disclosure/details/index.hbs new file mode 100644 index 0000000000..07dc7efe25 --- /dev/null +++ b/ui/packages/consul-ui/app/components/disclosure/details/index.hbs @@ -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}} diff --git a/ui/packages/consul-ui/app/components/disclosure/index.hbs b/ui/packages/consul-ui/app/components/disclosure/index.hbs index 73ef934d39..c5c5e2bbfa 100644 --- a/ui/packages/consul-ui/app/components/disclosure/index.hbs +++ b/ui/packages/consul-ui/app/components/disclosure/index.hbs @@ -1,5 +1,6 @@ {{#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|}} -
- {{yield api to="button"}} - - {{yield api to="panel"}} - -
+ {{yield api}} {{/let}} {{/let}}
\ No newline at end of file diff --git a/ui/packages/consul-ui/app/components/disclosure/index.js b/ui/packages/consul-ui/app/components/disclosure/index.js new file mode 100644 index 0000000000..0774ae3100 --- /dev/null +++ b/ui/packages/consul-ui/app/components/disclosure/index.js @@ -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(' '); + } +} diff --git a/ui/packages/consul-ui/app/styles/components.scss b/ui/packages/consul-ui/app/styles/components.scss index febe005c35..f90d8fe299 100644 --- a/ui/packages/consul-ui/app/styles/components.scss +++ b/ui/packages/consul-ui/app/styles/components.scss @@ -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';