Maintenance: Desktop view - Beta UI: column headers SERP not accessible to screen readers

This commit is contained in:
Ben
2026-06-05 13:48:53 +01:00
parent f0c0fef3bb
commit 68792d5be2
6 changed files with 53 additions and 30 deletions

View File

@@ -412,11 +412,11 @@ const endOfListMessage = computed(() => {
return props.totalItemsCount > props.maxItems
? i18n.t(
'You reached the table limit of %s tickets (%s remaining).',
'You reached the table limit of %s items (%s remaining).',
props.maxItems,
props.totalItemsCount - loadedItems.value.length,
)
: i18n.t("You don't have more tickets to load.")
: i18n.t("You don't have more items to load.")
})
const getLinkColorClasses = (item: TableAdvancedItem) => {
@@ -622,7 +622,7 @@ watch(
<slot :name="`item-suffix-${tableAttribute.name}`" :item="item" />
</div>
</td>
<td v-if="actions" class="h-10 p-2.5 text-center">
<td v-if="actions" headers="actions-header" class="h-10 p-2.5 text-center">
<slot name="actions" v-bind="{ actions, item }">
<CommonActionMenu
class="flex! items-center justify-center"

View File

@@ -4,10 +4,12 @@ import { useActiveElement, useEventListener } from '@vueuse/core'
import { computed, ref, useTemplateRef } from 'vue'
import { EnumTextDirection } from '#shared/graphql/types.ts'
import { i18n } from '#shared/i18n.ts'
import { useLocaleStore } from '#shared/stores/locale.ts'
import getUuid from '#shared/utils/getUuid.ts'
import { useResizeLine } from '#desktop/components/ResizeLine/useResizeLine.ts'
import { useAnnouncer } from '#desktop/composables/accessibility/useAnnouncer.ts'
import { MINIMUM_COLUMN_WIDTH } from './types.ts'
@@ -51,6 +53,8 @@ const setHeaderWidths = (diff: number) => {
const activeElement = useActiveElement()
const { announce, messageNodeId } = useAnnouncer()
const handleKeyStroke = (e: KeyboardEvent, diff: number) => {
if (activeElement.value !== resizeLine.value) return
@@ -59,6 +63,11 @@ const handleKeyStroke = (e: KeyboardEvent, diff: number) => {
setCurrentHeaderWidths()
setHeaderWidths(diff)
emit('resize')
// Announce the resulting width via the live region, so screen reader users
// get feedback while adjusting the column with the arrow keys.
if (currentHeader.value)
announce(i18n.t('Column width: %s pixels', currentHeader.value.clientWidth))
}
const resizeStartX = ref(0)
@@ -123,24 +132,19 @@ const id = getUuid()
<button
ref="resize-line"
v-tooltip="$t('Resize column')"
:aria-describedby="id"
:aria-describedby="`${id} ${messageNodeId}`"
tabindex="0"
class="absolute end-0 top-1/2 h-5 w-1 -translate-y-2.5 cursor-col-resize! rounded-xs bg-neutral-100 hover:bg-blue-600 focus:outline-none focus-visible:bg-blue-800! dark:bg-gray-200 dark:hover:bg-blue-900"
class="absolute inset-e-0 top-1/2 h-5 w-1 -translate-y-2.5 cursor-col-resize! rounded-xs bg-neutral-100 hover:bg-blue-600 focus:outline-none focus-visible:bg-blue-800! dark:bg-gray-200 dark:hover:bg-blue-900"
:class="{
'!bg-blue-800': resizing,
'bg-blue-800': resizing,
}"
@mousedown="handleMousedown"
@blur="resizing = false"
@touchstart="handleTouchstart"
@dblclick="handleDoubleClick"
>
<span
:id="id"
role="separator"
class="invisible absolute -z-20"
aria-orientation="horizontal"
:aria-valuenow="currentHeader?.clientWidth"
:aria-valuemin="MINIMUM_COLUMN_WIDTH"
/>
<span :id="id" class="sr-only">
{{ $t('Use the left and right arrow keys to resize the column.') }}
</span>
</button>
</template>

View File

@@ -293,7 +293,13 @@ useOnEmitter('primary-sidebar-transition', () => initializeHeaderWidths())
<template>
<thead ref="thead">
<tr>
<th v-if="hasBulkAction" id="select-header" :aria-label="$t('Select')" class="size-10">
<th
v-if="hasBulkAction"
id="select-header"
scope="col"
:aria-label="$t('Select')"
class="size-10"
>
<BulkCheckbox
:items="items"
:item-ids="itemIds"
@@ -306,6 +312,7 @@ useOnEmitter('primary-sidebar-transition', () => initializeHeaderWidths())
v-for="(tableAttribute, index) in tableAttributes"
:id="`${tableAttribute.name}-header`"
:key="tableAttribute.name"
scope="col"
class="relative h-10 p-2.5 text-xs"
:class="[tableAttribute.headerPreferences?.headerClass]"
:aria-label="$t(tableAttribute.label, ...(tableAttribute.labelPlaceholder || []))"
@@ -377,7 +384,7 @@ useOnEmitter('primary-sidebar-transition', () => initializeHeaderWidths())
@reset="resetHeaderWidths"
/>
</th>
<th v-if="actions" class="h-10 w-0 p-2.5 text-center">
<th v-if="actions" id="actions-header" scope="col" class="h-10 w-0 p-2.5 text-center">
<CommonLabel class="font-normal text-stone-200! dark:text-neutral-500!" size="small"
>{{ $t('Actions') }}
</CommonLabel>

View File

@@ -915,7 +915,7 @@ describe('CommonAdvancedTable', () => {
})
expect(
wrapper.getByText('You reached the table limit of 20 tickets (10 remaining).'),
wrapper.getByText('You reached the table limit of 20 items (10 remaining).'),
).toBeInTheDocument()
scrollContainer.remove()
@@ -943,7 +943,7 @@ describe('CommonAdvancedTable', () => {
orderBy: 'label',
})
expect(wrapper.getByText("You don't have more tickets to load.")).toBeInTheDocument()
expect(wrapper.getByText("You don't have more items to load.")).toBeInTheDocument()
scrollContainer.remove()
})

View File

@@ -662,7 +662,7 @@ setOnSuccessCallback(() => {
:is="searchPlugin.detailSearchComponent"
:key="selectedEntity"
:table-id="`search-${selectedEntity}-table`"
:caption="`Search result for: ${searchPlugin.label}`"
:caption="$t('Search result for: %s', searchPlugin.label)"
:items="searchResultItems"
:headers="detailSearchHeaders"
:total-count="searchResultTotalCount"

View File

@@ -348,11 +348,11 @@ msgstr ""
msgid "%s required!"
msgstr ""
#: app/frontend/apps/desktop/components/CommonTable/TableHeader.vue:392
#: app/frontend/apps/desktop/components/CommonTable/TableHeader.vue:399
msgid "%s result(s) selected"
msgstr ""
#: app/frontend/apps/desktop/components/CommonTable/TableHeader.vue:392
#: app/frontend/apps/desktop/components/CommonTable/TableHeader.vue:399
msgid "%s result(s) selected, selection limit reached"
msgstr ""
@@ -1052,7 +1052,7 @@ msgstr ""
#: app/assets/javascripts/app/views/integration/smime_list.jst.eco:15
#: app/assets/javascripts/app/views/ssl_certificates_list.jst.eco:16
#: app/frontend/apps/desktop/components/CommonTable/CommonSimpleTable.vue:100
#: app/frontend/apps/desktop/components/CommonTable/TableHeader.vue:382
#: app/frontend/apps/desktop/components/CommonTable/TableHeader.vue:389
msgid "Actions"
msgstr ""
@@ -1533,7 +1533,7 @@ msgstr ""
msgid "Alarm"
msgstr ""
#: app/frontend/apps/desktop/components/CommonTable/TableHeader.vue:393
#: app/frontend/apps/desktop/components/CommonTable/TableHeader.vue:400
msgid "All %s result(s) selected"
msgstr ""
@@ -3413,7 +3413,7 @@ msgid "Clear search"
msgstr ""
#: app/frontend/apps/desktop/components/CommonTable/CellContent/BulkCheckbox.vue:61
#: app/frontend/apps/desktop/components/CommonTable/TableHeader.vue:416
#: app/frontend/apps/desktop/components/CommonTable/TableHeader.vue:423
#: app/frontend/apps/desktop/components/Form/fields/FieldAutoComplete/FieldAutoCompleteInput.vue:649
#: app/frontend/apps/desktop/components/Form/fields/FieldDate/FieldDateTimeInput.vue:386
#: app/frontend/apps/desktop/components/Form/fields/FieldSelect/FieldSelectInput.vue:314
@@ -3596,6 +3596,10 @@ msgstr ""
msgid "Color representation of the open ticket indicator in the sidebar."
msgstr ""
#: app/frontend/apps/desktop/components/CommonTable/HeaderResizeLine.vue:70
msgid "Column width: %s pixels"
msgstr ""
#: app/frontend/apps/desktop/pages/personal-setting/views/PersonalSettingCalendar.vue:290
msgid "Combined subscription URL"
msgstr ""
@@ -13966,7 +13970,7 @@ msgstr ""
msgid "Resize article panel"
msgstr ""
#: app/frontend/apps/desktop/components/CommonTable/HeaderResizeLine.vue:125
#: app/frontend/apps/desktop/components/CommonTable/HeaderResizeLine.vue:134
msgid "Resize column"
msgstr ""
@@ -14564,6 +14568,10 @@ msgstr ""
msgid "Search or enter phone number…"
msgstr ""
#: app/frontend/apps/desktop/pages/search/components/SearchContent.vue:665
msgid "Search result for: %s"
msgstr ""
#: lib/external_data_source/errors.rb:114
msgid "Search result label is not a string, a number or a boolean. Please provide search result label key."
msgstr ""
@@ -14766,11 +14774,11 @@ msgstr ""
msgid "See your tickets from within your favorite calendar by adding the subscription URL to your calendar app."
msgstr ""
#: app/frontend/apps/desktop/components/CommonTable/TableHeader.vue:296
#: app/frontend/apps/desktop/components/CommonTable/TableHeader.vue:300
msgid "Select"
msgstr ""
#: app/frontend/apps/desktop/components/CommonTable/TableHeader.vue:406
#: app/frontend/apps/desktop/components/CommonTable/TableHeader.vue:413
msgid "Select %s result(s)"
msgstr ""
@@ -14791,7 +14799,7 @@ msgstr ""
msgid "Select Template"
msgstr ""
#: app/frontend/apps/desktop/components/CommonTable/TableHeader.vue:407
#: app/frontend/apps/desktop/components/CommonTable/TableHeader.vue:414
msgid "Select all %s results"
msgstr ""
@@ -19667,6 +19675,10 @@ msgstr ""
msgid "Use the camera to take a photo for the avatar."
msgstr ""
#: app/frontend/apps/desktop/components/CommonTable/HeaderResizeLine.vue:147
msgid "Use the left and right arrow keys to resize the column."
msgstr ""
#: db/seeds/settings.rb:2372
msgid "Use the start time of the last customer thread (which may consist of multiple articles)."
msgstr ""
@@ -20666,7 +20678,7 @@ msgid "You do not have permission to update"
msgstr ""
#: app/frontend/apps/desktop/components/CommonTable/CommonAdvancedTable.vue:419
msgid "You don't have more tickets to load."
msgid "You don't have more items to load."
msgstr ""
#: app/frontend/shared/router/guards/before/permission.ts:27
@@ -20771,7 +20783,7 @@ msgid "You need to configure the Zammad endpoints in the %s:"
msgstr ""
#: app/frontend/apps/desktop/components/CommonTable/CommonAdvancedTable.vue:415
msgid "You reached the table limit of %s tickets (%s remaining)."
msgid "You reached the table limit of %s items (%s remaining)."
msgstr ""
#: app/assets/javascripts/app/controllers/agent_ticket_create.coffee:772