migrate tag match type buttons to reka-ui radio group for accessibility

This commit is contained in:
Gregor Vostrak
2026-06-26 13:15:30 +02:00
parent 23ea1500b0
commit ccf3cec8c0

View File

@@ -1,6 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { CheckCircleIcon, TagIcon, UserGroupIcon } from '@heroicons/vue/20/solid'; import { CheckCircleIcon, TagIcon, UserGroupIcon } from '@heroicons/vue/20/solid';
import { FolderIcon } from '@heroicons/vue/16/solid'; import { FolderIcon } from '@heroicons/vue/16/solid';
import { Check } from '@lucide/vue';
import {
RadioGroupIndicator,
RadioGroupItem,
RadioGroupRoot,
type AcceptableValue,
} from 'reka-ui';
import BillableIcon from '@/packages/ui/src/Icons/BillableIcon.vue'; import BillableIcon from '@/packages/ui/src/Icons/BillableIcon.vue';
import ReportingRoundingControls from '@/Components/Common/Reporting/ReportingRoundingControls.vue'; import ReportingRoundingControls from '@/Components/Common/Reporting/ReportingRoundingControls.vue';
import TaskMultiselectDropdown from '@/Components/Common/Task/TaskMultiselectDropdown.vue'; import TaskMultiselectDropdown from '@/Components/Common/Task/TaskMultiselectDropdown.vue';
@@ -37,6 +44,16 @@ const emit = defineEmits<{
const { tags } = useTagsQuery(); const { tags } = useTagsQuery();
const tagMatchOptions: { value: TagMatchType; label: string }[] = [
{ value: 'contains', label: 'Contains' },
{ value: 'not_contains', label: 'Does Not Contain' },
];
function selectTagMatchType(value: AcceptableValue) {
tagMatchType.value = value as TagMatchType;
emit('submit');
}
async function createTag(name: string) { async function createTag(name: string) {
return await useTagsStore().createTag(name); return await useTagsStore().createTag(name);
} }
@@ -98,39 +115,29 @@ async function createTag(name: string) {
<template #content-before-list> <template #content-before-list>
<div class="mt-2 border-b border-card-background-separator pb-2"> <div class="mt-2 border-b border-card-background-separator pb-2">
<div <div
id="tag-match-type-label"
class="mb-1.5 px-2 text-xs font-medium text-text-tertiary uppercase"> class="mb-1.5 px-2 text-xs font-medium text-text-tertiary uppercase">
Match Match
</div> </div>
<div class="space-y-1"> <RadioGroupRoot
<button :model-value="tagMatchType"
type="button" aria-labelledby="tag-match-type-label"
class="w-full rounded-md px-2 py-1.5 text-left text-sm font-medium" class="space-y-1"
:class=" @update:model-value="selectTagMatchType">
tagMatchType === 'contains' <RadioGroupItem
? 'bg-card-background-active text-text-primary' v-for="option in tagMatchOptions"
: 'text-text-secondary hover:bg-card-background-active' :key="option.value"
" :value="option.value"
@click=" class="relative flex w-full items-center rounded-md py-1.5 pl-2 pr-8 text-left text-sm font-medium text-text-secondary hover:bg-card-background-active data-[state=checked]:text-text-primary">
tagMatchType = 'contains'; {{ option.label }}
emit('submit'); <span
"> class="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
Contains <RadioGroupIndicator>
</button> <Check class="h-4 w-4" />
<button </RadioGroupIndicator>
type="button" </span>
class="w-full rounded-md px-2 py-1.5 text-left text-sm font-medium" </RadioGroupItem>
:class=" </RadioGroupRoot>
tagMatchType === 'not_contains'
? 'bg-card-background-active text-text-primary'
: 'text-text-secondary hover:bg-card-background-active'
"
@click="
tagMatchType = 'not_contains';
emit('submit');
">
Does Not Contain
</button>
</div>
</div> </div>
</template> </template>
</TagDropdown> </TagDropdown>