fix(primitives): component fixes and test cleanup
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 4.7 KiB |
@@ -43,7 +43,7 @@ function load(nextSrc: string | undefined) {
|
|||||||
setStatus('error');
|
setStatus('error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeof globalThis.window === 'undefined') {
|
if (globalThis.window === undefined) {
|
||||||
setStatus('loading');
|
setStatus('loading');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ export interface ComboboxRootProps<T extends AcceptableValue = AcceptableValue>
|
|||||||
modelValue?: T | T[];
|
modelValue?: T | T[];
|
||||||
/** Uncontrolled initial value. */
|
/** Uncontrolled initial value. */
|
||||||
defaultValue?: T | T[];
|
defaultValue?: T | T[];
|
||||||
/** Controlled open state. Use `v-model:open`. */
|
|
||||||
open?: boolean;
|
|
||||||
/** Uncontrolled default open state. */
|
/** Uncontrolled default open state. */
|
||||||
defaultOpen?: boolean;
|
defaultOpen?: boolean;
|
||||||
/** Allow selecting multiple values. */
|
/** Allow selecting multiple values. */
|
||||||
@@ -77,6 +75,7 @@ const config = useConfig();
|
|||||||
const direction = computed(() => dir ?? config.dir.value);
|
const direction = computed(() => dir ?? config.dir.value);
|
||||||
|
|
||||||
const localOpen = ref<boolean>(defaultOpen);
|
const localOpen = ref<boolean>(defaultOpen);
|
||||||
|
/** Controlled open state. Use `v-model:open`. */
|
||||||
const open = defineModel<boolean>('open', {
|
const open = defineModel<boolean>('open', {
|
||||||
default: undefined,
|
default: undefined,
|
||||||
get: v => v ?? localOpen.value,
|
get: v => v ?? localOpen.value,
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ const filteredItems = computed<Map<string, number>>(() => {
|
|||||||
|
|
||||||
function escapeAttr(v: string): string {
|
function escapeAttr(v: string): string {
|
||||||
if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') return CSS.escape(v);
|
if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') return CSS.escape(v);
|
||||||
return v.replace(/["\\]/g, '\\$&');
|
return v.replaceAll(/["\\]/g, '\\$&');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSelectableItems(): string[] {
|
function getSelectableItems(): string[] {
|
||||||
|
|||||||
Vendored
+2
-6
@@ -5,13 +5,9 @@ declare global {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
interface ComponentCustomProps {
|
type ComponentCustomProps = Record<`data${string}`, unknown>;
|
||||||
[key: `data${string}`]: unknown;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
interface HTMLAttributes {
|
type HTMLAttributes = Record<`data-${string}`, unknown>;
|
||||||
[key: `data-${string}`]: unknown;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ const { openDelay = 700, closeDelay = 300, defaultOpen = false } = defineProps<H
|
|||||||
const openModel = defineModel<boolean | undefined>('open', { default: undefined });
|
const openModel = defineModel<boolean | undefined>('open', { default: undefined });
|
||||||
const uncontrolled = ref(defaultOpen);
|
const uncontrolled = ref(defaultOpen);
|
||||||
|
|
||||||
|
// `open` intentionally shares the model name: it's a local read-only computed that
|
||||||
|
// resolves the controlled model against the uncontrolled fallback. Safe in script-setup.
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
const open = computed(() => openModel.value ?? uncontrolled.value);
|
const open = computed(() => openModel.value ?? uncontrolled.value);
|
||||||
|
|
||||||
function setOpen(value: boolean) {
|
function setOpen(value: boolean) {
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
// This component takes no props of its own (it renders a fixed wrapper + RovingFocusGroup
|
||||||
|
// and forwards $attrs). The empty interface is the intentional public prop contract.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||||
export interface NavigationMenuListProps {}
|
export interface NavigationMenuListProps {}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ import type { Direction } from '../config-provider';
|
|||||||
import type { Orientation } from '../roving-focus';
|
import type { Orientation } from '../roving-focus';
|
||||||
|
|
||||||
export interface NavigationMenuRootProps {
|
export interface NavigationMenuRootProps {
|
||||||
/** Controlled active item value. Use `v-model`. */
|
|
||||||
modelValue?: string;
|
|
||||||
/** Uncontrolled initial value. */
|
/** Uncontrolled initial value. */
|
||||||
defaultValue?: string;
|
defaultValue?: string;
|
||||||
/** Reading direction. Falls back to `ConfigProvider`. */
|
/** Reading direction. Falls back to `ConfigProvider`. */
|
||||||
@@ -72,6 +70,7 @@ const config = useConfig();
|
|||||||
const dirRef = computed<Direction>(() => dir ?? config.dir.value);
|
const dirRef = computed<Direction>(() => dir ?? config.dir.value);
|
||||||
|
|
||||||
const localValue = ref<string>(defaultValue ?? '');
|
const localValue = ref<string>(defaultValue ?? '');
|
||||||
|
/** Controlled active item value. Use `v-model`. */
|
||||||
const modelValue = defineModel<string | undefined>({
|
const modelValue = defineModel<string | undefined>({
|
||||||
default: undefined,
|
default: undefined,
|
||||||
get: v => v ?? localValue.value,
|
get: v => v ?? localValue.value,
|
||||||
@@ -212,7 +211,7 @@ provideNavigationMenuContext({
|
|||||||
<Primitive
|
<Primitive
|
||||||
:ref="forwardRef"
|
:ref="forwardRef"
|
||||||
as="nav"
|
as="nav"
|
||||||
:aria-label="$attrs['aria-label'] as string | undefined ?? 'Main'"
|
:aria-label="($attrs['aria-label'] ?? 'Main') as string"
|
||||||
:data-orientation="orientation"
|
:data-orientation="orientation"
|
||||||
:dir="dirRef"
|
:dir="dirRef"
|
||||||
data-primitives-navigation-menu
|
data-primitives-navigation-menu
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
import type { Orientation } from '../roving-focus';
|
import type { Orientation } from '../roving-focus';
|
||||||
|
|
||||||
export interface NavigationMenuSubProps {
|
export interface NavigationMenuSubProps {
|
||||||
/** Controlled active value of the submenu. Use `v-model`. */
|
|
||||||
modelValue?: string;
|
|
||||||
/** Uncontrolled initial value. */
|
/** Uncontrolled initial value. */
|
||||||
defaultValue?: string;
|
defaultValue?: string;
|
||||||
/** Submenu orientation. @default 'horizontal' */
|
/** Submenu orientation. @default 'horizontal' */
|
||||||
@@ -36,6 +34,7 @@ defineSlots<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const localValue = ref<string>(defaultValue ?? '');
|
const localValue = ref<string>(defaultValue ?? '');
|
||||||
|
/** Controlled active value of the submenu. Use `v-model`. */
|
||||||
const modelValue = defineModel<string | undefined>({
|
const modelValue = defineModel<string | undefined>({
|
||||||
default: undefined,
|
default: undefined,
|
||||||
get: v => v ?? localValue.value,
|
get: v => v ?? localValue.value,
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ function onPaste(e: ClipboardEvent): void {
|
|||||||
return;
|
return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const chars = ctx.type.value === 'number'
|
const chars = ctx.type.value === 'number'
|
||||||
? data.replace(NON_DIGIT_G, '').split('')
|
? data.replaceAll(NON_DIGIT_G, '').split('')
|
||||||
: data.split('');
|
: data.split('');
|
||||||
let idx = props.index;
|
let idx = props.index;
|
||||||
for (const ch of chars) {
|
for (const ch of chars) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const { forwardRef } = useForwardExpose();
|
|||||||
|
|
||||||
function onClick(): void {
|
function onClick(): void {
|
||||||
if (item.disabled.value) return;
|
if (item.disabled.value) return;
|
||||||
const idx = ctx.modelValue.value.findIndex(v => v === item.value.value);
|
const idx = ctx.modelValue.value.indexOf(item.value.value);
|
||||||
if (idx !== -1) ctx.onRemoveValue(idx);
|
if (idx !== -1) ctx.onRemoveValue(idx);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -120,8 +120,8 @@ describe('TagsInput', () => {
|
|||||||
|
|
||||||
it('delete trigger removes its tag', async () => {
|
it('delete trigger removes its tag', async () => {
|
||||||
const w = createTagsInput({ defaultValue: ['a', 'b'] });
|
const w = createTagsInput({ defaultValue: ['a', 'b'] });
|
||||||
const deleteBtns = w.findAll('button').filter(b => b.text() === '×');
|
const deleteBtn = w.findAll('button').find(b => b.text() === '×');
|
||||||
await deleteBtns[0]!.trigger('click');
|
await deleteBtn!.trigger('click');
|
||||||
await nextTick();
|
await nextTick();
|
||||||
const items = w.findAllComponents(TagsInputItem as Component);
|
const items = w.findAllComponents(TagsInputItem as Component);
|
||||||
expect(items).toHaveLength(1);
|
expect(items).toHaveLength(1);
|
||||||
|
|||||||
Reference in New Issue
Block a user