Skip to content

Commit

Permalink
feat(a11y): various improvements for accessibility
Browse files Browse the repository at this point in the history
* Add `for` property on labels
* Add hidden text and labels on icon buttons
* Add role heading and proper level on active navigation items
  • Loading branch information
Jonas Metzener authored and anehx committed Mar 3, 2020
1 parent c0047a1 commit 3185fc8
Show file tree
Hide file tree
Showing 27 changed files with 154 additions and 21 deletions.
1 change: 1 addition & 0 deletions addon/components/cf-field/input/float.js
Expand Up @@ -15,6 +15,7 @@ export default Component.extend({
"step",
"disabled:readonly",
"field.pk:name",
"field.pk:id",
"field.answer.value:value",
"field.question.floatMinValue:min",
"field.question.floatMaxValue:max"
Expand Down
1 change: 1 addition & 0 deletions addon/components/cf-field/input/integer.js
Expand Up @@ -15,6 +15,7 @@ export default Component.extend({
"step",
"disabled:readonly",
"field.pk:name",
"field.pk:id",
"field.answer.value:value",
"field.question.integerMinValue:min",
"field.question.integerMaxValue:max"
Expand Down
1 change: 1 addition & 0 deletions addon/components/cf-field/input/text.js
Expand Up @@ -14,6 +14,7 @@ export default Component.extend({
"type",
"disabled:readonly",
"field.pk:name",
"field.pk:id",
"field.answer.value:value",
"field.question.placeholder:placeholder"
],
Expand Down
1 change: 1 addition & 0 deletions addon/components/cf-field/input/textarea.js
Expand Up @@ -13,6 +13,7 @@ export default Component.extend({
attributeBindings: [
"disabled:readonly",
"field.pk:name",
"field.pk:id",
"field.answer.value:value",
"field.question.textareaMaxLength:maxlength"
],
Expand Down
3 changes: 2 additions & 1 deletion addon/components/cf-field/label.js
Expand Up @@ -4,5 +4,6 @@ import layout from "../../templates/components/cf-field/label";
export default Component.extend({
layout,
tagName: "label",
classNames: ["uk-form-label"]
classNames: ["uk-form-label"],
attributeBindings: ["field.pk:for"]
});
5 changes: 4 additions & 1 deletion addon/components/cf-navigation-item.js
@@ -1,9 +1,12 @@
import Component from "@ember/component";
import { or } from "@ember/object/computed";
import layout from "../templates/components/cf-navigation-item";

export default Component.extend({
layout,
tagName: "li",
classNames: ["cf-navigation__item", "uk-width-auto"],
classNameBindings: ["item.active:uk-active"]
classNameBindings: ["active:uk-active"],

active: or("item.active", "item.childrenActive")
});
9 changes: 9 additions & 0 deletions addon/lib/navigation.js
Expand Up @@ -103,6 +103,15 @@ export const NavigationItem = Base.extend({
);
}),

/**
* Whether the item has active children
*
* @property {Boolean} childrenActive
*/
childrenActive: computed("children.@each.active", function() {
return this.children.some(child => child.active);
}),

/**
* The item is navigable if it is not hidden and its fieldset contains at
* least one question that is not a form question and is not hidden.
Expand Down
2 changes: 1 addition & 1 deletion addon/templates/components/cf-content.hbs
Expand Up @@ -3,7 +3,7 @@
{{else if document}}
{{#let (hash
document=document
navigation=(component "cf-navigation" navigation=navigation)
navigation=(component "cf-navigation" navigation=navigation useAsHeading=(or @useAsHeading false) headingBaseLevel=(or @headingBaseLevel 1))
pagination=(component "cf-pagination" navigation=navigation)
form=(component "cf-form-wrapper" document=document fieldset=fieldset context=context disabled=disabled)
) as |content|}}
Expand Down
5 changes: 4 additions & 1 deletion addon/templates/components/cf-field/info.hbs
Expand Up @@ -2,8 +2,11 @@
type="button"
class="uk-icon-button"
uk-icon="info"
title={{t "caluma.form.info"}}
{{action (mut showModal) true}}
></button>
>
<span class="uk-hidden">{{t "caluma.form.info"}}</span>
</button>

{{#uk-modal
visible=showModal
Expand Down
1 change: 1 addition & 0 deletions addon/templates/components/cf-field/input/date.hbs
@@ -1,6 +1,7 @@
{{pikaday-wrapper
class="uk-input"
name=field.pk
id=field.pk
format="L"
onSelection=(action "onchange")
value=(get field.answer field.answer._valueKey)
Expand Down
1 change: 1 addition & 0 deletions addon/templates/components/cf-field/input/file.hbs
Expand Up @@ -6,6 +6,7 @@
<input
type="file"
name={{field.pk}}
id={{field.pk}}
onchange={{action "save"}}>

<input
Expand Down
1 change: 1 addition & 0 deletions addon/templates/components/cf-field/input/powerselect.hbs
Expand Up @@ -6,6 +6,7 @@
preventScroll=true
searchEnabled=searchEnabled
searchField="label"
triggerId=field.pk

placeholder=placeholder
loadingMessage=(t "caluma.form.power-select.options-loading")
Expand Down
32 changes: 29 additions & 3 deletions addon/templates/components/cf-field/input/table.hbs
Expand Up @@ -23,9 +23,27 @@
{{/each}}
{{/if}}
<td class="uk-text-right">
<button type="button" data-test-edit-row class="uk-icon-button" uk-icon="pencil" {{action "editRow" document}}></button>
<button
data-test-edit-row
type="button"
class="uk-icon-button"
uk-icon="pencil"
title={{t "caluma.form.edit"}}
{{action "editRow" document}}
>
<span class="uk-hidden">{{t "caluma.form.edit"}}</span>
</button>
{{#unless disabled}}
<button type="button" data-test-delete-row class="uk-icon-button" uk-icon="trash" {{action "deleteRow" document}}></button>
<button
data-test-delete-row
type="button"
class="uk-icon-button"
uk-icon="trash"
title={{t "caluma.form.delete"}}
{{action "deleteRow" document}}
>
<span class="uk-hidden">{{t "caluma.form.delete"}}</span>
</button>
{{/unless}}
</td>
</tr>
Expand All @@ -35,7 +53,15 @@
{{#unless disabled}}
<tr>
<td colspan={{add (count-keys questions) 1}} class="uk-text-center">
{{#uk-button size="small" on-click=(perform addRow)}}{{uk-icon "plus"}}{{/uk-button}}
<button
type="button"
class="uk-button uk-button-default uk-button-small"
title={{t "caluma.form.addRow"}}
{{on "click" (perform addRow)}}
>
<UkIcon @icon="plus" />
<span class="uk-hidden">{{t "caluma.form.addRow"}}</span>
</button>
</td>
</tr>
{{/unless}}
Expand Down
2 changes: 1 addition & 1 deletion addon/templates/components/cf-field/label.hbs
Expand Up @@ -3,5 +3,5 @@
</span>

{{#if field.optional}}
<span class="uk-margin-small-left uk-text-muted uk-text-lowercase">({{t "caluma.form.optional"}})</span>
<span class="uk-margin-small-left uk-text-muted uk-text-lowercase uk-text-normal">({{t "caluma.form.optional"}})</span>
{{/if}}
27 changes: 20 additions & 7 deletions addon/templates/components/cf-navigation-item.hbs
@@ -1,13 +1,26 @@
{{#link-to (query-params displayedForm=item.slug) class="uk-width-1-1"}}
<span title={{item.label}} class="uk-width-expand uk-text-truncate">{{item.label}}</span>
<span class="cf-navigation__item__icon cf-navigation__item__icon--{{item.state}}"></span>
{{/link-to}}
<LinkTo @query={{hash displayedForm=@item.slug}}>
{{#if (and @useAsHeading this.active)}}
<span
role="heading"
aria-level={{@headingLevel}}
title={{@item.label}}
class="uk-width-expand uk-text-truncate"
>
{{@item.label}}
</span>
{{else}}
<span title={{@item.label}} class="uk-width-expand uk-text-truncate">
{{@item.label}}
</span>
{{/if}}
<span class="cf-navigation__item__icon cf-navigation__item__icon--{{@item.state}}"></span>
</LinkTo>

{{#if item.children}}
{{#if @item.children}}
<ul class="uk-tab uk-tab-left uk-margin-left uk-width-auto">
{{#each item.children as |childItem|}}
{{#each @item.children as |childItem|}}
{{#if childItem.visible}}
{{cf-navigation-item item=childItem}}
<CfNavigationItem @item={{childItem}} @headingLevel={{add @headingLevel 1}} />
{{/if}}
{{/each}}
</ul>
Expand Down
4 changes: 2 additions & 2 deletions addon/templates/components/cf-navigation.hbs
@@ -1,7 +1,7 @@
<ul class="uk-tab uk-tab-left">
{{#each (reject-by "parent" navigation.items) as |item|}}
{{#each (reject-by "parent" this.navigation.items) as |item|}}
{{#if item.visible}}
{{cf-navigation-item item=item}}
<CfNavigationItem @item={{item}} @useAsHeading={{@useAsHeading}} @headingLevel={{@headingBaseLevel}} />
{{#unless item.fieldset.field}}
<hr class="uk-divider-small uk-margin-small">
{{/unless}}
Expand Down
4 changes: 3 additions & 1 deletion tests/integration/components/cf-field/info-test.js
Expand Up @@ -2,13 +2,15 @@ import { module, test } from "qunit";
import { setupRenderingTest } from "ember-qunit";
import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { setupIntl } from "ember-intl/test-support";

module("Integration | Component | cf-field/info", function(hooks) {
setupRenderingTest(hooks);
setupIntl(hooks);

test("it renders", async function(assert) {
await render(hbs`{{cf-field/info}}`);

assert.dom(this.element).hasText("");
assert.dom(this.element).hasText("t:caluma.form.info:()");
});
});
6 changes: 6 additions & 0 deletions tests/integration/components/cf-field/input/date-test.js
Expand Up @@ -13,6 +13,12 @@ module("Integration | Component | cf-field/input/date", function(hooks) {
this.owner.register("service:validator", ValidatorServiceStub);
});

test("it computes the proper element id", async function(assert) {
await render(hbs`{{cf-field/input/date field=(hash pk="test-id")}}`);

assert.dom("#test-id").exists();
});

test("it renders an input tag", async function(assert) {
await render(hbs`{{cf-field/input/date}}`);
assert.ok(this.element);
Expand Down
7 changes: 4 additions & 3 deletions tests/integration/components/cf-field/input/file-test.js
Expand Up @@ -8,9 +8,10 @@ module("Integration | Component | cf-field/input/file", function(hooks) {
setupRenderingTest(hooks);
setupMirage(hooks);

test("it renders", async function(assert) {
await render(hbs`{{cf-field/input/file}}`);
assert.ok(this.element);
test("it computes the proper element id", async function(assert) {
await render(hbs`{{cf-field/input/file field=(hash pk="test-id")}}`);

assert.dom("#test-id").exists();
});

test("it allows to upload a file", async function(assert) {
Expand Down
6 changes: 6 additions & 0 deletions tests/integration/components/cf-field/input/float-test.js
Expand Up @@ -6,6 +6,12 @@ import { hbs } from "ember-cli-htmlbars";
module("Integration | Component | cf-field/input/float", function(hooks) {
setupRenderingTest(hooks);

test("it computes the proper element id", async function(assert) {
await render(hbs`{{cf-field/input/float field=(hash pk="test-id")}}`);

assert.dom("#test-id").exists();
});

test("it renders", async function(assert) {
assert.expect(7);

Expand Down
6 changes: 6 additions & 0 deletions tests/integration/components/cf-field/input/integer-test.js
Expand Up @@ -6,6 +6,12 @@ import { hbs } from "ember-cli-htmlbars";
module("Integration | Component | cf-field/input/integer", function(hooks) {
setupRenderingTest(hooks);

test("it computes the proper element id", async function(assert) {
await render(hbs`{{cf-field/input/integer field=(hash pk="test-id")}}`);

assert.dom("#test-id").exists();
});

test("it renders", async function(assert) {
assert.expect(7);

Expand Down
14 changes: 14 additions & 0 deletions tests/integration/components/cf-field/input/powerselect-test.js
Expand Up @@ -61,6 +61,20 @@ module("Integration | Component | cf-field/input/powerselect", function(hooks) {
});
});

test("it computes the proper element id", async function(assert) {
await render(hbs`{{cf-field/input/powerselect field=singleChoiceField}}`);

assert
.dom(".ember-power-select-trigger")
.hasAttribute("id", this.singleChoiceField.pk);

await render(hbs`{{cf-field/input/powerselect field=multipleChoiceField}}`);

assert
.dom(".ember-power-select-trigger")
.hasAttribute("id", this.multipleChoiceField.pk);
});

test("it renders (single)", async function(assert) {
assert.expect(1);

Expand Down
6 changes: 6 additions & 0 deletions tests/integration/components/cf-field/input/text-test.js
Expand Up @@ -6,6 +6,12 @@ import { hbs } from "ember-cli-htmlbars";
module("Integration | Component | cf-field/input/text", function(hooks) {
setupRenderingTest(hooks);

test("it computes the proper element id", async function(assert) {
await render(hbs`{{cf-field/input/text field=(hash pk="test-id")}}`);

assert.dom("#test-id").exists();
});

test("it renders", async function(assert) {
assert.expect(4);

Expand Down
6 changes: 6 additions & 0 deletions tests/integration/components/cf-field/input/textarea-test.js
Expand Up @@ -6,6 +6,12 @@ import { hbs } from "ember-cli-htmlbars";
module("Integration | Component | cf-field/input/textarea", function(hooks) {
setupRenderingTest(hooks);

test("it computes the proper element id", async function(assert) {
await render(hbs`{{cf-field/input/textarea field=(hash pk="test-id")}}`);

assert.dom("#test-id").exists();
});

test("it renders", async function(assert) {
assert.expect(4);

Expand Down
8 changes: 8 additions & 0 deletions translations/de.yaml
Expand Up @@ -9,14 +9,22 @@ caluma:
optional: "Optional"
save: "Speichern"
delete: "Löschen"
edit: "Bearbeiten"
selectFile: "Klicken um Datei hochzuladen"
changeFile: "Klicken um andere Datei hochzuladen"
deleteRow: "Möchten Sie diese Zeile wirklich löschen?"
addRow: "Zeile hinzufügen"
optionNotAvailable: "Diese Option ist nicht mehr verfügbar"
info: "Mehr Informationen"

navigation:
next: "Weiter"
previous: "Zurück"
states:
untouched: "Nicht ausgefüllt"
unfinished: "Nicht vollständig ausgefüllt"
valid: "Korrekt ausgefüllt"
invalid: "Inkorrekt ausgefüllt"

notification:
table:
Expand Down
8 changes: 8 additions & 0 deletions translations/en.yaml
Expand Up @@ -9,14 +9,22 @@ caluma:
optional: "Optional"
save: "Save"
delete: "Delete"
edit: "Edit"
selectFile: "Click to upload a file"
changeFile: "Click to upload a different file"
deleteRow: "Do you really want to delete this row?"
addRow: "Add row"
optionNotAvailable: "This option is not available anymore"
info: "More information"

navigation:
next: "Next"
previous: "Previous"
states:
untouched: "Not filled in"
unfinished: "Not completely filled in"
valid: "Correctly filled in"
invalid: "Incorrectly filled in"

notification:
table:
Expand Down
8 changes: 8 additions & 0 deletions translations/fr.yaml
Expand Up @@ -9,14 +9,22 @@ caluma:
optional: "optional"
save: "sauvegarder"
delete: "supprimer"
edit: "modifier"
selectFile: "cliquez pour télécharger le ficher"
changeFile: "cliquez pour télécharger un autre fichier"
deleteRow: "Voulez-vous supprimer cette ligne?"
addRow: "Ajouter une ligne"
optionNotAvailable: "Cette option n'est plus disponible"
info: "Plus d'informations"

navigation:
next: "suivante"
previous: "précédente"
states:
untouched: "non rempli"
unfinished: "pas complètement rempli"
valid: "rempli correctement"
invalid: "mal rempli"

notification:
table:
Expand Down

0 comments on commit 3185fc8

Please sign in to comment.