Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CL-500] Add disclosure component and directive #11865

Merged
merged 5 commits into from
Nov 7, 2024
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Directive, HostBinding, HostListener, Input } from "@angular/core";

Check warning on line 1 in libs/components/src/disclosure/disclosure-trigger-for.directive.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/disclosure/disclosure-trigger-for.directive.ts#L1

Added line #L1 was not covered by tests

import { DisclosureComponent } from "./disclosure.component";

@Directive({
selector: "[bitDisclosureTriggerFor]",
exportAs: "disclosureTriggerFor",
standalone: true,
})
export class DisclosureTriggerForDirective {

Check warning on line 10 in libs/components/src/disclosure/disclosure-trigger-for.directive.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/disclosure/disclosure-trigger-for.directive.ts#L10

Added line #L10 was not covered by tests
/**
* Accepts template reference for a bit-disclosure component instance
*/
@Input("bitDisclosureTriggerFor") disclosure: DisclosureComponent;

@HostBinding("attr.aria-expanded") get ariaExpanded() {
return this.disclosure.open;

Check warning on line 17 in libs/components/src/disclosure/disclosure-trigger-for.directive.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/disclosure/disclosure-trigger-for.directive.ts#L17

Added line #L17 was not covered by tests
}

@HostBinding("attr.aria-controls") get ariaControls() {
return this.disclosure.id;

Check warning on line 21 in libs/components/src/disclosure/disclosure-trigger-for.directive.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/disclosure/disclosure-trigger-for.directive.ts#L21

Added line #L21 was not covered by tests
}

@HostListener("click") click() {
this.disclosure.open = !this.disclosure.open;

Check warning on line 25 in libs/components/src/disclosure/disclosure-trigger-for.directive.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/disclosure/disclosure-trigger-for.directive.ts#L25

Added line #L25 was not covered by tests
}
}
21 changes: 21 additions & 0 deletions libs/components/src/disclosure/disclosure.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Component, HostBinding, Input, booleanAttribute } from "@angular/core";

Check warning on line 1 in libs/components/src/disclosure/disclosure.component.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/disclosure/disclosure.component.ts#L1

Added line #L1 was not covered by tests

let nextId = 0;

Check warning on line 3 in libs/components/src/disclosure/disclosure.component.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/disclosure/disclosure.component.ts#L3

Added line #L3 was not covered by tests

@Component({
selector: "bit-disclosure",
standalone: true,
template: `<ng-content></ng-content>`,
})
export class DisclosureComponent {

Check warning on line 10 in libs/components/src/disclosure/disclosure.component.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/disclosure/disclosure.component.ts#L10

Added line #L10 was not covered by tests
/**
* Optionally init the disclosure in its opened state
*/
@Input({ transform: booleanAttribute }) open?: boolean = false;

Check warning on line 14 in libs/components/src/disclosure/disclosure.component.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/disclosure/disclosure.component.ts#L14

Added line #L14 was not covered by tests

@HostBinding("class") get classList() {
return this.open ? "" : "tw-hidden";
}

@HostBinding("id") id = `bit-disclosure-${nextId++}`;

Check warning on line 20 in libs/components/src/disclosure/disclosure.component.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/disclosure/disclosure.component.ts#L20

Added line #L20 was not covered by tests
}
55 changes: 55 additions & 0 deletions libs/components/src/disclosure/disclosure.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Meta, Story, Primary, Controls } from "@storybook/addon-docs";

import * as stories from "./disclosure.stories";

<Meta of={stories} />

```ts
import { DisclosureComponent, DisclosureTriggerForDirective } from "@bitwarden/components";
```

# Disclosure

The `bit-disclosure` component is used in tandem with the `bitDisclosureTriggerFor` directive to
create an accessible content area whose visibility is controlled by a trigger button.

To compose a disclosure and trigger:

1. Create a trigger component (see "Supported Trigger Components" section below)
2. Create a `bit-disclosure`
3. Set a template reference on the `bit-disclosure`
4. Use the `bitDisclosureTriggerFor` directive on the trigger component, and pass it the
`bit-disclosure` template reference
5. Set the `open` property on the `bit-disclosure` to init the disclosure as either currently
expanded or currently collapsed. The disclosure will default to `false`, meaning it defaults to
being hidden.

