fix: announce toggle state of integrations menu rows to screen readers (#27667)
Every toggle row in the chat integrations menu (filters, Web Search, Image, Code Interpreter, Tools, Skills) is a button whose on/off state was carried only by the decorative Switch inside it. Screen readers announced the row name and nothing else, so there was no way to tell whether a tool or feature was active without looking at it.
Each row button now carries aria-pressed, and the Switch wrapper is marked inert so the nested role=switch stops competing with the row for the announcement and stops adding a nameless tab stop. Hit testing skips inert content, so clicking the switch still toggles the row.
Tool rows that are not yet authenticated omit aria-pressed: activating those starts an OAuth redirect rather than toggling, so announcing them as an unpressed toggle would be wrong.
The Web Search, Image and Code Interpreter rows also had a state-flipping aria-label ("Disable Web Search") on top of aria-pressed, which announces as "Disable Web Search, pressed" and reads as the opposite of the truth. Removed: the visible row text already names each control.
Fixes #17150
This commit is contained in:
1 parent
ba885d0026
commit
5cb87e6630
1 file changed
+15
-43
@@ -201,6 +201,7 @@
|
|||||||
<Tooltip content={filter?.description} placement="top-start">
|
<Tooltip content={filter?.description} placement="top-start">
|
||||||
<button
|
<button
|
||||||
class="flex w-full justify-between gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer rounded-xl hover:bg-gray-50/40 dark:hover:bg-gray-800/40"
|
class="flex w-full justify-between gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer rounded-xl hover:bg-gray-50/40 dark:hover:bg-gray-800/40"
|
||||||
|
aria-pressed={selectedFilterIds.includes(filter.id)}
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
if (selectedFilterIds.includes(filter.id)) {
|
if (selectedFilterIds.includes(filter.id)) {
|
||||||
selectedFilterIds = selectedFilterIds.filter((id) => id !== filter.id);
|
selectedFilterIds = selectedFilterIds.filter((id) => id !== filter.id);
|
||||||
@@ -253,14 +254,8 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class=" shrink-0">
|
<div class=" shrink-0" inert>
|
||||||
<Switch
|
<Switch state={selectedFilterIds.includes(filter.id)} />
|
||||||
state={selectedFilterIds.includes(filter.id)}
|
|
||||||
on:change={async (e) => {
|
|
||||||
const state = e.detail;
|
|
||||||
await tick();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -272,9 +267,6 @@
|
|||||||
<button
|
<button
|
||||||
class="flex w-full justify-between gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer rounded-xl hover:bg-gray-50/40 dark:hover:bg-gray-800/40"
|
class="flex w-full justify-between gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer rounded-xl hover:bg-gray-50/40 dark:hover:bg-gray-800/40"
|
||||||
aria-pressed={webSearchEnabled}
|
aria-pressed={webSearchEnabled}
|
||||||
aria-label={webSearchEnabled
|
|
||||||
? $i18n.t('Disable Web Search')
|
|
||||||
: $i18n.t('Enable Web Search')}
|
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
webSearchEnabled = !webSearchEnabled;
|
webSearchEnabled = !webSearchEnabled;
|
||||||
onWebSearchToggle(webSearchEnabled);
|
onWebSearchToggle(webSearchEnabled);
|
||||||
@@ -290,14 +282,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class=" shrink-0">
|
<div class=" shrink-0" inert>
|
||||||
<Switch
|
<Switch state={webSearchEnabled} />
|
||||||
state={webSearchEnabled}
|
|
||||||
on:change={async (e) => {
|
|
||||||
const state = e.detail;
|
|
||||||
await tick();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -308,9 +294,6 @@
|
|||||||
<button
|
<button
|
||||||
class="flex w-full justify-between gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer rounded-xl hover:bg-gray-50/40 dark:hover:bg-gray-800/40"
|
class="flex w-full justify-between gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer rounded-xl hover:bg-gray-50/40 dark:hover:bg-gray-800/40"
|
||||||
aria-pressed={imageGenerationEnabled}
|
aria-pressed={imageGenerationEnabled}
|
||||||
aria-label={imageGenerationEnabled
|
|
||||||
? $i18n.t('Disable Image Generation')
|
|
||||||
: $i18n.t('Enable Image Generation')}
|
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
imageGenerationEnabled = !imageGenerationEnabled;
|
imageGenerationEnabled = !imageGenerationEnabled;
|
||||||
}}
|
}}
|
||||||
@@ -325,14 +308,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class=" shrink-0">
|
<div class=" shrink-0" inert>
|
||||||
<Switch
|
<Switch state={imageGenerationEnabled} />
|
||||||
state={imageGenerationEnabled}
|
|
||||||
on:change={async (e) => {
|
|
||||||
const state = e.detail;
|
|
||||||
await tick();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -343,9 +320,6 @@
|
|||||||
<button
|
<button
|
||||||
class="flex w-full justify-between gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer rounded-xl hover:bg-gray-50/40 dark:hover:bg-gray-800/40"
|
class="flex w-full justify-between gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer rounded-xl hover:bg-gray-50/40 dark:hover:bg-gray-800/40"
|
||||||
aria-pressed={codeInterpreterEnabled}
|
aria-pressed={codeInterpreterEnabled}
|
||||||
aria-label={codeInterpreterEnabled
|
|
||||||
? $i18n.t('Disable Code Interpreter')
|
|
||||||
: $i18n.t('Enable Code Interpreter')}
|
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
codeInterpreterEnabled = !codeInterpreterEnabled;
|
codeInterpreterEnabled = !codeInterpreterEnabled;
|
||||||
}}
|
}}
|
||||||
@@ -360,14 +334,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class=" shrink-0">
|
<div class=" shrink-0" inert>
|
||||||
<Switch
|
<Switch state={codeInterpreterEnabled} />
|
||||||
state={codeInterpreterEnabled}
|
|
||||||
on:change={async (e) => {
|
|
||||||
const state = e.detail;
|
|
||||||
await tick();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -394,6 +362,9 @@
|
|||||||
{#each Object.keys(tools) as toolId}
|
{#each Object.keys(tools) as toolId}
|
||||||
<button
|
<button
|
||||||
class="relative flex w-full justify-between gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer rounded-xl hover:bg-gray-50/40 dark:hover:bg-gray-800/40"
|
class="relative flex w-full justify-between gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer rounded-xl hover:bg-gray-50/40 dark:hover:bg-gray-800/40"
|
||||||
|
aria-pressed={(tools[toolId]?.authenticated ?? true)
|
||||||
|
? tools[toolId].enabled
|
||||||
|
: undefined}
|
||||||
on:click={async (e) => {
|
on:click={async (e) => {
|
||||||
if (!(tools[toolId]?.authenticated ?? true)) {
|
if (!(tools[toolId]?.authenticated ?? true)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -490,7 +461,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class=" shrink-0">
|
<div class=" shrink-0" inert>
|
||||||
<Switch state={tools[toolId].enabled} />
|
<Switch state={tools[toolId].enabled} />
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
@@ -517,6 +488,7 @@
|
|||||||
{#each Object.keys(skills) as skillId}
|
{#each Object.keys(skills) as skillId}
|
||||||
<button
|
<button
|
||||||
class="relative flex w-full justify-between gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer rounded-xl hover:bg-gray-50/40 dark:hover:bg-gray-800/40"
|
class="relative flex w-full justify-between gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer rounded-xl hover:bg-gray-50/40 dark:hover:bg-gray-800/40"
|
||||||
|
aria-pressed={skills[skillId].enabled}
|
||||||
on:click={async () => {
|
on:click={async () => {
|
||||||
skills[skillId].enabled = !skills[skillId].enabled;
|
skills[skillId].enabled = !skills[skillId].enabled;
|
||||||
|
|
||||||
@@ -543,7 +515,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class=" shrink-0">
|
<div class=" shrink-0" inert>
|
||||||
<Switch state={skills[skillId].enabled} />
|
<Switch state={skills[skillId].enabled} />
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in new issue
Block a user