fix(primitives): eslint/tsconfig migration, asChild refactor, type fixes

- Migrate to eslint flat config + composite tsconfig.
- Complete the asChild→as="template" refactor (remove asChild prop + :as-child
  bindings across components, matching Primitive's slot model).
- Fix test type errors and source type-safety (useGraceArea hull/point math,
  FocusScope/util ref typing).

Note: ~53 vue-tsc errors remain (HTML attr/event passthrough typing on
transparent wrapper components + a couple of duplicate-export naming
collisions) — not gated by CI (build/lint/test green); pending a
component-attribute-typing design decision.
This commit is contained in:
2026-06-07 16:29:56 +07:00
parent c7644ade69
commit 626fbc70d8
408 changed files with 27367 additions and 154 deletions
@@ -0,0 +1,56 @@
import type { VueWrapper } from '@vue/test-utils';
import { mount } from '@vue/test-utils';
import { afterEach, describe, expect, it } from 'vitest';
import { defineComponent, h } from 'vue';
import { NavigationMenuList, NavigationMenuRoot } from '../index';
const wrappers: Array<VueWrapper<any>> = [];
afterEach(() => {
while (wrappers.length) wrappers.pop()!.unmount();
document.body.innerHTML = '';
});
function track<T extends VueWrapper<any>>(w: T): T {
wrappers.push(w);
return w;
}
function mountRoot(attrs: Record<string, unknown> = {}) {
const Harness = defineComponent({
setup() {
return () => h(NavigationMenuRoot, attrs, {
default: () => h(NavigationMenuList),
});
},
});
return track(mount(Harness, { attachTo: document.body }));
}
describe('navigation-menu — root landmark a11y', () => {
it('renders a <nav> element (implicit role=navigation)', () => {
mountRoot();
const nav = document.querySelector('nav');
expect(nav).toBeTruthy();
// <nav> has implicit role="navigation" — no explicit role attribute needed.
});
it('falls back to aria-label="Main" when no label is supplied', () => {
mountRoot();
const nav = document.querySelector('nav') as HTMLElement;
expect(nav.getAttribute('aria-label')).toBe('Main');
});
it('honours a user-supplied aria-label', () => {
mountRoot({ 'aria-label': 'Primary site navigation' });
const nav = document.querySelector('nav') as HTMLElement;
expect(nav.getAttribute('aria-label')).toBe('Primary site navigation');
});
it('exposes data-orientation matching the orientation prop', () => {
mountRoot({ orientation: 'vertical' });
const nav = document.querySelector('nav') as HTMLElement;
expect(nav.getAttribute('data-orientation')).toBe('vertical');
});
});