```
<button
type="button"
bitIconButton="bwi-sliders"
[buttonType]="'muted'"
[bitDisclosureTriggerFor]="disclosureRef"
></button>
<bit-disclosure #disclosureRef open>click button to hide this content</bit-disclosure>
```

<Story of={stories.DisclosureWithIconButton} />

<br />
<br />

## Supported Trigger Components

This is the list of currently supported trigger components:

- Icon button `muted` variant

## Accessibility

The disclosure and trigger directive functionality follow the
[Disclosure (Show/Hide)](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/) pattern for
accessibility, automatically handling the `aria-controls` and `aria-expanded` properties. A `button`
element must be used as the trigger for the disclosure. The `button` element must also have an
accessible label/title -- please follow the accessibility guidelines for whatever trigger component
you choose.
29 changes: 29 additions & 0 deletions libs/components/src/disclosure/disclosure.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Meta, moduleMetadata, StoryObj } from "@storybook/angular";

Check warning on line 1 in libs/components/src/disclosure/disclosure.stories.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/disclosure/disclosure.stories.ts#L1

Added line #L1 was not covered by tests

import { IconButtonModule } from "../icon-button";

Check warning on line 3 in libs/components/src/disclosure/disclosure.stories.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/disclosure/disclosure.stories.ts#L3

Added line #L3 was not covered by tests

import { DisclosureTriggerForDirective } from "./disclosure-trigger-for.directive";
import { DisclosureComponent } from "./disclosure.component";

Check warning on line 6 in libs/components/src/disclosure/disclosure.stories.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/disclosure/disclosure.stories.ts#L5-L6

Added lines #L5 - L6 were not covered by tests

export default {

Check warning on line 8 in libs/components/src/disclosure/disclosure.stories.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/disclosure/disclosure.stories.ts#L8

Added line #L8 was not covered by tests
title: "Component Library/Disclosure",
component: DisclosureComponent,
decorators: [
moduleMetadata({
imports: [DisclosureTriggerForDirective, DisclosureComponent, IconButtonModule],
}),
],
} as Meta<DisclosureComponent>;

type Story = StoryObj<DisclosureComponent>;

export const DisclosureWithIconButton: Story = {
render: (args) => ({

Check warning on line 21 in libs/components/src/disclosure/disclosure.stories.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/disclosure/disclosure.stories.ts#L20-L21

Added lines #L20 - L21 were not covered by tests
props: args,
template: /*html*/ `
<button type="button" bitIconButton="bwi-sliders" [buttonType]="'muted'" [bitDisclosureTriggerFor]="disclosureRef">
</button>
<bit-disclosure #disclosureRef class="tw-text-main tw-block" open>click button to hide this content</bit-disclosure>
willmartian marked this conversation as resolved.
Show resolved Hide resolved
`,
}),
};
2 changes: 2 additions & 0 deletions libs/components/src/disclosure/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./disclosure-trigger-for.directive";
export * from "./disclosure.component";

Check warning on line 2 in libs/components/src/disclosure/index.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/disclosure/index.ts#L1-L2

