mirror of
https://github.com/bitwarden/browser
synced 2026-02-12 22:44:11 +00:00
[PM-11131] Prevent duplicated sr labels on form field icon buttons (#11383)
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
} from "@angular/forms";
|
||||
import { Meta, StoryObj, moduleMetadata } from "@storybook/angular";
|
||||
|
||||
import { A11yTitleDirective } from "@bitwarden/angular/src/directives/a11y-title.directive";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
|
||||
import { AsyncActionsModule } from "../async-actions";
|
||||
@@ -50,6 +51,7 @@ export default {
|
||||
TextFieldModule,
|
||||
BadgeModule,
|
||||
],
|
||||
declarations: [A11yTitleDirective],
|
||||
providers: [
|
||||
{
|
||||
provide: I18nService,
|
||||
@@ -237,7 +239,7 @@ export const Readonly: Story = {
|
||||
<bit-label>Input</bit-label>
|
||||
<input bitInput type="password" value="Foobar" [readonly]="true" />
|
||||
<button type="button" bitIconButton bitSuffix bitPasswordInputToggle></button>
|
||||
<button type="button" bitSuffix bitIconButton="bwi-clone" aria-label="Clone"></button>
|
||||
<button type="button" bitSuffix bitIconButton="bwi-clone" [appA11yTitle]="'Clone Input'"></button>
|
||||
</bit-form-field>
|
||||
|
||||
<bit-form-field>
|
||||
@@ -258,7 +260,7 @@ export const Readonly: Story = {
|
||||
<bit-label>Input</bit-label>
|
||||
<input bitInput type="password" value="Foobar" readonly />
|
||||
<button type="button" bitIconButton bitSuffix bitPasswordInputToggle></button>
|
||||
<button type="button" bitSuffix bitIconButton="bwi-clone" aria-label="Clone"></button>
|
||||
<button type="button" bitSuffix bitIconButton="bwi-clone" [appA11yTitle]="'Clone Input'"></button>
|
||||
</bit-form-field>
|
||||
|
||||
<bit-form-field>
|
||||
@@ -302,14 +304,14 @@ export const ButtonInputGroup: Story = {
|
||||
<bit-form-field>
|
||||
<bit-label>
|
||||
Label
|
||||
<a href="#" slot="end" bitLink>
|
||||
<a href="#" slot="end" bitLink [appA11yTitle]="'More info'">
|
||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||
</a>
|
||||
</bit-label>
|
||||
<button bitPrefix bitIconButton="bwi-star" aria-label="Favorite"></button>
|
||||
<button bitPrefix bitIconButton="bwi-star" [appA11yTitle]="'Favorite Label'"></button>
|
||||
<input bitInput placeholder="Placeholder" />
|
||||
<button bitSuffix bitIconButton="bwi-eye" aria-label="Hide"></button>
|
||||
<button bitSuffix bitIconButton="bwi-clone" aria-label="Clone"></button>
|
||||
<button bitSuffix bitIconButton="bwi-eye" [appA11yTitle]="'Hide Label'"></button>
|
||||
<button bitSuffix bitIconButton="bwi-clone" [appA11yTitle]="'Clone Label'"></button>
|
||||
<button bitSuffix bitLink>
|
||||
Apply
|
||||
</button>
|
||||
@@ -325,10 +327,10 @@ export const DisabledButtonInputGroup: Story = {
|
||||
template: /*html*/ `
|
||||
<bit-form-field>
|
||||
<bit-label>Label</bit-label>
|
||||
<button bitPrefix bitIconButton="bwi-star" disabled aria-label="Favorite"></button>
|
||||
<button bitPrefix bitIconButton="bwi-star" disabled [appA11yTitle]="'Favorite Label'"></button>
|
||||
<input bitInput placeholder="Placeholder" disabled />
|
||||
<button bitSuffix bitIconButton="bwi-eye" disabled aria-label="Hide"></button>
|
||||
<button bitSuffix bitIconButton="bwi-clone" disabled aria-label="Clone"></button>
|
||||
<button bitSuffix bitIconButton="bwi-eye" disabled [appA11yTitle]="'Hide Label'"></button>
|
||||
<button bitSuffix bitIconButton="bwi-clone" disabled [appA11yTitle]="'Clone Label'"></button>
|
||||
<button bitSuffix bitLink disabled>
|
||||
Apply
|
||||
</button>
|
||||
@@ -345,8 +347,8 @@ export const PartiallyDisabledButtonInputGroup: Story = {
|
||||
<bit-form-field>
|
||||
<bit-label>Label</bit-label>
|
||||
<input bitInput placeholder="Placeholder" disabled />
|
||||
<button bitSuffix bitIconButton="bwi-eye" aria-label="Hide"></button>
|
||||
<button bitSuffix bitIconButton="bwi-clone" aria-label="Clone"></button>
|
||||
<button bitSuffix bitIconButton="bwi-eye" [appA11yTitle]="'Hide Label'"></button>
|
||||
<button bitSuffix bitIconButton="bwi-clone" [appA11yTitle]="'Clone Label'"></button>
|
||||
<button bitSuffix bitLink disabled>
|
||||
Apply
|
||||
</button>
|
||||
|
||||
@@ -1,34 +1,20 @@
|
||||
import { AfterContentInit, Directive, HostBinding, Input, OnInit, Optional } from "@angular/core";
|
||||
import { Directive, HostBinding, Input, OnInit, Optional } from "@angular/core";
|
||||
|
||||
import { BitIconButtonComponent } from "../icon-button/icon-button.component";
|
||||
|
||||
import { BitFormFieldComponent } from "./form-field.component";
|
||||
|
||||
@Directive({
|
||||
selector: "[bitPrefix]",
|
||||
})
|
||||
export class BitPrefixDirective implements OnInit, AfterContentInit {
|
||||
export class BitPrefixDirective implements OnInit {
|
||||
@HostBinding("class") @Input() get classList() {
|
||||
return ["tw-text-muted"];
|
||||
}
|
||||
|
||||
@HostBinding("attr.aria-describedby")
|
||||
protected ariaDescribedBy: string;
|
||||
|
||||
constructor(
|
||||
@Optional() private parentFormField: BitFormFieldComponent,
|
||||
@Optional() private iconButtonComponent: BitIconButtonComponent,
|
||||
) {}
|
||||
constructor(@Optional() private iconButtonComponent: BitIconButtonComponent) {}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.iconButtonComponent) {
|
||||
this.iconButtonComponent.size = "small";
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
if (this.parentFormField?.label?.id) {
|
||||
this.ariaDescribedBy = this.parentFormField.label.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,20 @@
|
||||
import { AfterContentInit, Directive, HostBinding, Input, OnInit, Optional } from "@angular/core";
|
||||
import { Directive, HostBinding, Input, OnInit, Optional } from "@angular/core";
|
||||
|
||||
import { BitIconButtonComponent } from "../icon-button/icon-button.component";
|
||||
|
||||
import { BitFormFieldComponent } from "./form-field.component";
|
||||
|
||||
@Directive({
|
||||
selector: "[bitSuffix]",
|
||||
})
|
||||
export class BitSuffixDirective implements OnInit, AfterContentInit {
|
||||
export class BitSuffixDirective implements OnInit {
|
||||
@HostBinding("class") @Input() get classList() {
|
||||
return ["tw-text-muted"];
|
||||
}
|
||||
|
||||
@HostBinding("attr.aria-describedby")
|
||||
protected ariaDescribedBy: string;
|
||||
|
||||
constructor(
|
||||
@Optional() private parentFormField: BitFormFieldComponent,
|
||||
@Optional() private iconButtonComponent: BitIconButtonComponent,
|
||||
) {}
|
||||
constructor(@Optional() private iconButtonComponent: BitIconButtonComponent) {}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.iconButtonComponent) {
|
||||
this.iconButtonComponent.size = "small";
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
if (this.parentFormField?.label?.id) {
|
||||
this.ariaDescribedBy = this.parentFormField.label.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +152,12 @@ If a checkbox group has more than 4 options a
|
||||
helper text.
|
||||
- **Example:** "Billing Email is required if owned by a business".
|
||||
|
||||
### Icon Buttons in Form Fields
|
||||
|
||||
When adding prefix or suffix icon buttons to a form field, be sure to use the `appA11yTitle`
|
||||
directive to provide a label for screenreaders. Typically, the label should follow this pattern:
|
||||
`{Action} {field label}`, i.e. "Copy username".
|
||||
|
||||
### Form Field Errors
|
||||
|
||||
- When a resting field is filled out, validation is triggered when the user de-focuses the field
|
||||
|
||||
Reference in New Issue
Block a user