mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 07:43:35 +00:00
[PS-1301] Accessibility: improve item edit for URIs and custom fields (#3305)
* Add `role="group"` and accName to URI and custom field groups (browser) Provides more context when editing fields (to understand what the remove, options, etc buttons are all about) * Add `aria-describedby` to custom field value fields (browser) provides further context other than the generic "Value" label * Add `role="group"` and accName to URI and custom field groups, add `aria-describedby` to custom field value fields (desktop) * Add `role="group"` and accName to URI and custom field groups, add `aria-describedby` to custom field value fields (web) * Use `attr.aria-label` instead of `appA11yTitle` don't need/want the tooltips appearing everywhere
This commit is contained in:
@@ -6,11 +6,13 @@
|
|||||||
<!-- Current custom fields -->
|
<!-- Current custom fields -->
|
||||||
<div cdkDropList (cdkDropListDropped)="drop($event)" *ngIf="cipher.hasFields">
|
<div cdkDropList (cdkDropListDropped)="drop($event)" *ngIf="cipher.hasFields">
|
||||||
<div
|
<div
|
||||||
|
role="group"
|
||||||
class="box-content-row box-content-row-multi box-draggable-row"
|
class="box-content-row box-content-row-multi box-draggable-row"
|
||||||
appBoxRow
|
appBoxRow
|
||||||
cdkDrag
|
cdkDrag
|
||||||
*ngFor="let f of cipher.fields; let i = index; trackBy: trackByFunction"
|
*ngFor="let f of cipher.fields; let i = index; trackBy: trackByFunction"
|
||||||
[ngClass]="{ 'box-content-row-checkbox': f.type === fieldType.Boolean }"
|
[ngClass]="{ 'box-content-row-checkbox': f.type === fieldType.Boolean }"
|
||||||
|
attr.aria-label="{{ f.name }}"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -41,6 +43,7 @@
|
|||||||
*ngIf="f.type === fieldType.Text"
|
*ngIf="f.type === fieldType.Text"
|
||||||
placeholder="{{ 'value' | i18n }}"
|
placeholder="{{ 'value' | i18n }}"
|
||||||
appInputVerbatim
|
appInputVerbatim
|
||||||
|
attr.aria-describedby="fieldName{{ i }}"
|
||||||
/>
|
/>
|
||||||
<!-- Hidden -->
|
<!-- Hidden -->
|
||||||
<input
|
<input
|
||||||
@@ -53,6 +56,7 @@
|
|||||||
*ngIf="f.type === fieldType.Hidden"
|
*ngIf="f.type === fieldType.Hidden"
|
||||||
placeholder="{{ 'value' | i18n }}"
|
placeholder="{{ 'value' | i18n }}"
|
||||||
[disabled]="!cipher.viewPassword && !f.newField"
|
[disabled]="!cipher.viewPassword && !f.newField"
|
||||||
|
attr.aria-describedby="fieldName{{ i }}"
|
||||||
/>
|
/>
|
||||||
<!-- Linked -->
|
<!-- Linked -->
|
||||||
<select
|
<select
|
||||||
@@ -60,6 +64,7 @@
|
|||||||
name="Field.Value{{ i }}"
|
name="Field.Value{{ i }}"
|
||||||
[(ngModel)]="f.linkedId"
|
[(ngModel)]="f.linkedId"
|
||||||
*ngIf="f.type === fieldType.Linked && cipher.linkedFieldOptions != null"
|
*ngIf="f.type === fieldType.Linked && cipher.linkedFieldOptions != null"
|
||||||
|
attr.aria-describedby="fieldName{{ i }}"
|
||||||
>
|
>
|
||||||
<option *ngFor="let o of linkedFieldOptions" [ngValue]="o.value">{{ o.name }}</option>
|
<option *ngFor="let o of linkedFieldOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -74,6 +79,7 @@
|
|||||||
appTrueFalseValue
|
appTrueFalseValue
|
||||||
trueValue="true"
|
trueValue="true"
|
||||||
falseValue="false"
|
falseValue="false"
|
||||||
|
attr.aria-describedby="fieldName{{ i }}"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="action-buttons"
|
class="action-buttons"
|
||||||
|
|||||||
@@ -402,9 +402,11 @@
|
|||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<ng-container *ngIf="cipher.login.hasUris">
|
<ng-container *ngIf="cipher.login.hasUris">
|
||||||
<div
|
<div
|
||||||
|
role="group"
|
||||||
class="box-content-row box-content-row-multi"
|
class="box-content-row box-content-row-multi"
|
||||||
appBoxRow
|
appBoxRow
|
||||||
*ngFor="let u of cipher.login.uris; let i = index; trackBy: trackByFunction"
|
*ngFor="let u of cipher.login.uris; let i = index; trackBy: trackByFunction"
|
||||||
|
attr.aria-label="{{ 'uriPosition' | i18n: i + 1 }}"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -5,10 +5,12 @@
|
|||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div cdkDropList (cdkDropListDropped)="drop($event)" *ngIf="cipher.hasFields">
|
<div cdkDropList (cdkDropListDropped)="drop($event)" *ngIf="cipher.hasFields">
|
||||||
<div
|
<div
|
||||||
|
role="group"
|
||||||
class="box-content-row box-content-row-multi box-draggable-row"
|
class="box-content-row box-content-row-multi box-draggable-row"
|
||||||
cdkDrag
|
cdkDrag
|
||||||
*ngFor="let f of cipher.fields; let i = index; trackBy: trackByFunction"
|
*ngFor="let f of cipher.fields; let i = index; trackBy: trackByFunction"
|
||||||
[ngClass]="{ 'box-content-row-checkbox': f.type === fieldType.Boolean }"
|
[ngClass]="{ 'box-content-row-checkbox': f.type === fieldType.Boolean }"
|
||||||
|
attr.aria-label="{{ f.name }}"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -39,6 +41,7 @@
|
|||||||
*ngIf="f.type === fieldType.Text"
|
*ngIf="f.type === fieldType.Text"
|
||||||
placeholder="{{ 'value' | i18n }}"
|
placeholder="{{ 'value' | i18n }}"
|
||||||
appInputVerbatim
|
appInputVerbatim
|
||||||
|
attr.aria-describedby="fieldName{{ i }}"
|
||||||
/>
|
/>
|
||||||
<!-- Password -->
|
<!-- Password -->
|
||||||
<input
|
<input
|
||||||
@@ -51,6 +54,7 @@
|
|||||||
placeholder="{{ 'value' | i18n }}"
|
placeholder="{{ 'value' | i18n }}"
|
||||||
[disabled]="!cipher.viewPassword && !f.newField"
|
[disabled]="!cipher.viewPassword && !f.newField"
|
||||||
appInputVerbatim
|
appInputVerbatim
|
||||||
|
attr.aria-describedby="fieldName{{ i }}"
|
||||||
/>
|
/>
|
||||||
<!-- Linked -->
|
<!-- Linked -->
|
||||||
<select
|
<select
|
||||||
@@ -58,6 +62,7 @@
|
|||||||
name="Field.Value{{ i }}"
|
name="Field.Value{{ i }}"
|
||||||
[(ngModel)]="f.linkedId"
|
[(ngModel)]="f.linkedId"
|
||||||
*ngIf="f.type === fieldType.Linked && cipher.linkedFieldOptions != null"
|
*ngIf="f.type === fieldType.Linked && cipher.linkedFieldOptions != null"
|
||||||
|
attr.aria-describedby="fieldName{{ i }}"
|
||||||
>
|
>
|
||||||
<option *ngFor="let o of linkedFieldOptions" [ngValue]="o.value">{{ o.name }}</option>
|
<option *ngFor="let o of linkedFieldOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -72,6 +77,7 @@
|
|||||||
appTrueFalseValue
|
appTrueFalseValue
|
||||||
trueValue="true"
|
trueValue="true"
|
||||||
falseValue="false"
|
falseValue="false"
|
||||||
|
attr.aria-describedby="fieldName{{ i }}"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="action-buttons"
|
class="action-buttons"
|
||||||
@@ -84,6 +90,7 @@
|
|||||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
[attr.aria-pressed]="f.showValue"
|
[attr.aria-pressed]="f.showValue"
|
||||||
(click)="toggleFieldValue(f)"
|
(click)="toggleFieldValue(f)"
|
||||||
|
attr.aria-describedby="fieldName{{ i }}"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="bwi bwi-lg"
|
class="bwi bwi-lg"
|
||||||
|
|||||||
@@ -391,9 +391,11 @@
|
|||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<ng-container *ngIf="cipher.login.hasUris">
|
<ng-container *ngIf="cipher.login.hasUris">
|
||||||
<div
|
<div
|
||||||
|
role="group"
|
||||||
class="box-content-row box-content-row-multi"
|
class="box-content-row box-content-row-multi"
|
||||||
appBoxRow
|
appBoxRow
|
||||||
*ngFor="let u of cipher.login.uris; let i = index; trackBy: trackByFunction"
|
*ngFor="let u of cipher.login.uris; let i = index; trackBy: trackByFunction"
|
||||||
|
attr.aria-label="{{ 'uriPosition' | i18n: i + 1 }}"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
<h3 class="mt-4">{{ "customFields" | i18n }}</h3>
|
<h3 class="mt-4">{{ "customFields" | i18n }}</h3>
|
||||||
<div cdkDropList (cdkDropListDropped)="drop($event)" *ngIf="cipher.hasFields">
|
<div cdkDropList (cdkDropListDropped)="drop($event)" *ngIf="cipher.hasFields">
|
||||||
<div
|
<div
|
||||||
|
role="group"
|
||||||
class="row"
|
class="row"
|
||||||
cdkDrag
|
cdkDrag
|
||||||
*ngFor="let f of cipher.fields; let i = index; trackBy: trackByFunction"
|
*ngFor="let f of cipher.fields; let i = index; trackBy: trackByFunction"
|
||||||
|
attr.aria-label="{{ f.name }}"
|
||||||
>
|
>
|
||||||
<div class="col-5 form-group">
|
<div class="col-5 form-group">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
@@ -42,6 +44,7 @@
|
|||||||
[(ngModel)]="f.value"
|
[(ngModel)]="f.value"
|
||||||
appInputVerbatim
|
appInputVerbatim
|
||||||
[disabled]="cipher.isDeleted || viewOnly"
|
[disabled]="cipher.isDeleted || viewOnly"
|
||||||
|
attr.aria-describedby="fieldName{{ i }}"
|
||||||
/>
|
/>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button
|
<button
|
||||||
@@ -65,6 +68,7 @@
|
|||||||
appInputVerbatim
|
appInputVerbatim
|
||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
[disabled]="cipher.isDeleted || viewOnly || (!cipher.viewPassword && !f.newField)"
|
[disabled]="cipher.isDeleted || viewOnly || (!cipher.viewPassword && !f.newField)"
|
||||||
|
attr.aria-describedby="fieldName{{ i }}"
|
||||||
/>
|
/>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button
|
<button
|
||||||
@@ -101,6 +105,7 @@
|
|||||||
[(ngModel)]="f.linkedId"
|
[(ngModel)]="f.linkedId"
|
||||||
*ngIf="f.type === fieldType.Linked && cipher.linkedFieldOptions != null"
|
*ngIf="f.type === fieldType.Linked && cipher.linkedFieldOptions != null"
|
||||||
[disabled]="cipher.isDeleted || viewOnly"
|
[disabled]="cipher.isDeleted || viewOnly"
|
||||||
|
attr.aria-describedby="fieldName{{ i }}"
|
||||||
>
|
>
|
||||||
<option *ngFor="let o of linkedFieldOptions" [ngValue]="o.value">{{ o.name }}</option>
|
<option *ngFor="let o of linkedFieldOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -117,6 +122,7 @@
|
|||||||
trueValue="true"
|
trueValue="true"
|
||||||
falseValue="false"
|
falseValue="false"
|
||||||
[disabled]="cipher.isDeleted || viewOnly"
|
[disabled]="cipher.isDeleted || viewOnly"
|
||||||
|
attr.aria-describedby="fieldName{{ i }}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -273,8 +273,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<ng-container *ngIf="cipher.login.hasUris">
|
<ng-container *ngIf="cipher.login.hasUris">
|
||||||
<div
|
<div
|
||||||
|
role="group"
|
||||||
class="row"
|
class="row"
|
||||||
*ngFor="let u of cipher.login.uris; let i = index; trackBy: trackByFunction"
|
*ngFor="let u of cipher.login.uris; let i = index; trackBy: trackByFunction"
|
||||||
|
attr.aria-label="{{ 'uriPosition' | i18n: i + 1 }}"
|
||||||
>
|
>
|
||||||
<div class="col-7 form-group">
|
<div class="col-7 form-group">
|
||||||
<label for="loginUri{{ i }}">{{ "uriPosition" | i18n: i + 1 }}</label>
|
<label for="loginUri{{ i }}">{{ "uriPosition" | i18n: i + 1 }}</label>
|
||||||
|
|||||||
Reference in New Issue
Block a user