Added lines #L1 - L2 were not covered by tests
4 changes: 4 additions & 0 deletions libs/components/src/icon-button/icon-button.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,14 @@ const styles: Record<IconButtonType, string[]> = {
"tw-bg-transparent",
"!tw-text-muted",
"tw-border-transparent",
"aria-expanded:tw-bg-text-muted",
"aria-expanded:!tw-text-contrast",
"hover:tw-bg-transparent-hover",
"hover:tw-border-primary-700",
"focus-visible:before:tw-ring-primary-700",
"disabled:tw-opacity-60",
"aria-expanded:hover:tw-bg-secondary-700",
"aria-expanded:hover:tw-border-secondary-700",
"disabled:hover:tw-border-transparent",
"disabled:hover:tw-bg-transparent",
...focusRing,
Expand Down
20 changes: 9 additions & 11 deletions libs/components/src/icon-button/icon-button.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ Icon buttons can be found in other components such as: the
[dialog](?path=/docs/component-library-dialogs--docs), and
[table](?path=/docs/component-library-table--docs).

<Story id="component-library-banner--premium" />

## Styles

There are 4 common styles for button main, muted, contrast, and danger. The other styles follow the
Expand All @@ -40,48 +38,48 @@ button component styles.

Used for general icon buttons appearing on the themeโ€™s main `background`

<Story id="component-library-icon-button--main" />
<Story of={stories.Main} />

### Muted

Used for low emphasis icon buttons appearing on the themeโ€™s main `background`

<Story id="component-library-icon-button--muted" />
<Story of={stories.Muted} />

### Contrast

Used on a themeโ€™s colored or contrasting backgrounds such as in the navigation or on toasts and
banners.

<Story id="component-library-icon-button--contrast" />
<Story of={stories.Contrast} />

### Danger

Danger is used for โ€œtrashโ€ actions throughout the experience, most commonly in the bottom right of
the dialog component.

<Story id="component-library-icon-button--danger" />
<Story of={stories.Danger} />

### Primary

Used in place of the main button component if no text is used. This allows the button to display
square.

<Story id="component-library-icon-button--primary" />
<Story of={stories.Primary} />

### Secondary

Used in place of the main button component if no text is used. This allows the button to display
square.

<Story id="component-library-icon-button--secondary" />
<Story of={stories.Secondary} />

### Light

Used on a background that is dark in both light theme and dark theme. Example: end user navigation
styles.

<Story id="component-library-icon-button--light" />
<Story of={stories.Light} />

**Note:** Main and contrast styles appear on backgrounds where using `primary-700` as a focus
indicator does not meet WCAG graphic contrast guidelines.
Expand All @@ -95,11 +93,11 @@ with less padding around the icon, such as in the navigation component.

### Small

<Story id="component-library-icon-button--small" />
<Story of={stories.Small} />

### Default

<Story id="component-library-icon-button--default" />
<Story of={stories.Default} />

## Accessibility

Expand Down
8 changes: 4 additions & 4 deletions libs/components/src/icon-button/icon-button.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type Story = StoryObj<BitIconButtonComponent>;
export const Default: Story = {
render: (args) => ({
props: args,
template: `
template: /*html*/ `
<div class="tw-space-x-4">
<button bitIconButton="bwi-plus" [disabled]="disabled" [loading]="loading" buttonType="main" [size]="size">Button</button>
<button bitIconButton="bwi-plus" [disabled]="disabled" [loading]="loading" buttonType="muted" [size]="size">Button</button>
Expand Down Expand Up @@ -56,7 +56,7 @@ export const Small: Story = {
export const Primary: Story = {
render: (args) => ({
props: args,
template: `
template: /*html*/ `
<button bitIconButton="bwi-plus" [disabled]="disabled" [loading]="loading" [buttonType]="buttonType" [size]="size">Button</button>
`,
}),
Expand Down Expand Up @@ -96,7 +96,7 @@ export const Muted: Story = {
export const Light: Story = {
render: (args) => ({
props: args,
template: `
template: /*html*/ `
<div class="tw-bg-background-alt2 tw-p-6 tw-w-full tw-inline-block">
<button bitIconButton="bwi-plus" [disabled]="disabled" [loading]="loading" [buttonType]="buttonType" [size]="size">Button</button>
</div>
Expand All @@ -110,7 +110,7 @@ export const Light: Story = {
export const Contrast: Story = {
render: (args) => ({
props: args,
template: `
template: /*html*/ `
<div class="tw-bg-primary-600 tw-p-6 tw-w-full tw-inline-block">
<button bitIconButton="bwi-plus" [disabled]="disabled" [loading]="loading" [buttonType]="buttonType" [size]="size">Button</button>
</div>
Expand Down
1 change: 1 addition & 0 deletions libs/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
export * from "./color-password";
export * from "./container";
export * from "./dialog";
export * from "./disclosure";

Check warning on line 16 in libs/components/src/index.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/index.ts#L16

Added line #L16 was not covered by tests
export * from "./form-field";
export * from "./icon-button";
export * from "./icon";
Expand Down
Loading