mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-21 11:35:34 +01:00
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
|
||||
|
||||
These directives should be used when building forms with buttons that trigger long running tasks in 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).
|
||||
These directives should be used when building forms with buttons that trigger long running tasks in
|
||||
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
|
||||
|
||||
@ -15,17 +17,19 @@ Adding async actions to submit buttons requires the following 3 steps
|
||||
|
||||
### 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
|
||||
useful because `return;` can be used to abort an action.
|
||||
A handler is a function that returns a promise or an observable. Functions that return `void` are
|
||||
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
|
||||
component using the variable `this`.
|
||||
**NOTE:** Defining the handlers as arrow-functions assigned to variables is mandatory if the handler
|
||||
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
|
||||
invalid. This is due to the form getting disabled by the `bitSubmit` directive while waiting for the async action to complete.
|
||||
**NOTE:** `formGroup.invalid` will always return `true` after the first `await` operation, event if
|
||||
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
|
||||
users attempting to trigger new actions before the previous ones have finished.
|
||||
**NOTE:** Handlers do not need to check if any previous requests have finished because the
|
||||
directives have built in protection against users attempting to trigger new actions before the
|
||||
previous ones have finished.
|
||||
|
||||
```ts
|
||||
@Component({...})
|
||||
@ -51,8 +55,8 @@ class Component {
|
||||
|
||||
Add the `bitSubmit` directive and supply the handler defined in step 1.
|
||||
|
||||
**NOTE:** The `directive` is defined using the input syntax: `[input]="handler"`.
|
||||
This is different from how submit handlers are usually defined with the output syntax `(ngSubmit)="handler()"`.
|
||||
**NOTE:** The `directive` is defined using the input syntax: `[input]="handler"`. This is different
|
||||
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.
|
||||
|
||||
@ -76,14 +80,15 @@ Adding async actions to standalone form buttons requires the following 3 steps.
|
||||
|
||||
### 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
|
||||
useful for aborting an action.
|
||||
A handler is a function that returns a promise or an observable. Functions that return `void` are
|
||||
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
|
||||
component using the variable `this`.
|
||||
**NOTE:** Defining the handlers as arrow-functions assigned to variables is mandatory if the handler
|
||||
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
|
||||
users attempting to trigger new actions before the previous ones have finished.
|
||||
**NOTE:** Handlers do not need to check if any previous requests have finished because the
|
||||
directives have built in protection against users attempting to trigger new actions before the
|
||||
previous ones have finished.
|
||||
|
||||
```ts
|
||||
@Component({...})
|
||||
@ -113,7 +118,8 @@ The `bitSubmit` directive is required because of its coordinating role inside of
|
||||
|
||||
### 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.
|
||||
|
||||
@ -124,7 +130,8 @@ Add `bitButton`, `bitFormButton`, `bitAction` directives to the button. Make sur
|
||||
|
||||
## `[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
|
||||
<form [formGroup]="formGroup" [bitSubmit]="submit" [allowDisabledFormSubmit]="true">...</form>
|
||||
|
@ -4,14 +4,14 @@ import { Meta } from "@storybook/addon-docs";
|
||||
|
||||
# Async Actions
|
||||
|
||||
The directives in this module makes it easier for developers to reflect the progress of async actions in the UI when using
|
||||
buttons, while also providing robust and standardized error handling.
|
||||
The directives in this module makes it easier for developers to reflect the progress of async
|
||||
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
|
||||
that are part of a form (such as Delete buttons).
|
||||
These buttons can either be standalone (such as Refresh buttons), submit buttons for forms or as
|
||||
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
|
||||
clutter inside our view `components`.
|
||||
These directives are meant to replace the older `appApiAction` directive, providing the option to
|
||||
use `observables` and reduce clutter inside our view `components`.
|
||||
|
||||
## When to use?
|
||||
|
||||
|
@ -4,8 +4,9 @@ import { Meta } from "@storybook/addon-docs";
|
||||
|
||||
# Standalone Async Actions
|
||||
|
||||
These directives should be used when building a standalone button that triggers a long running task 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).
|
||||
These directives should be used when building a standalone button that triggers a long running task
|
||||
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
|
||||
|
||||
@ -13,14 +14,15 @@ Adding async actions to standalone buttons requires the following 2 steps
|
||||
|
||||
### 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
|
||||
useful because `return;` can be used to abort an action.
|
||||
A handler is a function that returns a promise or an observable. Functions that return `void` are
|
||||
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
|
||||
component using the variable `this`.
|
||||
**NOTE:** Defining the handlers as arrow-functions assigned to variables is mandatory if the handler
|
||||
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
|
||||
users attempting to trigger new actions before the previous ones have finished.
|
||||
**NOTE:** Handlers do not need to check if any previous requests have finished because the
|
||||
directives have built in protection against users attempting to trigger new actions before the
|
||||
previous ones have finished.
|
||||
|
||||
#### Example using promises
|
||||
|
||||
@ -48,8 +50,8 @@ class Component {
|
||||
|
||||
Add the `bitAction` directive and supply the handler defined in step 1.
|
||||
|
||||
**NOTE:** The `directive` is defined using the input syntax: `[input]="handler"`.
|
||||
This is different from how click handlers are usually defined with the output syntax `(click)="handler()"`.
|
||||
**NOTE:** The `directive` is defined using the input syntax: `[input]="handler"`. This is different
|
||||
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.
|
||||
|
||||
|
@ -95,18 +95,12 @@ const Template: Story<BitFormFieldComponent> = (args: BitFormFieldComponent) =>
|
||||
...args,
|
||||
},
|
||||
template: `
|
||||
<form [formGroup]="formObj" (ngSubmit)="submit()">
|
||||
<form [formGroup]="formObj">
|
||||
<bit-form-field>
|
||||
<bit-label>Name</bit-label>
|
||||
<bit-label>Label</bit-label>
|
||||
<input bitInput formControlName="name" />
|
||||
<bit-hint>Optional Hint</bit-hint>
|
||||
</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>
|
||||
`,
|
||||
});
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { FormsModule, ReactiveFormsModule, FormBuilder } from "@angular/forms";
|
||||
import { FormsModule, ReactiveFormsModule, FormControl, FormGroup } from "@angular/forms";
|
||||
import { Meta, moduleMetadata, Story } from "@storybook/angular";
|
||||
|
||||
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 { RadioButtonModule } from "./radio-button.module";
|
||||
|
||||
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) {}
|
||||
}
|
||||
import { RadioGroupComponent } from "./radio-group.component";
|
||||
|
||||
export default {
|
||||
title: "Component Library/Form/Radio Button",
|
||||
component: ExampleComponent,
|
||||
component: RadioGroupComponent,
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
declarations: [ExampleComponent],
|
||||
imports: [FormsModule, ReactiveFormsModule, RadioButtonModule],
|
||||
providers: [
|
||||
{
|
||||
@ -92,56 +34,65 @@ export default {
|
||||
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;
|
||||
|
||||
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) => ({
|
||||
props: args,
|
||||
template: storyTemplate,
|
||||
<bit-radio-button id="radio-first" [value]="0">
|
||||
<bit-label>First</bit-label>
|
||||
</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({});
|
||||
Inline.args = {
|
||||
layout: "inline",
|
||||
};
|
||||
|
||||
const BlockTemplate: Story<ExampleComponent> = (args: ExampleComponent) => ({
|
||||
props: args,
|
||||
template: storyTemplate,
|
||||
const BlockTemplate: 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>
|
||||
|
||||
<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({});
|
||||
Block.args = {
|
||||
layout: "block",
|
||||
};
|
||||
|
@ -81,13 +81,13 @@ import { Meta } from "@storybook/addon-docs";
|
||||
|
||||
# Bitwarden Component Library
|
||||
|
||||
The Bitwarden Component Library is a collection of reusable low level components which empowers designers and
|
||||
developers to work more efficiently. The primary goal is to ensure a consistent design and behavior across the
|
||||
different clients and platforms. Currently the primary focus is the web based clients, namely _web_, _browser_ and
|
||||
_desktop_.
|
||||
The Bitwarden Component Library is a collection of reusable low level components which empowers
|
||||
designers and developers to work more efficiently. The primary goal is to ensure a consistent design
|
||||
and behavior across the different clients and platforms. Currently the primary focus is the web
|
||||
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
|
||||
and the other clients will follow once this work is completed.
|
||||
**Roll out status:** we are currently in the process of transitioning the Web Vault to utilize the
|
||||
component library and the other clients will follow once this work is completed.
|
||||
|
||||
<div className="subheading">Configure</div>
|
||||
|
||||
|
@ -4,20 +4,21 @@ import { Meta, Story } from "@storybook/addon-docs";
|
||||
|
||||
# Banner
|
||||
|
||||
Banners are used for important communication with the user that needs to be seen right away, but has little effect on
|
||||
the experience. Banners appear at the top of the user's screen on page load and persist across all pages a user
|
||||
navigates to.
|
||||
Banners are used for important communication with the user that needs to be seen right away, but has
|
||||
little effect on the experience. Banners appear at the top of the user's screen on page load and
|
||||
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
|
||||
during that same active session.
|
||||
- Use banners sparingly, as they can feel intrusive to the user if they appear unexpectedly. Their effectiveness may
|
||||
decrease if too many are used.
|
||||
- They should always be dismissable and never use a timeout. If a user dismisses a banner, it should
|
||||
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 decrease if too many are used.
|
||||
- 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
|
||||
|
||||
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.
|
||||
|
||||
@ -47,5 +48,6 @@ Rarely used, but may be used to alert users over critical messages or very outda
|
||||
|
||||
## Accessibility
|
||||
|
||||
Dialogs sets the `role="status"` and `aria-live="polite"` attributes to ensure screen readers announce the content
|
||||
prior to the test of the page. This behaviour can be disabled by setting `[useAlertRole]="false"`.
|
||||
Banners sets the `role="status"` and `aria-live="polite"` attributes to ensure screen readers
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
## Choosing the `<a>` or `<button>`
|
||||
|
||||
Buttons can use either the `<a>` or `<button>` tags. Choose which based on the action the button takes:
|
||||
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 taking an action on the current page 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" />
|
||||
|
||||
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
|
||||
|
||||
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" />
|
||||
|
||||
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
|
||||
|
||||
<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
|
||||
|
||||
@ -52,22 +79,14 @@ Use the danger styling only in settings when the user may preform a permanent ac
|
||||
|
||||
## 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" />
|
||||
|
||||
## 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" />
|
||||
|
||||
## 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
|
||||
|
||||
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">
|
||||
<Table />
|
||||
<Table class="theme_dark tw-bg-background" />
|
||||
|
51
libs/components/src/stories/forms-docs.stories.mdx
Normal file
51
libs/components/src/stories/forms-docs.stories.mdx
Normal file
@ -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
|
||||
|
||||
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
|
||||
|
||||
|
@ -4,9 +4,9 @@ import { Meta } from "@storybook/addon-docs";
|
||||
|
||||
# `bitInput`
|
||||
|
||||
`bitInput` is an Angular directive to be used on `<input>`, `<select>`, and `<textarea>`
|
||||
tags in order to provide standardized TailwindCss styling, error handling, and more.
|
||||
It is meant to be used within a `<bit-form-field>` custom component.
|
||||
`bitInput` is an Angular directive to be used on `<input>`, `<select>`, and `<textarea>` tags in
|
||||
order to provide standardized TailwindCss styling, error handling, and more. It is meant to be used
|
||||
within a `<bit-form-field>` custom component.
|
||||
|
||||
## 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
|
||||
|
||||
If you would like to be able to still show errors when an input is disabled for
|
||||
specific validation scenarios, then set `[showErrorsWhenDisabled]="true"`
|
||||
If you would like to be able to still show errors when an input is disabled for specific validation
|
||||
scenarios, then set `[showErrorsWhenDisabled]="true"`
|
||||
|
||||
```html
|
||||
<bit-form-field>
|
||||
@ -31,7 +31,8 @@ specific validation scenarios, then set `[showErrorsWhenDisabled]="true"`
|
||||
</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
|
||||
get exampleFormCtrl(): FormControl {
|
||||
|
@ -6,13 +6,37 @@ import { Meta, Story, Source } from "@storybook/addon-docs";
|
||||
|
||||
## 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" />
|
||||
|
||||
The below code is the absolute 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.
|
||||
### Usage
|
||||
|
||||
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
|
||||
<bit-table>
|
||||
@ -36,9 +60,8 @@ data.
|
||||
## Data Source
|
||||
|
||||
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 `bitTable` component to focus on rendering while offloading the data management to the data
|
||||
source.
|
||||
The `TableDataSource` implements sorting and filtering capabilities. This allows the `bitTable`
|
||||
component to focus on rendering while offloading the data management to the data source.
|
||||
|
||||
```ts
|
||||
// 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
|
||||
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
|
||||
wires up to the `TableDataSource` and will automatically sort the data when clicked and display
|
||||
an indicator for which column is currently sorted. The dafault sorting can be specified by setting
|
||||
the `default`.
|
||||
wires up to the `TableDataSource` and will automatically sort the data when clicked and display an
|
||||
indicator for which column is currently sorted. The dafault sorting can be specified by setting the
|
||||
`default`.
|
||||
|
||||
```html
|
||||
<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);
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
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>
|
||||
</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
Block a user