trypost/resources/js/components/LanguagePicker.vue
Paulo Castellano de4d5f3c68 Share a ContentLanguageOption type and close the review's test gaps
- Extract ContentLanguageOption into @/types and use it for availableContentLanguages
  across BrandForm, BrandTab, Brand, Create, and the LanguagePicker options, so the
  englishName field that drives search is visible to TypeScript instead of being
  dropped silently by the pass-through prop types.
- Add BrandAnalyzerTest: assert the language schema enum equals the full 15-language
  set (guards against it shrinking back to a hardcoded subset behind ::fake()) and
  that instructions() lists every code.
- Isolate the "LLM language wins" autofill test by declaring the page as `en` while
  the LLM returns `de`, so it actually proves mergeLlm precedence instead of both
  paths agreeing.
- Cover SetLocale's cookie side effect: the default locale cookie is set on an
  invalid/absent cookie and left untouched for a valid locale.
2026-07-03 19:28:28 -03:00

78 lines
2.8 KiB
Vue

<script setup lang="ts">
import { IconCheck, IconChevronDown } from '@tabler/icons-vue';
import { computed, ref } from 'vue';
import { Button } from '@/components/ui/button';
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { cn } from '@/lib/utils';
import type { ContentLanguageOption } from '@/types';
interface Props {
/** Selectable languages as { value: locale code, label: native name, englishName: name in English }. */
options: ContentLanguageOption[];
placeholder?: string;
searchPlaceholder?: string;
emptyText?: string;
disabled?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
placeholder: 'Select a language…',
searchPlaceholder: 'Search language…',
emptyText: 'No language found.',
disabled: false,
});
const value = defineModel<string | null>({ required: true });
const open = ref(false);
const selectedLabel = computed(() => props.options.find((option) => option.value === value.value)?.label ?? '');
const select = (code: string) => {
value.value = code;
open.value = false;
};
</script>
<template>
<Popover v-model:open="open">
<PopoverTrigger as-child>
<Button
type="button"
variant="outline"
role="combobox"
:aria-expanded="open"
:disabled="disabled"
class="w-full justify-between font-normal"
>
<span :class="selectedLabel ? '' : 'text-muted-foreground'">
{{ selectedLabel || placeholder }}
</span>
<IconChevronDown class="ms-2 size-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent class="w-(--reka-popover-trigger-width) p-0" align="start">
<Command>
<CommandInput :placeholder="searchPlaceholder" />
<CommandList>
<CommandEmpty>{{ emptyText }}</CommandEmpty>
<CommandGroup>
<CommandItem
v-for="option in options"
:key="option.value"
:value="option.value"
@select="select(option.value)"
>
{{ option.label }}
<span v-if="option.englishName" class="sr-only">{{ option.englishName }}</span>
<IconCheck :class="cn('ms-auto size-4', value === option.value ? 'opacity-100' : 'opacity-0')" />
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</template>