Update CL documentation (#5379)
This commit is contained in:
parent
f51ed1092d
commit
d53f79e325
|
@ -1,3 +1,11 @@
|
||||||
{
|
{
|
||||||
"printWidth": 100
|
"printWidth": 100,
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "*.mdx",
|
||||||
|
"options": {
|
||||||
|
"proseWrap": "always"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,12 @@ import { Meta } from "@storybook/addon-docs";
|
||||||
|
|
||||||
# Async Actions In Forms
|
# Async Actions In Forms
|
||||||
|
|
||||||
These directives should be used when building forms with buttons that trigger long running tasks in the background,
|
These directives should be used when building forms with buttons that trigger long running tasks in
|
||||||
eg. Submit or Delete buttons. For buttons that are not associated with a form see [Standalone Async Actions](?path=/story/component-library-async-actions-standalone-documentation--page).
|
the background, eg. Submit or Delete buttons. For buttons that are not associated with a form see
|
||||||
|
[Standalone Async Actions](?path=/story/component-library-async-actions-standalone-documentation--page).
|
||||||
|
|
||||||
There are two separately supported use-cases: Submit buttons and standalone form buttons (eg. Delete buttons).
|
There are two separately supported use-cases: Submit buttons and standalone form buttons (eg. Delete
|
||||||
|
buttons).
|
||||||
|
|
||||||
## Usage: Submit buttons
|
## Usage: Submit buttons
|
||||||
|
|
||||||
|
@ -15,17 +17,19 @@ Adding async actions to submit buttons requires the following 3 steps
|
||||||
|
|
||||||
### 1. Add a handler to your `Component`
|
### 1. Add a handler to your `Component`
|
||||||
|
|
||||||
A handler is a function that returns a promise or an observable. Functions that return `void` are also supported which is
|
A handler is a function that returns a promise or an observable. Functions that return `void` are
|
||||||
useful because `return;` can be used to abort an action.
|
also supported which is useful because `return;` can be used to abort an action.
|
||||||
|
|
||||||
**NOTE:** Defining the handlers as arrow-functions assigned to variables is mandatory if the handler needs access to the parent
|
**NOTE:** Defining the handlers as arrow-functions assigned to variables is mandatory if the handler
|
||||||
component using the variable `this`.
|
needs access to the parent component using the variable `this`.
|
||||||
|
|
||||||
**NOTE:** `formGroup.invalid` will always return `true` after the first `await` operation, event if the form is not actually
|
**NOTE:** `formGroup.invalid` will always return `true` after the first `await` operation, event if
|
||||||
invalid. This is due to the form getting disabled by the `bitSubmit` directive while waiting for the async action to complete.
|
the form is not actually invalid. This is due to the form getting disabled by the `bitSubmit`
|
||||||
|
directive while waiting for the async action to complete.
|
||||||
|
|
||||||
**NOTE:** Handlers do not need to check if any previous requests have finished because the directives have built in protection against
|
**NOTE:** Handlers do not need to check if any previous requests have finished because the
|
||||||
users attempting to trigger new actions before the previous ones have finished.
|
directives have built in protection against users attempting to trigger new actions before the
|
||||||
|
previous ones have finished.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
@Component({...})
|
@Component({...})
|
||||||
|
@ -51,8 +55,8 @@ class Component {
|
||||||
|
|
||||||
Add the `bitSubmit` directive and supply the handler defined in step 1.
|
Add the `bitSubmit` directive and supply the handler defined in step 1.
|
||||||
|
|
||||||
**NOTE:** The `directive` is defined using the input syntax: `[input]="handler"`.
|
**NOTE:** The `directive` is defined using the input syntax: `[input]="handler"`. This is different
|
||||||
This is different from how submit handlers are usually defined with the output syntax `(ngSubmit)="handler()"`.
|
from how submit handlers are usually defined with the output syntax `(ngSubmit)="handler()"`.
|
||||||
|
|
||||||
**NOTE:** `[bitSubmit]` is used instead of `(ngSubmit)`. Using both is not supported.
|
**NOTE:** `[bitSubmit]` is used instead of `(ngSubmit)`. Using both is not supported.
|
||||||
|
|
||||||
|
@ -76,14 +80,15 @@ Adding async actions to standalone form buttons requires the following 3 steps.
|
||||||
|
|
||||||
### 1. Add a handler to your `Component`
|
### 1. Add a handler to your `Component`
|
||||||
|
|
||||||
A handler is a function that returns a promise or an observable. Functions that return `void` are also supported which is
|
A handler is a function that returns a promise or an observable. Functions that return `void` are
|
||||||
useful for aborting an action.
|
also supported which is useful for aborting an action.
|
||||||
|
|
||||||
**NOTE:** Defining the handlers as arrow-functions assigned to variables is mandatory if the handler needs access to the parent
|
**NOTE:** Defining the handlers as arrow-functions assigned to variables is mandatory if the handler
|
||||||
component using the variable `this`.
|
needs access to the parent component using the variable `this`.
|
||||||
|
|
||||||
**NOTE:** Handlers do not need to check if any previous requests have finished because the directives have built in protection against
|
**NOTE:** Handlers do not need to check if any previous requests have finished because the
|
||||||
users attempting to trigger new actions before the previous ones have finished.
|
directives have built in protection against users attempting to trigger new actions before the
|
||||||
|
previous ones have finished.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
@Component({...})
|
@Component({...})
|
||||||
|
@ -113,7 +118,8 @@ The `bitSubmit` directive is required because of its coordinating role inside of
|
||||||
|
|
||||||
### 3. Add directives to the `button` element
|
### 3. Add directives to the `button` element
|
||||||
|
|
||||||
Add `bitButton`, `bitFormButton`, `bitAction` directives to the button. Make sure to supply a handler.
|
Add `bitButton`, `bitFormButton`, `bitAction` directives to the button. Make sure to supply a
|
||||||
|
handler.
|
||||||
|
|
||||||
**NOTE:** A summary of what each directive does can be found inside the source code.
|
**NOTE:** A summary of what each directive does can be found inside the source code.
|
||||||
|
|
||||||
|
@ -124,7 +130,8 @@ Add `bitButton`, `bitFormButton`, `bitAction` directives to the button. Make sur
|
||||||
|
|
||||||
## `[bitSubmit]` Disabled Form Submit
|
## `[bitSubmit]` Disabled Form Submit
|
||||||
|
|
||||||
If you need your form to be able to submit even when the form is disabled, then add `[allowDisabledFormSubmit]="true"` to your `<form>`
|
If you need your form to be able to submit even when the form is disabled, then add
|
||||||
|
`[allowDisabledFormSubmit]="true"` to your `<form>`
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<form [formGroup]="formGroup" [bitSubmit]="submit" [allowDisabledFormSubmit]="true">...</form>
|
<form [formGroup]="formGroup" [bitSubmit]="submit" [allowDisabledFormSubmit]="true">...</form>
|
||||||
|
|
|
@ -4,14 +4,14 @@ import { Meta } from "@storybook/addon-docs";
|
||||||
|
|
||||||
# Async Actions
|
# Async Actions
|
||||||
|
|
||||||
The directives in this module makes it easier for developers to reflect the progress of async actions in the UI when using
|
The directives in this module makes it easier for developers to reflect the progress of async
|
||||||
buttons, while also providing robust and standardized error handling.
|
actions in the UI when using buttons, while also providing robust and standardized error handling.
|
||||||
|
|
||||||
These buttons can either be standalone (such as Refresh buttons), submit buttons for forms or as standalone buttons
|
These buttons can either be standalone (such as Refresh buttons), submit buttons for forms or as
|
||||||
that are part of a form (such as Delete buttons).
|
standalone buttons that are part of a form (such as Delete buttons).
|
||||||
|
|
||||||
These directives are meant to replace the older `appApiAction` directive, providing the option to use `observables` and reduce
|
These directives are meant to replace the older `appApiAction` directive, providing the option to
|
||||||
clutter inside our view `components`.
|
use `observables` and reduce clutter inside our view `components`.
|
||||||
|
|
||||||
## When to use?
|
## When to use?
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,9 @@ import { Meta } from "@storybook/addon-docs";
|
||||||
|
|
||||||
# Standalone Async Actions
|
# Standalone Async Actions
|
||||||
|
|
||||||
These directives should be used when building a standalone button that triggers a long running task in the background,
|
These directives should be used when building a standalone button that triggers a long running task
|
||||||
eg. Refresh buttons. For non-submit buttons that are associated with forms see [Async Actions In Forms](?path=/story/component-library-async-actions-in-forms-documentation--page).
|
in the background, eg. Refresh buttons. For non-submit buttons that are associated with forms see
|
||||||
|
[Async Actions In Forms](?path=/story/component-library-async-actions-in-forms-documentation--page).
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -13,14 +14,15 @@ Adding async actions to standalone buttons requires the following 2 steps
|
||||||
|
|
||||||
### 1. Add a handler to your `Component`
|
### 1. Add a handler to your `Component`
|
||||||
|
|
||||||
A handler is a function that returns a promise or an observable. Functions that return `void` are also supported which is
|
A handler is a function that returns a promise or an observable. Functions that return `void` are
|
||||||
useful because `return;` can be used to abort an action.
|
also supported which is useful because `return;` can be used to abort an action.
|
||||||
|
|
||||||
**NOTE:** Defining the handlers as arrow-functions assigned to variables is mandatory if the handler needs access to the parent
|
**NOTE:** Defining the handlers as arrow-functions assigned to variables is mandatory if the handler
|
||||||
component using the variable `this`.
|
needs access to the parent component using the variable `this`.
|
||||||
|
|
||||||
**NOTE:** Handlers do not need to check if any previous requests have finished because the directives have built in protection against
|
**NOTE:** Handlers do not need to check if any previous requests have finished because the
|
||||||
users attempting to trigger new actions before the previous ones have finished.
|
directives have built in protection against users attempting to trigger new actions before the
|
||||||
|
previous ones have finished.
|
||||||
|
|
||||||
#### Example using promises
|
#### Example using promises
|
||||||
|
|
||||||
|
@ -48,8 +50,8 @@ class Component {
|
||||||
|
|
||||||
Add the `bitAction` directive and supply the handler defined in step 1.
|
Add the `bitAction` directive and supply the handler defined in step 1.
|
||||||
|
|
||||||
**NOTE:** The `directive` is defined using the input syntax: `[input]="handler"`.
|
**NOTE:** The `directive` is defined using the input syntax: `[input]="handler"`. This is different
|
||||||
This is different from how click handlers are usually defined with the output syntax `(click)="handler()"`.
|
from how click handlers are usually defined with the output syntax `(click)="handler()"`.
|
||||||
|
|
||||||
**NOTE:** `[bitAction]` is used instead of `(click)`. Using both is not supported.
|
**NOTE:** `[bitAction]` is used instead of `(click)`. Using both is not supported.
|
||||||
|
|
||||||
|
|
|
@ -95,18 +95,12 @@ const Template: Story<BitFormFieldComponent> = (args: BitFormFieldComponent) =>
|
||||||
...args,
|
...args,
|
||||||
},
|
},
|
||||||
template: `
|
template: `
|
||||||
<form [formGroup]="formObj" (ngSubmit)="submit()">
|
<form [formGroup]="formObj">
|
||||||
<bit-form-field>
|
<bit-form-field>
|
||||||
<bit-label>Name</bit-label>
|
<bit-label>Label</bit-label>
|
||||||
<input bitInput formControlName="name" />
|
<input bitInput formControlName="name" />
|
||||||
|
<bit-hint>Optional Hint</bit-hint>
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
|
|
||||||
<bit-form-field>
|
|
||||||
<bit-label>Email</bit-label>
|
|
||||||
<input bitInput formControlName="email" />
|
|
||||||
</bit-form-field>
|
|
||||||
|
|
||||||
<button type="submit" bitButton buttonType="primary">Submit</button>
|
|
||||||
</form>
|
</form>
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Component, Input } from "@angular/core";
|
import { FormsModule, ReactiveFormsModule, FormControl, FormGroup } from "@angular/forms";
|
||||||
import { FormsModule, ReactiveFormsModule, FormBuilder } from "@angular/forms";
|
|
||||||
import { Meta, moduleMetadata, Story } from "@storybook/angular";
|
import { Meta, moduleMetadata, Story } from "@storybook/angular";
|
||||||
|
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
|
@ -7,70 +6,13 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
import { I18nMockService } from "../utils/i18n-mock.service";
|
import { I18nMockService } from "../utils/i18n-mock.service";
|
||||||
|
|
||||||
import { RadioButtonModule } from "./radio-button.module";
|
import { RadioButtonModule } from "./radio-button.module";
|
||||||
|
import { RadioGroupComponent } from "./radio-group.component";
|
||||||
const template = `
|
|
||||||
<form [formGroup]="formObj">
|
|
||||||
<bit-radio-group formControlName="radio" aria-label="Example radio group">
|
|
||||||
<bit-label *ngIf="label">Group of radio buttons</bit-label>
|
|
||||||
<bit-radio-button *ngFor="let option of TestValue | keyvalue" [ngClass]="{ 'tw-block': blockLayout }"
|
|
||||||
[value]="option.value" id="radio-{{option.key}}" [disabled]="optionDisabled?.includes(option.value)">
|
|
||||||
<bit-label>{{ option.key }}</bit-label>
|
|
||||||
<bit-hint *ngIf="blockLayout">This is a hint for the {{option.key}} option</bit-hint>
|
|
||||||
</bit-radio-button>
|
|
||||||
</bit-radio-group>
|
|
||||||
</form>`;
|
|
||||||
|
|
||||||
const TestValue = {
|
|
||||||
First: 0,
|
|
||||||
Second: 1,
|
|
||||||
Third: 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
const reverseObject = (obj: Record<any, any>) =>
|
|
||||||
Object.fromEntries(Object.entries(obj).map(([key, value]) => [value, key]));
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: "app-example",
|
|
||||||
template: template,
|
|
||||||
})
|
|
||||||
class ExampleComponent {
|
|
||||||
protected TestValue = TestValue;
|
|
||||||
|
|
||||||
protected formObj = this.formBuilder.group({
|
|
||||||
radio: TestValue.First,
|
|
||||||
});
|
|
||||||
|
|
||||||
@Input() layout: "block" | "inline" = "inline";
|
|
||||||
|
|
||||||
@Input() label: boolean;
|
|
||||||
|
|
||||||
@Input() set selected(value: number) {
|
|
||||||
this.formObj.patchValue({ radio: value });
|
|
||||||
}
|
|
||||||
|
|
||||||
@Input() set groupDisabled(disable: boolean) {
|
|
||||||
if (disable) {
|
|
||||||
this.formObj.disable();
|
|
||||||
} else {
|
|
||||||
this.formObj.enable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Input() optionDisabled: number[] = [];
|
|
||||||
|
|
||||||
get blockLayout() {
|
|
||||||
return this.layout === "block";
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(private formBuilder: FormBuilder) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Component Library/Form/Radio Button",
|
title: "Component Library/Form/Radio Button",
|
||||||
component: ExampleComponent,
|
component: RadioGroupComponent,
|
||||||
decorators: [
|
decorators: [
|
||||||
moduleMetadata({
|
moduleMetadata({
|
||||||
declarations: [ExampleComponent],
|
|
||||||
imports: [FormsModule, ReactiveFormsModule, RadioButtonModule],
|
imports: [FormsModule, ReactiveFormsModule, RadioButtonModule],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
|
@ -92,56 +34,65 @@ export default {
|
||||||
url: "https://www.figma.com/file/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library?node-id=3930%3A16850&t=xXPx6GJYsJfuMQPE-4",
|
url: "https://www.figma.com/file/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library?node-id=3930%3A16850&t=xXPx6GJYsJfuMQPE-4",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
args: {
|
|
||||||
selected: TestValue.First,
|
|
||||||
groupDisabled: false,
|
|
||||||
optionDisabled: null,
|
|
||||||
label: true,
|
|
||||||
layout: "inline",
|
|
||||||
},
|
|
||||||
argTypes: {
|
|
||||||
selected: {
|
|
||||||
options: Object.values(TestValue),
|
|
||||||
control: {
|
|
||||||
type: "inline-radio",
|
|
||||||
labels: reverseObject(TestValue),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
optionDisabled: {
|
|
||||||
options: Object.values(TestValue),
|
|
||||||
control: {
|
|
||||||
type: "check",
|
|
||||||
labels: reverseObject(TestValue),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
layout: {
|
|
||||||
options: ["inline", "block"],
|
|
||||||
control: {
|
|
||||||
type: "inline-radio",
|
|
||||||
labels: ["inline", "block"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as Meta;
|
} as Meta;
|
||||||
|
|
||||||
const storyTemplate = `<app-example [selected]="selected" [groupDisabled]="groupDisabled" [optionDisabled]="optionDisabled" [label]="label" [layout]="layout"></app-example>`;
|
const InlineTemplate: Story<RadioGroupComponent> = (args: RadioGroupComponent) => ({
|
||||||
|
props: {
|
||||||
|
formObj: new FormGroup({
|
||||||
|
radio: new FormControl(0),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<form [formGroup]="formObj">
|
||||||
|
<bit-radio-group formControlName="radio" aria-label="Example radio group">
|
||||||
|
<bit-label>Group of radio buttons</bit-label>
|
||||||
|
|
||||||
const InlineTemplate: Story<ExampleComponent> = (args: ExampleComponent) => ({
|
<bit-radio-button id="radio-first" [value]="0">
|
||||||
props: args,
|
<bit-label>First</bit-label>
|
||||||
template: storyTemplate,
|
</bit-radio-button>
|
||||||
|
|
||||||
|
<bit-radio-button id="radio-second" [value]="1">
|
||||||
|
<bit-label>Second</bit-label>
|
||||||
|
</bit-radio-button>
|
||||||
|
|
||||||
|
<bit-radio-button id="radio-third" [value]="2">
|
||||||
|
<bit-label>Third</bit-label>
|
||||||
|
</bit-radio-button>
|
||||||
|
</bit-radio-group>
|
||||||
|
</form>
|
||||||
|
`,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Inline = InlineTemplate.bind({});
|
export const Inline = InlineTemplate.bind({});
|
||||||
Inline.args = {
|
|
||||||
layout: "inline",
|
|
||||||
};
|
|
||||||
|
|
||||||
const BlockTemplate: Story<ExampleComponent> = (args: ExampleComponent) => ({
|
const BlockTemplate: Story<RadioGroupComponent> = (args: RadioGroupComponent) => ({
|
||||||
props: args,
|
props: {
|
||||||
template: storyTemplate,
|
formObj: new FormGroup({
|
||||||
|
radio: new FormControl(0),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<form [formGroup]="formObj">
|
||||||
|
<bit-radio-group formControlName="radio" aria-label="Example radio group">
|
||||||
|
<bit-label>Group of radio buttons</bit-label>
|
||||||
|
|
||||||
|
<bit-radio-button id="radio-first" class="tw-block" [value]="0">
|
||||||
|
<bit-label>First</bit-label>
|
||||||
|
<bit-hint>This is a hint for the first option</bit-hint>
|
||||||
|
</bit-radio-button>
|
||||||
|
|
||||||
|
<bit-radio-button id="radio-second" class="tw-block" [value]="1">
|
||||||
|
<bit-label>Second</bit-label>
|
||||||
|
<bit-hint>This is a hint for the second option</bit-hint>
|
||||||
|
</bit-radio-button>
|
||||||
|
|
||||||
|
<bit-radio-button id="radio-third" class="tw-block" [value]="2">
|
||||||
|
<bit-label>Third</bit-label>
|
||||||
|
<bit-hint>This is a hint for the third option</bit-hint>
|
||||||
|
</bit-radio-button>
|
||||||
|
</bit-radio-group>
|
||||||
|
</form>
|
||||||
|
`,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Block = BlockTemplate.bind({});
|
export const Block = BlockTemplate.bind({});
|
||||||
Block.args = {
|
|
||||||
layout: "block",
|
|
||||||
};
|
|
||||||
|
|
|
@ -81,13 +81,13 @@ import { Meta } from "@storybook/addon-docs";
|
||||||
|
|
||||||
# Bitwarden Component Library
|
# Bitwarden Component Library
|
||||||
|
|
||||||
The Bitwarden Component Library is a collection of reusable low level components which empowers designers and
|
The Bitwarden Component Library is a collection of reusable low level components which empowers
|
||||||
developers to work more efficiently. The primary goal is to ensure a consistent design and behavior across the
|
designers and developers to work more efficiently. The primary goal is to ensure a consistent design
|
||||||
different clients and platforms. Currently the primary focus is the web based clients, namely _web_, _browser_ and
|
and behavior across the different clients and platforms. Currently the primary focus is the web
|
||||||
_desktop_.
|
based clients, namely _web_, _browser_ and _desktop_.
|
||||||
|
|
||||||
**Roll out status:** we are currently in the process of transitioning the Web Vault to utilize the component library
|
**Roll out status:** we are currently in the process of transitioning the Web Vault to utilize the
|
||||||
and the other clients will follow once this work is completed.
|
component library and the other clients will follow once this work is completed.
|
||||||
|
|
||||||
<div className="subheading">Configure</div>
|
<div className="subheading">Configure</div>
|
||||||
|
|
||||||
|
|
|
@ -4,20 +4,21 @@ import { Meta, Story } from "@storybook/addon-docs";
|
||||||
|
|
||||||
# Banner
|
# Banner
|
||||||
|
|
||||||
Banners are used for important communication with the user that needs to be seen right away, but has little effect on
|
Banners are used for important communication with the user that needs to be seen right away, but has
|
||||||
the experience. Banners appear at the top of the user's screen on page load and persist across all pages a user
|
little effect on the experience. Banners appear at the top of the user's screen on page load and
|
||||||
navigates to.
|
persist across all pages a user navigates to.
|
||||||
|
|
||||||
- They should always be dismissable and never use a timeout. If a user dismisses a banner, it should not reappear
|
- They should always be dismissable and never use a timeout. If a user dismisses a banner, it should
|
||||||
during that same active session.
|
not reappear during that same active session.
|
||||||
- Use banners sparingly, as they can feel intrusive to the user if they appear unexpectedly. Their effectiveness may
|
- Use banners sparingly, as they can feel intrusive to the user if they appear unexpectedly. Their
|
||||||
decrease if too many are used.
|
effectiveness may decrease if too many are used.
|
||||||
- Avoid stacking multiple banners.
|
- Avoid stacking multiple banners.
|
||||||
- Banners supports buttons and anchors using [bitLink](?path=/story/component-library-link--anchors).
|
- Banners support a button link (text button).
|
||||||
|
|
||||||
## Types
|
## Types
|
||||||
|
|
||||||
Icons should remain consistent across these types. Do not change the icon without consulting designers.
|
Icons should remain consistent across these types. Do not change the icon without consulting
|
||||||
|
designers.
|
||||||
|
|
||||||
Use the following guidelines to help choose the correct type of banner.
|
Use the following guidelines to help choose the correct type of banner.
|
||||||
|
|
||||||
|
@ -47,5 +48,6 @@ Rarely used, but may be used to alert users over critical messages or very outda
|
||||||
|
|
||||||
## Accessibility
|
## Accessibility
|
||||||
|
|
||||||
Dialogs sets the `role="status"` and `aria-live="polite"` attributes to ensure screen readers announce the content
|
Banners sets the `role="status"` and `aria-live="polite"` attributes to ensure screen readers
|
||||||
prior to the test of the page. This behaviour can be disabled by setting `[useAlertRole]="false"`.
|
announce the content prior to the test of the page. This behaviour can be disabled by setting
|
||||||
|
`[useAlertRole]="false"`.
|
||||||
|
|
|
@ -4,30 +4,54 @@ import { Meta, Story } from "@storybook/addon-docs";
|
||||||
|
|
||||||
# Button
|
# Button
|
||||||
|
|
||||||
Use buttons for actions in forms, dialogs, and more with support for style, block, icon, and state.
|
Buttons are interactive elements that can be triggered using a mouse, keyboard, or touch. They are
|
||||||
|
used to indicate actions that can be performed by a user such as submitting a form.
|
||||||
|
|
||||||
For pairings in the bottom left corner of a page or component, the `primary` call to action will go on the left side of a button group with the `secondary` call to action option on the left.
|
## Guidelines
|
||||||
|
|
||||||
Pairings in the top right corner of a page, should have the `primary` call to action on the right.
|
### Choosing the `<a>` or `<button>`
|
||||||
|
|
||||||
Groups of buttons should have 1rem of spacing between them.
|
Buttons can use either the `<a>` or `<button>` tags. Choose which based on the action the button
|
||||||
|
takes:
|
||||||
## Choosing the `<a>` or `<button>`
|
|
||||||
|
|
||||||
Buttons can use either the `<a>` or `<button>` tags. Choose which based on the action the button takes:
|
|
||||||
|
|
||||||
- If navigating to a new page, use `<a>`
|
- If navigating to a new page, use `<a>`
|
||||||
- If taking an action on the current page use `<button>`
|
- If taking an action on the current page use `<button>`
|
||||||
- If the button launches a dialog, use `<button>`
|
- If the button launches a dialog, use `<button>`
|
||||||
|
|
||||||
## Submit and async actions
|
### Groups
|
||||||
|
|
||||||
Both submit and async action buttons use a loading button state while an action is taken.
|
Groups of buttons should be seperated by a `0.5` rem gap. Usually acomplished by using the
|
||||||
|
`tw-gap-2` class in the button group container.
|
||||||
|
|
||||||
|
Groups within page content, dialog footers or forms should have the `primary` call to action placed
|
||||||
|
to left. Groups in headers and navigational areas should have the `primary` call to action on the
|
||||||
|
right.
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
Please follow these guidelines to ensure that buttons are accessible to all users.
|
||||||
|
|
||||||
|
### Color contrast
|
||||||
|
|
||||||
|
All button styles are WCAG compliant when displayed on `background` and `background-alt` colors. To
|
||||||
|
use a button on a different background, double check that the color contrast is sufficient in both
|
||||||
|
the light and dark themes.
|
||||||
|
|
||||||
|
### Loading Buttons
|
||||||
|
|
||||||
|
Include an `aria-label` attribute that defaults to “loading” but can be configurable per
|
||||||
|
implementation. On click, the screen reader should announce the `aria-label`. Once the action is
|
||||||
|
compelted, use another messaging pattern to alert the user that the action is complete (example:
|
||||||
|
success toast).
|
||||||
|
|
||||||
|
### Submit and async actions
|
||||||
|
|
||||||
|
Both submit and async action buttons use a loading button state while an action is taken. If your
|
||||||
|
button is preforming a long running task in the background like a server API call, be sure to review
|
||||||
|
the [Async Actions Directive](?path=/story/component-library-async-actions-overview--page).
|
||||||
|
|
||||||
<Story id="component-library-button--loading" />
|
<Story id="component-library-button--loading" />
|
||||||
|
|
||||||
If your button is preforming a long running task in the background like a server API call, be sure to review the [Async Actions Directive](https://components.bitwarden.com/?path=/story/component-library-async-actions-overview--page).
|
|
||||||
|
|
||||||
## Styles
|
## Styles
|
||||||
|
|
||||||
There are 3 main styles for the button: Primary, Secondary, and Danger.
|
There are 3 main styles for the button: Primary, Secondary, and Danger.
|
||||||
|
@ -36,13 +60,16 @@ There are 3 main styles for the button: Primary, Secondary, and Danger.
|
||||||
|
|
||||||
<Story id="component-library-button--primary" />
|
<Story id="component-library-button--primary" />
|
||||||
|
|
||||||
Use the primary button styling for all Primary call to actions. An action is "primary" if it relates to the main purpose of a page. There should never be 2 primary styled buttons next to each other.
|
Use the primary button styling for all Primary call to actions. An action is "primary" if it relates
|
||||||
|
to the main purpose of a page. There should never be 2 primary styled buttons next to each other.
|
||||||
|
|
||||||
### Secondary
|
### Secondary
|
||||||
|
|
||||||
<Story id="component-library-button--secondary" />
|
<Story id="component-library-button--secondary" />
|
||||||
|
|
||||||
The secondary styling should be used for secondary calls to action. An action is "secondary" if it relates indirectly to the purpose of a page. There may be multiple secondary buttons next to each other; however, generally there should only be 1 or 2 calls to action per page.
|
The secondary styling should be used for secondary calls to action. An action is "secondary" if it
|
||||||
|
relates indirectly to the purpose of a page. There may be multiple secondary buttons next to each
|
||||||
|
other; however, generally there should only be 1 or 2 calls to action per page.
|
||||||
|
|
||||||
### Danger
|
### Danger
|
||||||
|
|
||||||
|
@ -52,22 +79,14 @@ Use the danger styling only in settings when the user may preform a permanent ac
|
||||||
|
|
||||||
## Disabled UI
|
## Disabled UI
|
||||||
|
|
||||||
Both the disabled and loading states use the default state’s color with a 60% opacity or `tw-opacity-60`.
|
Both the disabled and loading states use the default state’s color with a 60% opacity or
|
||||||
|
`tw-opacity-60`.
|
||||||
|
|
||||||
<Story id="component-library-button--disabled" />
|
<Story id="component-library-button--disabled" />
|
||||||
|
|
||||||
## Block
|
## Block
|
||||||
|
|
||||||
Typically button widths expand with their text. In some causes though buttons may need to be block where the width is fixed and the text wraps to 2 lines if exceeding the button’s width.
|
Typically button widths expand with their text. In some causes though buttons may need to be block
|
||||||
|
where the width is fixed and the text wraps to 2 lines if exceeding the button’s width.
|
||||||
|
|
||||||
<Story id="component-library-button--block" />
|
<Story id="component-library-button--block" />
|
||||||
|
|
||||||
## Accessibility
|
|
||||||
|
|
||||||
### Color contrast
|
|
||||||
|
|
||||||
All button styles are WCAG compliant when displayed on `background` and `background-alt` colors. To use a button on a different background, double check that the color contrast is sufficient in both the light and dark themes.
|
|
||||||
|
|
||||||
### Loading Buttons
|
|
||||||
|
|
||||||
Include an `aria-label` attribute that defaults to “loading” but can be configurable per implementation. On click, the screen reader should announce the `aria-label`. Once the action is compelted, use another messaging pattern to alert the user that the action is complete (example: success toast).
|
|
||||||
|
|
|
@ -89,6 +89,17 @@ th {
|
||||||
|
|
||||||
# Colors
|
# Colors
|
||||||
|
|
||||||
|
Tailwind traditionally has a very large color palette. Bitwarden has their own more limited color
|
||||||
|
palette instead.
|
||||||
|
|
||||||
|
This has a couple of advantages:
|
||||||
|
|
||||||
|
- Promotes consistency across the application.
|
||||||
|
- Easier to maintain and make adjustments.
|
||||||
|
- Allows us to support more than two themes light & dark, should it be needed.
|
||||||
|
|
||||||
|
Below are all the permited colors. Please consult design before considering adding a new color.
|
||||||
|
|
||||||
<div class="tw-flex tw-space-x-4">
|
<div class="tw-flex tw-space-x-4">
|
||||||
<Table />
|
<Table />
|
||||||
<Table class="theme_dark tw-bg-background" />
|
<Table class="theme_dark tw-bg-background" />
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { Meta, Story, Source } from "@storybook/addon-docs";
|
||||||
|
|
||||||
|
<Meta title="Documentation/Forms" />
|
||||||
|
|
||||||
|
# Forms
|
||||||
|
|
||||||
|
Examples and usage guidelines for form control styles, layout options, and custom components for
|
||||||
|
creating a wide variety of forms.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Component Library forms should always be built using [Angular Reactive Forms][reactive]. Please read
|
||||||
|
[ADR-0001][adr-0001] for a background to this decision. In practice this means that forms should
|
||||||
|
always use the native `form` element and bind a `formGroup`.
|
||||||
|
|
||||||
|
Forms consists of one or multiple sections, and ends with one or multiple buttons.
|
||||||
|
|
||||||
|
### Form Field
|
||||||
|
|
||||||
|
A form field is the most common section in a form. It consists of a label, control and a optional
|
||||||
|
hint.
|
||||||
|
|
||||||
|
<Story id="component-library-form-field--default" />
|
||||||
|
|
||||||
|
<Source id="component-library-form-field--default" />
|
||||||
|
|
||||||
|
### Radio group
|
||||||
|
|
||||||
|
A radio group is a form field that consists of a main label and multiple radio groups. Each group
|
||||||
|
consists of a label and a radio input.
|
||||||
|
|
||||||
|
#### Block
|
||||||
|
|
||||||
|
<Story id="component-library-form-radio-button--block" />
|
||||||
|
|
||||||
|
<Source id="component-library-form-radio-button--block" />
|
||||||
|
|
||||||
|
#### Inline
|
||||||
|
|
||||||
|
<Story id="component-library-form-radio-button--inline" />
|
||||||
|
|
||||||
|
<Source id="component-library-form-radio-button--inline" />
|
||||||
|
|
||||||
|
## Full Example
|
||||||
|
|
||||||
|
<Story id="component-library-form--full-example" />
|
||||||
|
|
||||||
|
<Source id="component-library-form--full-example" />
|
||||||
|
|
||||||
|
[reactive]: https://angular.io/guide/reactive-forms
|
||||||
|
[adr-0001]: https://contributing.bitwarden.com/architecture/adr/reactive-forms
|
|
@ -6,7 +6,11 @@ import { Meta } from "@storybook/addon-docs/";
|
||||||
|
|
||||||
# Iconography
|
# Iconography
|
||||||
|
|
||||||
Avoid using icons to convey information unless paired with meaningful, clear text. If an icon must be used and text cannot be displayed visually along with the icon, use an `aria-label` to provide the text to screen readers, and a `title` attribute to provide the text visually through a tool tip. Note: this pattern should only be followed for very common iconography such as, a settings cog icon or an options menu icon.
|
Avoid using icons to convey information unless paired with meaningful, clear text. If an icon must
|
||||||
|
be used and text cannot be displayed visually along with the icon, use an `aria-label` to provide
|
||||||
|
the text to screen readers, and a `title` attribute to provide the text visually through a tool tip.
|
||||||
|
Note: this pattern should only be followed for very common iconography such as, a settings cog icon
|
||||||
|
or an options menu icon.
|
||||||
|
|
||||||
## Status Indicators
|
## Status Indicators
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@ import { Meta } from "@storybook/addon-docs";
|
||||||
|
|
||||||
# `bitInput`
|
# `bitInput`
|
||||||
|
|
||||||
`bitInput` is an Angular directive to be used on `<input>`, `<select>`, and `<textarea>`
|
`bitInput` is an Angular directive to be used on `<input>`, `<select>`, and `<textarea>` tags in
|
||||||
tags in order to provide standardized TailwindCss styling, error handling, and more.
|
order to provide standardized TailwindCss styling, error handling, and more. It is meant to be used
|
||||||
It is meant to be used within a `<bit-form-field>` custom component.
|
within a `<bit-form-field>` custom component.
|
||||||
|
|
||||||
## Basic Usage Example
|
## Basic Usage Example
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ It is meant to be used within a `<bit-form-field>` custom component.
|
||||||
|
|
||||||
## Disabled `bitInput` and Error Handling
|
## Disabled `bitInput` and Error Handling
|
||||||
|
|
||||||
If you would like to be able to still show errors when an input is disabled for
|
If you would like to be able to still show errors when an input is disabled for specific validation
|
||||||
specific validation scenarios, then set `[showErrorsWhenDisabled]="true"`
|
scenarios, then set `[showErrorsWhenDisabled]="true"`
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<bit-form-field>
|
<bit-form-field>
|
||||||
|
@ -31,7 +31,8 @@ specific validation scenarios, then set `[showErrorsWhenDisabled]="true"`
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
```
|
```
|
||||||
|
|
||||||
**NOTE:** Disabling a FormControl removes validation errors so you must manually set the errors after disabling:
|
**NOTE:** Disabling a FormControl removes validation errors so you must manually set the errors
|
||||||
|
after disabling:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
get exampleFormCtrl(): FormControl {
|
get exampleFormCtrl(): FormControl {
|
||||||
|
|
|
@ -6,13 +6,37 @@ import { Meta, Story, Source } from "@storybook/addon-docs";
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
All tables should have a visible horizontal header and label for each column.
|
The table component provides a comprehensive way to display, sort and filter data. It consists of
|
||||||
|
two portions, a UI component called `bit-table` and the underlying data source `TableDataSource`.
|
||||||
|
This documentation will initially focus on the UI portion before covering the data source.
|
||||||
|
|
||||||
|
## UI Component
|
||||||
|
|
||||||
|
The UI component consists of a couple of elements.
|
||||||
|
|
||||||
|
- `bit-table`: The main component that creates a native table element and applies the table styling.
|
||||||
|
- `header`: A container for the table header.
|
||||||
|
- `body`: A container for the table body.
|
||||||
|
- `bitCell`: A cell within the table. Used for both headers and content.
|
||||||
|
|
||||||
|
### Guidelines
|
||||||
|
|
||||||
|
- Always include a row or column header with your table; this allows screen readers to better
|
||||||
|
contextualize the data
|
||||||
|
- Avoid spanning data across cells.
|
||||||
|
- Be sure to make repeating actions unique by associating them with the object they relate to.
|
||||||
|
Example: if there are multiple “Edit” buttons on a table, a screen reader should read “Edit,
|
||||||
|
Netflix” for an edit option for a Netflix item.
|
||||||
|
- Use [Virtual Scrolling](#virtual-scrolling) for large data sets.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
<Story id="component-library-table--default" />
|
<Story id="component-library-table--default" />
|
||||||
|
|
||||||
The below code is the absolute minimum required to create a table. However we strongly advise you to
|
### Usage
|
||||||
use the `dataSource` input to provide a data source for your table. This allows you to easily sort
|
|
||||||
data.
|
The below code is the minimum required to create a table. However we strongly advise you to use the
|
||||||
|
`dataSource` input to provide a data source for your table. This allows you to easily sort data.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<bit-table>
|
<bit-table>
|
||||||
|
@ -36,9 +60,8 @@ data.
|
||||||
## Data Source
|
## Data Source
|
||||||
|
|
||||||
Bitwarden provides a data source for tables that can be used in place of a traditional data array.
|
Bitwarden provides a data source for tables that can be used in place of a traditional data array.
|
||||||
The `TableDataSource` implements sorting and will in the future also support filtering. This allows
|
The `TableDataSource` implements sorting and filtering capabilities. This allows the `bitTable`
|
||||||
the `bitTable` component to focus on rendering while offloading the data management to the data
|
component to focus on rendering while offloading the data management to the data source.
|
||||||
source.
|
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// External data source
|
// External data source
|
||||||
|
@ -51,14 +74,31 @@ dataSource.data = data;
|
||||||
We use the `dataSource` as an input to the `bit-table` component, and access the rows to render
|
We use the `dataSource` as an input to the `bit-table` component, and access the rows to render
|
||||||
within the `ng-template`which provides access to the rows using `let-rows$`.
|
within the `ng-template`which provides access to the rows using `let-rows$`.
|
||||||
|
|
||||||
<Source id="component-library-table--data-source" />
|
```html
|
||||||
|
<bit-table [dataSource]="dataSource">
|
||||||
|
<ng-container header>
|
||||||
|
<tr>
|
||||||
|
<th bitCell bitSortable="id" default>Id</th>
|
||||||
|
<th bitCell bitSortable="name">Name</th>
|
||||||
|
<th bitCell bitSortable="other" [fn]="sortFn">Other</th>
|
||||||
|
</tr>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template body let-rows$>
|
||||||
|
<tr bitRow *ngFor="let r of rows$ | async">
|
||||||
|
<td bitCell>{{ r.id }}</td>
|
||||||
|
<td bitCell>{{ r.name }}</td>
|
||||||
|
<td bitCell>{{ r.other }}</td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
</bit-table>
|
||||||
|
```
|
||||||
|
|
||||||
### Sortable
|
### Sorting
|
||||||
|
|
||||||
We provide a simple component for displaying sortable column headers. The `bitSortable` component
|
We provide a simple component for displaying sortable column headers. The `bitSortable` component
|
||||||
wires up to the `TableDataSource` and will automatically sort the data when clicked and display
|
wires up to the `TableDataSource` and will automatically sort the data when clicked and display an
|
||||||
an indicator for which column is currently sorted. The dafault sorting can be specified by setting
|
indicator for which column is currently sorted. The dafault sorting can be specified by setting the
|
||||||
the `default`.
|
`default`.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<th bitCell bitSortable="id" default>Id</th>
|
<th bitCell bitSortable="id" default>Id</th>
|
||||||
|
@ -71,6 +111,16 @@ It's also possible to define a custom sorting function by setting the `fn` input
|
||||||
const sortFn = (a: T, b: T) => (a.id > b.id ? 1 : -1);
|
const sortFn = (a: T, b: T) => (a.id > b.id ? 1 : -1);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Filtering
|
||||||
|
|
||||||
|
The `TableDataSource` supports a rudimentary filtering capability most commonly used to implement a
|
||||||
|
search function. It works by converting each entry into a string of it's properties. The string is
|
||||||
|
then compared against the filter value using a simple `indexOf`check.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
dataSource.filter = "search value";
|
||||||
|
```
|
||||||
|
|
||||||
### Virtual Scrolling
|
### Virtual Scrolling
|
||||||
|
|
||||||
It's heavily adviced to use virtual scrolling if you expect the table to have any significant amount
|
It's heavily adviced to use virtual scrolling if you expect the table to have any significant amount
|
||||||
|
@ -97,8 +147,3 @@ specify a `itemSize`, set `scrollWindow` to `true` and replace `*ngFor` with `*c
|
||||||
</bit-table>
|
</bit-table>
|
||||||
</cdk-virtual-scroll-viewport>
|
</cdk-virtual-scroll-viewport>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Accessibility
|
|
||||||
|
|
||||||
- Always incude a row or column header with your table; this allows screen readers to better contextualize the data
|
|
||||||
- Avoid spanning data across cells
|
|
||||||
|
|
Loading…
Reference in New Issue