From 835fc40a39195c21cfbfded61506d6744cf43683 Mon Sep 17 00:00:00 2001 From: Alex Sorafumo Date: Mon, 26 Oct 2026 16:41:37 +1100 Subject: [PATCH 1/3] fix(a11y): add landmarks, dialog names and table roles - Page shells have a main landmark, the menus are nav landmarks and active links mark the current page. A skip control moves focus to the content. The page language follows the locale. - Dialogs are named by their heading through a new appDialogTitle directive, which works like mat-dialog-title without its styles. - simple-table and the upload library expose rows, column headers, cells and sort state, with row numbers for virtual scrolling. - Settings toggles are one checkbox, not a checkbox in a button. Module status, badges, filters, loaders and icon tooltips have names. Error toasts are announced at once. Co-Authored-By: Claude Opus 5.5 --- public/assets/locale/en-AU.json | 11 +- src/app/admin/admin.component.ts | 14 +- .../signage-plugin-import-modal.component.ts | 4 +- .../signage-plugin-test-modal.component.ts | 4 +- src/app/admin/upload-library.component.ts | 180 ++++++++++------ src/app/admin/view-upload-modal.component.ts | 4 +- .../admin/zone-tree-export-modal.component.ts | 4 +- src/app/app.ts | 3 + src/app/common/locale.service.ts | 3 + src/app/common/notifications.ts | 2 + src/app/domains/domains.component.ts | 8 +- .../driver-update-list-modal.component.ts | 7 +- src/app/drivers/drivers.component.ts | 8 +- .../groups/group-bulk-add-modal.component.ts | 7 +- .../group-permissions-modal.component.ts | 6 +- src/app/groups/groups.component.ts | 8 +- src/app/modules/modules.component.ts | 8 +- .../bulk-item-modal.component.ts | 4 +- src/app/overlays/confirm-modal.component.ts | 4 +- .../metadata-details-modal.component.ts | 6 +- .../metadata-history-modal.component.ts | 4 +- .../overlays/view-module-state.component.ts | 4 +- .../repositories/repositories.component.ts | 8 +- src/app/systems/system-modules.component.ts | 26 ++- src/app/systems/systems.component.ts | 8 +- src/app/triggers/triggers.component.ts | 8 +- src/app/ui/context-menu.component.ts | 2 + src/app/ui/dialog-title.directive.ts | 33 +++ .../ui/fullscreen-modal-shell.component.ts | 4 +- src/app/ui/global-loading.component.ts | 5 +- src/app/ui/icon.component.ts | 38 ++-- src/app/ui/item-sidebar.component.ts | 35 +++- .../module-runtime-errors-modal.component.ts | 7 +- src/app/ui/settings-toggle.component.ts | 5 + src/app/ui/sidebar-menu.component.ts | 12 +- src/app/ui/simple-table.component.ts | 193 +++++++++++------- src/app/ui/skip-to-content.component.ts | 27 +++ .../ui/upload-permissions-modal.component.ts | 4 +- src/app/users/users.component.ts | 8 +- src/app/zones/zones.component.ts | 8 +- src/tests/common/notifications.spec.ts | 9 + .../components/simple-table.component.spec.ts | 31 ++- 42 files changed, 576 insertions(+), 198 deletions(-) create mode 100644 src/app/ui/dialog-title.directive.ts create mode 100644 src/app/ui/skip-to-content.component.ts diff --git a/public/assets/locale/en-AU.json b/public/assets/locale/en-AU.json index fb192147f..ebbd77829 100644 --- a/public/assets/locale/en-AU.json +++ b/public/assets/locale/en-AU.json @@ -49,6 +49,10 @@ "CANCEL": "Cancel", "CLOSE": "Close", "MENU": "Menu", + "MAIN_MENU": "Main menu", + "SKIP_TO_CONTENT": "Skip to main content", + "SELECT_ALL_ROWS": "Select all rows", + "SELECT_ROW": "Select row", "CLEAR_VALUE": "Clear", "REMOVE": "Remove", "VIEW": "View", @@ -395,7 +399,12 @@ "STATE": "Module State", "STATE_UPDATE": "Update state", "STATE_LOADING": "Loading module's state...", - "ERROR": "Runtime error in the module" + "ERROR": "Runtime error in the module", + "STATUS_LABEL": "Connection status: {{ status }}", + "STATUS_STOPPED": "Stopped", + "STATUS_CONNECTING": "Connecting", + "STATUS_CONNECTED": "Connected", + "STATUS_DISCONNECTED": "Disconnected" }, "ZONES": { "SINGULAR": "Zone", diff --git a/src/app/admin/admin.component.ts b/src/app/admin/admin.component.ts index c67a3ec25..6e70ce58a 100644 --- a/src/app/admin/admin.component.ts +++ b/src/app/admin/admin.component.ts @@ -23,8 +23,9 @@ import { TranslatePipe } from '../ui/translate.pipe';
- -
+ +
-
+ - +
+
-
- {{ - 'ADMIN.UPLOADS_LIB_FIELD_TYPE' - | translate - }} -
- {{ - sorted_by()[1] - ? 'arrow_downward' - : 'arrow_upward' - }} - - +
+
-
- {{ - 'ADMIN.UPLOADS_LIB_FIELD_SIZE' - | translate - }} -
- {{ - sorted_by()[1] - ? 'arrow_downward' - : 'arrow_upward' - }} - - +
+
-
- {{ 'COMMON.CREATED_AT' | translate }} -
- {{ - sorted_by()[1] - ? 'arrow_downward' - : 'arrow_upward' - }} - -
+ +
+
{{ upload.mime_type }}
{{ sizeOf(upload.file_size) }}
-
+
{{ +upload.created_at * 1000 | dateFrom }}
@@ -47,6 +48,7 @@ import { UploadInfo } from './upload-library.component'; `, styles: [``], imports: [ + DialogTitleDirective, MatDialogModule, AuthenticatedImageDirective, IconComponent, diff --git a/src/app/admin/zone-tree-export-modal.component.ts b/src/app/admin/zone-tree-export-modal.component.ts index 4bee61d05..79aac3e2a 100644 --- a/src/app/admin/zone-tree-export-modal.component.ts +++ b/src/app/admin/zone-tree-export-modal.component.ts @@ -9,13 +9,14 @@ import { import { PlaceZone, queryZones } from '@placeos/ts-client'; import { ItemSearchFieldComponent } from '../ui/custom-fields/item-search-field.component'; +import { DialogTitleDirective } from '../ui/dialog-title.directive'; import { IconComponent } from '../ui/icon.component'; @Component({ selector: 'app-zone-tree-export-modal', template: `
-

Export Zone Tree

+

Export Zone Tree

@@ -87,6 +88,7 @@ import { IconComponent } from '../ui/icon.component'; `, imports: [ + DialogTitleDirective, FormsModule, IconComponent, ItemSearchFieldComponent, diff --git a/src/app/app.ts b/src/app/app.ts index 9e9d3ff05..415e63fa7 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -79,9 +79,11 @@ function browserOnline() {
+
@@ -90,6 +92,7 @@ function browserOnline() { @if (!online() && !loading()) { `, styles: [``], diff --git a/src/app/drivers/driver-update-list-modal.component.ts b/src/app/drivers/driver-update-list-modal.component.ts index 1badf8ef5..508685ef8 100644 --- a/src/app/drivers/driver-update-list-modal.component.ts +++ b/src/app/drivers/driver-update-list-modal.component.ts @@ -11,6 +11,7 @@ import { describeError } from '../common/errors'; import { reportLoadError } from '../common/load-error'; import { i18n } from '../common/locale.service'; import { notifyError, notifySuccess } from '../common/notifications'; +import { DialogTitleDirective } from '../ui/dialog-title.directive'; import { IconComponent } from '../ui/icon.component'; import { DateFromPipe } from '../ui/pipes/date-from.pipe'; import { TranslatePipe } from '../ui/translate.pipe'; @@ -21,7 +22,10 @@ import { TranslatePipe } from '../ui/translate.pipe';
-

+

{{ 'DRIVERS.UPDATE' | translate }}
@if (!loading()) { @@ -156,6 +160,7 @@ import { TranslatePipe } from '../ui/translate.pipe'; `, ], imports: [ + DialogTitleDirective, MatDialogModule, TranslatePipe, IconComponent, diff --git a/src/app/drivers/drivers.component.ts b/src/app/drivers/drivers.component.ts index 883ce94a7..b8441ebba 100644 --- a/src/app/drivers/drivers.component.ts +++ b/src/app/drivers/drivers.component.ts @@ -31,7 +31,11 @@ import { DriverStateService } from './driver-state.service'; [route]="name" [title]="'DRIVERS.PLURAL' | translate" /> -
+
add -
+

@if (debug_position() === 'below') { diff --git a/src/app/groups/group-bulk-add-modal.component.ts b/src/app/groups/group-bulk-add-modal.component.ts index da79639c0..2eb8dcf16 100644 --- a/src/app/groups/group-bulk-add-modal.component.ts +++ b/src/app/groups/group-bulk-add-modal.component.ts @@ -10,6 +10,7 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatTabsModule } from '@angular/material/tabs'; +import { DialogTitleDirective } from '../ui/dialog-title.directive'; import { IconComponent } from '../ui/icon.component'; import { SettingsToggleComponent } from '../ui/settings-toggle.component'; import { TranslatePipe } from '../ui/translate.pipe'; @@ -49,7 +50,10 @@ export interface GroupBulkAddResult {
-

+

{{ title | translate }}

-
+
`, styles: [``], diff --git a/src/app/modules/modules.component.ts b/src/app/modules/modules.component.ts index 2674b5b84..1b73e7083 100644 --- a/src/app/modules/modules.component.ts +++ b/src/app/modules/modules.component.ts @@ -33,7 +33,11 @@ import { ModuleStateService } from './module-state.service'; [route]="name" [title]="'MODULES.PLURAL' | translate" /> -
+
add -
+
@if (debug_position() === 'below') { diff --git a/src/app/overlays/bulk-item-modal/bulk-item-modal.component.ts b/src/app/overlays/bulk-item-modal/bulk-item-modal.component.ts index e82330c09..b0a7a7b72 100644 --- a/src/app/overlays/bulk-item-modal/bulk-item-modal.component.ts +++ b/src/app/overlays/bulk-item-modal/bulk-item-modal.component.ts @@ -17,6 +17,7 @@ import { import { unique } from '../../common/general'; import { notifyError, notifyWarn } from '../../common/notifications'; import { HashMap, Identity } from '../../common/types'; +import { DialogTitleDirective } from '../../ui/dialog-title.directive'; import { IconComponent } from '../../ui/icon.component'; import { TranslatePipe } from '../../ui/translate.pipe'; import { CsvUploadComponent } from './csv-upload.component'; @@ -57,7 +58,7 @@ export interface BulkItemModalData> { class="bg-base-200 m-4 mb-2 flex items-center justify-between rounded-sm px-4 py-3" >
-

+

{{ 'COMMON.BULK_ADD' | translate: { type: type } }}

@@ -155,6 +156,7 @@ export interface BulkItemModalData> { `, ], imports: [ + DialogTitleDirective, MatDialogModule, TranslatePipe, IconComponent, diff --git a/src/app/overlays/confirm-modal.component.ts b/src/app/overlays/confirm-modal.component.ts index 5116fe456..bb964269a 100644 --- a/src/app/overlays/confirm-modal.component.ts +++ b/src/app/overlays/confirm-modal.component.ts @@ -25,6 +25,7 @@ import { i18n } from '../common/locale.service'; import { notifyInfo } from '../common/notifications'; import { waitForEvent } from '../common/signals'; import { ApplicationIcon, DialogEvent } from '../common/types'; +import { DialogTitleDirective } from '../ui/dialog-title.directive'; import { IconComponent } from '../ui/icon.component'; import { TranslatePipe } from '../ui/translate.pipe'; @@ -179,7 +180,7 @@ export async function openConfirmModal(

-

+

{{ result() ? result().title : (title | translate) }}

@@ -399,6 +400,7 @@ export async function openConfirmModal( `, styles: [``], imports: [ + DialogTitleDirective, MatProgressSpinnerModule, MatCheckboxModule, TranslatePipe, diff --git a/src/app/overlays/metadata-details-modal.component.ts b/src/app/overlays/metadata-details-modal.component.ts index 52eeb2ade..c14e3fb3a 100644 --- a/src/app/overlays/metadata-details-modal.component.ts +++ b/src/app/overlays/metadata-details-modal.component.ts @@ -18,6 +18,7 @@ import { import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { addSignalChipItem, removeSignalChipItem } from '../common/forms'; +import { DialogTitleDirective } from '../ui/dialog-title.directive'; import { IconComponent } from '../ui/icon.component'; import type { MetadataFormModel } from '../ui/metadata-display.component'; import { TranslatePipe } from '../ui/translate.pipe'; @@ -35,7 +36,9 @@ export interface MetadataDetailsModalData {
-

Update metadata details

+

+ Update metadata details +

-
+ `, styles: [``], diff --git a/src/app/systems/system-modules.component.ts b/src/app/systems/system-modules.component.ts index 9595125f7..00ae6a60e 100644 --- a/src/app/systems/system-modules.component.ts +++ b/src/app/systems/system-modules.component.ts @@ -197,6 +197,11 @@ import { SystemStateService } from './system-state.service';
+ + @let status_label = + 'MODULES.STATUS_LABEL' + | translate + : { status: moduleStatus(row) | translate }; @if (row.running && row.connected === undefined) { E @@ -288,6 +300,9 @@ import { SystemStateService } from './system-state.service';