Migrate navigation components to Zod props
This commit is contained in:
parent
fdd12f95c6
commit
c0019d57e7
17
packages/core/src/components/accordion/accordion.props.ts
Normal file
17
packages/core/src/components/accordion/accordion.props.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { z } from "zod/v4";
|
||||||
|
import type { JSX } from "solid-js";
|
||||||
|
import type { ComponentMeta } from "../../meta";
|
||||||
|
export const AccordionRootPropsSchema = z.object({
|
||||||
|
value: z.union([z.string(), z.array(z.string())]).optional().describe("Controlled expanded item value(s)"),
|
||||||
|
defaultValue: z.union([z.string(), z.array(z.string())]).optional().describe("Initial expanded item value(s) when uncontrolled"),
|
||||||
|
multiple: z.boolean().optional().describe("Allow multiple items to be expanded simultaneously"),
|
||||||
|
collapsible: z.boolean().optional().describe("Allow the currently open item to be closed by clicking it again"),
|
||||||
|
disabled: z.boolean().optional().describe("Disable all accordion items"),
|
||||||
|
});
|
||||||
|
export interface AccordionRootProps extends z.infer<typeof AccordionRootPropsSchema>, Omit<JSX.HTMLAttributes<HTMLDivElement>, keyof z.infer<typeof AccordionRootPropsSchema>> { onValueChange?: (value: string | string[]) => void; children: JSX.Element; }
|
||||||
|
export const AccordionMeta: ComponentMeta = {
|
||||||
|
name: "Accordion",
|
||||||
|
description: "Vertically stacked sections that expand/collapse to show content one at a time or multiple",
|
||||||
|
parts: ["Root", "Item", "Header", "Trigger", "Content"] as const,
|
||||||
|
requiredParts: ["Root", "Item", "Trigger", "Content"] as const,
|
||||||
|
} as const;
|
||||||
@ -4,18 +4,10 @@ import { AccordionHeader } from "./accordion-header";
|
|||||||
import { AccordionItem } from "./accordion-item";
|
import { AccordionItem } from "./accordion-item";
|
||||||
import { AccordionRoot } from "./accordion-root";
|
import { AccordionRoot } from "./accordion-root";
|
||||||
import { AccordionTrigger } from "./accordion-trigger";
|
import { AccordionTrigger } from "./accordion-trigger";
|
||||||
|
export { AccordionRootPropsSchema, AccordionItemPropsSchema, AccordionMeta } from "./accordion.props";
|
||||||
export const Accordion = Object.assign(AccordionRoot, {
|
export type { AccordionRootProps, AccordionItemProps } from "./accordion.props";
|
||||||
Item: AccordionItem,
|
|
||||||
Header: AccordionHeader,
|
|
||||||
Trigger: AccordionTrigger,
|
|
||||||
Content: AccordionContent,
|
|
||||||
useContext: useAccordionRootContext,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type { AccordionRootProps } from "./accordion-root";
|
|
||||||
export type { AccordionItemProps } from "./accordion-item";
|
|
||||||
export type { AccordionHeaderProps } from "./accordion-header";
|
export type { AccordionHeaderProps } from "./accordion-header";
|
||||||
export type { AccordionTriggerProps } from "./accordion-trigger";
|
export type { AccordionTriggerProps } from "./accordion-trigger";
|
||||||
export type { AccordionContentProps } from "./accordion-content";
|
export type { AccordionContentProps } from "./accordion-content";
|
||||||
export type { AccordionRootContextValue, AccordionItemContextValue } from "./accordion-context";
|
export type { AccordionRootContextValue, AccordionItemContextValue } from "./accordion-context";
|
||||||
|
export const Accordion = Object.assign(AccordionRoot, { Item: AccordionItem, Header: AccordionHeader, Trigger: AccordionTrigger, Content: AccordionContent, useContext: useAccordionRootContext });
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
import { z } from "zod/v4";
|
||||||
|
import type { JSX } from "solid-js";
|
||||||
|
import type { ComponentMeta } from "../../meta";
|
||||||
|
export const AlertDialogRootPropsSchema = z.object({
|
||||||
|
open: z.boolean().optional().describe("Controlled open state"),
|
||||||
|
defaultOpen: z.boolean().optional().describe("Initial open state when uncontrolled"),
|
||||||
|
});
|
||||||
|
export interface AlertDialogRootProps extends z.infer<typeof AlertDialogRootPropsSchema> { onOpenChange?: (open: boolean) => void; children: JSX.Element; }
|
||||||
|
export const AlertDialogMeta: ComponentMeta = {
|
||||||
|
name: "AlertDialog",
|
||||||
|
description: "Modal dialog for critical confirmations that requires explicit user action to dismiss",
|
||||||
|
parts: ["Root", "Trigger", "Portal", "Overlay", "Content", "Title", "Description", "Cancel", "Action"] as const,
|
||||||
|
requiredParts: ["Root", "Content", "Title", "Action"] as const,
|
||||||
|
} as const;
|
||||||
@ -8,26 +8,7 @@ import { AlertDialogPortal } from "./alert-dialog-portal";
|
|||||||
import { AlertDialogRoot } from "./alert-dialog-root";
|
import { AlertDialogRoot } from "./alert-dialog-root";
|
||||||
import { AlertDialogTitle } from "./alert-dialog-title";
|
import { AlertDialogTitle } from "./alert-dialog-title";
|
||||||
import { AlertDialogTrigger } from "./alert-dialog-trigger";
|
import { AlertDialogTrigger } from "./alert-dialog-trigger";
|
||||||
|
export { AlertDialogRootPropsSchema, AlertDialogMeta } from "./alert-dialog.props";
|
||||||
export const AlertDialog = Object.assign(AlertDialogRoot, {
|
export type { AlertDialogRootProps } from "./alert-dialog.props";
|
||||||
Content: AlertDialogContent,
|
export type { AlertDialogContentProps, AlertDialogTitleProps, AlertDialogDescriptionProps, AlertDialogTriggerProps, AlertDialogCancelProps, AlertDialogActionProps, AlertDialogPortalProps, AlertDialogOverlayProps, AlertDialogContextValue } from "./alert-dialog-content";
|
||||||
Title: AlertDialogTitle,
|
export const AlertDialog = Object.assign(AlertDialogRoot, { Content: AlertDialogContent, Title: AlertDialogTitle, Description: AlertDialogDescription, Trigger: AlertDialogTrigger, Cancel: AlertDialogCancel, Action: AlertDialogAction, Portal: AlertDialogPortal, Overlay: AlertDialogOverlay, useContext: useAlertDialogContext });
|
||||||
Description: AlertDialogDescription,
|
|
||||||
Trigger: AlertDialogTrigger,
|
|
||||||
Cancel: AlertDialogCancel,
|
|
||||||
Action: AlertDialogAction,
|
|
||||||
Portal: AlertDialogPortal,
|
|
||||||
Overlay: AlertDialogOverlay,
|
|
||||||
useContext: useAlertDialogContext,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type { AlertDialogRootProps } from "./alert-dialog-root";
|
|
||||||
export type { AlertDialogContentProps } from "./alert-dialog-content";
|
|
||||||
export type { AlertDialogTitleProps } from "./alert-dialog-title";
|
|
||||||
export type { AlertDialogDescriptionProps } from "./alert-dialog-description";
|
|
||||||
export type { AlertDialogTriggerProps } from "./alert-dialog-trigger";
|
|
||||||
export type { AlertDialogCancelProps } from "./alert-dialog-cancel";
|
|
||||||
export type { AlertDialogActionProps } from "./alert-dialog-action";
|
|
||||||
export type { AlertDialogPortalProps } from "./alert-dialog-portal";
|
|
||||||
export type { AlertDialogOverlayProps } from "./alert-dialog-overlay";
|
|
||||||
export type { AlertDialogContextValue } from "./alert-dialog-context";
|
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { z } from "zod/v4";
|
||||||
|
import type { JSX } from "solid-js";
|
||||||
|
import type { ComponentMeta } from "../../meta";
|
||||||
|
export const BreadcrumbsRootPropsSchema = z.object({
|
||||||
|
separator: z.string().optional().describe("Custom separator string rendered between breadcrumb items"),
|
||||||
|
});
|
||||||
|
export interface BreadcrumbsRootProps extends z.infer<typeof BreadcrumbsRootPropsSchema>, Omit<JSX.HTMLAttributes<HTMLElement>, keyof z.infer<typeof BreadcrumbsRootPropsSchema>> { children: JSX.Element; }
|
||||||
|
export const BreadcrumbsMeta: ComponentMeta = {
|
||||||
|
name: "Breadcrumbs",
|
||||||
|
description: "Navigation trail showing the current page location within a hierarchy",
|
||||||
|
parts: ["Root", "Item", "Link", "Separator"] as const,
|
||||||
|
requiredParts: ["Root", "Item"] as const,
|
||||||
|
} as const;
|
||||||
@ -2,15 +2,7 @@ import { BreadcrumbsItem } from "./breadcrumbs-item";
|
|||||||
import { BreadcrumbsLink } from "./breadcrumbs-link";
|
import { BreadcrumbsLink } from "./breadcrumbs-link";
|
||||||
import { BreadcrumbsRoot } from "./breadcrumbs-root";
|
import { BreadcrumbsRoot } from "./breadcrumbs-root";
|
||||||
import { BreadcrumbsSeparator } from "./breadcrumbs-separator";
|
import { BreadcrumbsSeparator } from "./breadcrumbs-separator";
|
||||||
|
export { BreadcrumbsRootPropsSchema, BreadcrumbsMeta } from "./breadcrumbs.props";
|
||||||
/** Compound breadcrumbs component with Item, Link, and Separator sub-components. */
|
export type { BreadcrumbsRootProps } from "./breadcrumbs.props";
|
||||||
export const Breadcrumbs = Object.assign(BreadcrumbsRoot, {
|
export type { BreadcrumbsItemProps, BreadcrumbsLinkProps, BreadcrumbsSeparatorProps } from "./breadcrumbs-item";
|
||||||
Item: BreadcrumbsItem,
|
export const Breadcrumbs = Object.assign(BreadcrumbsRoot, { Item: BreadcrumbsItem, Link: BreadcrumbsLink, Separator: BreadcrumbsSeparator });
|
||||||
Link: BreadcrumbsLink,
|
|
||||||
Separator: BreadcrumbsSeparator,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type { BreadcrumbsRootProps } from "./breadcrumbs-root";
|
|
||||||
export type { BreadcrumbsItemProps } from "./breadcrumbs-item";
|
|
||||||
export type { BreadcrumbsLinkProps } from "./breadcrumbs-link";
|
|
||||||
export type { BreadcrumbsSeparatorProps } from "./breadcrumbs-separator";
|
|
||||||
|
|||||||
@ -0,0 +1,15 @@
|
|||||||
|
import { z } from "zod/v4";
|
||||||
|
import type { JSX } from "solid-js";
|
||||||
|
import type { ComponentMeta } from "../../meta";
|
||||||
|
export const CollapsibleRootPropsSchema = z.object({
|
||||||
|
open: z.boolean().optional().describe("Controlled open state"),
|
||||||
|
defaultOpen: z.boolean().optional().describe("Initial open state when uncontrolled"),
|
||||||
|
disabled: z.boolean().optional().describe("Prevent the collapsible from being toggled"),
|
||||||
|
});
|
||||||
|
export interface CollapsibleRootProps extends z.infer<typeof CollapsibleRootPropsSchema>, Omit<JSX.HTMLAttributes<HTMLDivElement>, keyof z.infer<typeof CollapsibleRootPropsSchema>> { onOpenChange?: (open: boolean) => void; children: JSX.Element; }
|
||||||
|
export const CollapsibleMeta: ComponentMeta = {
|
||||||
|
name: "Collapsible",
|
||||||
|
description: "Content section that can be expanded or collapsed with a trigger",
|
||||||
|
parts: ["Root", "Trigger", "Content"] as const,
|
||||||
|
requiredParts: ["Root", "Trigger", "Content"] as const,
|
||||||
|
} as const;
|
||||||
@ -2,14 +2,9 @@ import { CollapsibleContent } from "./collapsible-content";
|
|||||||
import { useCollapsibleContext } from "./collapsible-context";
|
import { useCollapsibleContext } from "./collapsible-context";
|
||||||
import { CollapsibleRoot } from "./collapsible-root";
|
import { CollapsibleRoot } from "./collapsible-root";
|
||||||
import { CollapsibleTrigger } from "./collapsible-trigger";
|
import { CollapsibleTrigger } from "./collapsible-trigger";
|
||||||
|
export { CollapsibleRootPropsSchema, CollapsibleMeta } from "./collapsible.props";
|
||||||
export const Collapsible = Object.assign(CollapsibleRoot, {
|
export type { CollapsibleRootProps } from "./collapsible.props";
|
||||||
Trigger: CollapsibleTrigger,
|
|
||||||
Content: CollapsibleContent,
|
|
||||||
useContext: useCollapsibleContext,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type { CollapsibleRootProps } from "./collapsible-root";
|
|
||||||
export type { CollapsibleTriggerProps } from "./collapsible-trigger";
|
export type { CollapsibleTriggerProps } from "./collapsible-trigger";
|
||||||
export type { CollapsibleContentProps } from "./collapsible-content";
|
export type { CollapsibleContentProps } from "./collapsible-content";
|
||||||
export type { CollapsibleContextValue } from "./collapsible-context";
|
export type { CollapsibleContextValue } from "./collapsible-context";
|
||||||
|
export const Collapsible = Object.assign(CollapsibleRoot, { Trigger: CollapsibleTrigger, Content: CollapsibleContent, useContext: useCollapsibleContext });
|
||||||
|
|||||||
@ -3,16 +3,7 @@ import { TabsList } from "./tabs-list";
|
|||||||
import { TabsPanel } from "./tabs-panel";
|
import { TabsPanel } from "./tabs-panel";
|
||||||
import { TabsRoot } from "./tabs-root";
|
import { TabsRoot } from "./tabs-root";
|
||||||
import { TabsTab } from "./tabs-tab";
|
import { TabsTab } from "./tabs-tab";
|
||||||
|
export { TabsRootPropsSchema, TabsMeta } from "./tabs.props";
|
||||||
export const Tabs = Object.assign(TabsRoot, {
|
export type { TabsRootProps } from "./tabs.props";
|
||||||
List: TabsList,
|
export type { TabsListProps, TabsTabProps, TabsPanelProps, TabsContextValue } from "./tabs-list";
|
||||||
Tab: TabsTab,
|
export const Tabs = Object.assign(TabsRoot, { List: TabsList, Tab: TabsTab, Panel: TabsPanel, useContext: useTabsContext });
|
||||||
Panel: TabsPanel,
|
|
||||||
useContext: useTabsContext,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type { TabsRootProps } from "./tabs-root";
|
|
||||||
export type { TabsListProps } from "./tabs-list";
|
|
||||||
export type { TabsTabProps } from "./tabs-tab";
|
|
||||||
export type { TabsPanelProps } from "./tabs-panel";
|
|
||||||
export type { TabsContextValue } from "./tabs-context";
|
|
||||||
|
|||||||
17
packages/core/src/components/tabs/tabs.props.ts
Normal file
17
packages/core/src/components/tabs/tabs.props.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { z } from "zod/v4";
|
||||||
|
import type { JSX } from "solid-js";
|
||||||
|
import type { ComponentMeta } from "../../meta";
|
||||||
|
export const TabsRootPropsSchema = z.object({
|
||||||
|
value: z.string().optional().describe("Controlled active tab value"),
|
||||||
|
defaultValue: z.string().optional().describe("Initial active tab value when uncontrolled"),
|
||||||
|
orientation: z.enum(["horizontal", "vertical"]).optional().describe("Layout orientation of the tab list. Defaults to horizontal"),
|
||||||
|
activationMode: z.enum(["automatic", "manual"]).optional().describe("automatic: activates on focus; manual: activates on click or Enter/Space. Defaults to automatic"),
|
||||||
|
disabled: z.boolean().optional().describe("Disable all tabs"),
|
||||||
|
});
|
||||||
|
export interface TabsRootProps extends z.infer<typeof TabsRootPropsSchema>, Omit<JSX.HTMLAttributes<HTMLDivElement>, keyof z.infer<typeof TabsRootPropsSchema>> { onValueChange?: (value: string) => void; children: JSX.Element; }
|
||||||
|
export const TabsMeta: ComponentMeta = {
|
||||||
|
name: "Tabs",
|
||||||
|
description: "Tabbed interface for switching between different views or sections of content",
|
||||||
|
parts: ["Root", "List", "Trigger", "Content"] as const,
|
||||||
|
requiredParts: ["Root", "List", "Trigger", "Content"] as const,
|
||||||
|
} as const;
|
||||||
28
packages/core/tests/schemas/disclosure-components.test.ts
Normal file
28
packages/core/tests/schemas/disclosure-components.test.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { AccordionRootPropsSchema, AccordionMeta } from "../../src/components/accordion/accordion.props";
|
||||||
|
import { CollapsibleRootPropsSchema, CollapsibleMeta } from "../../src/components/collapsible/collapsible.props";
|
||||||
|
import { AlertDialogRootPropsSchema, AlertDialogMeta } from "../../src/components/alert-dialog/alert-dialog.props";
|
||||||
|
import { BreadcrumbsRootPropsSchema, BreadcrumbsMeta } from "../../src/components/breadcrumbs/breadcrumbs.props";
|
||||||
|
import { TabsRootPropsSchema, TabsMeta } from "../../src/components/tabs/tabs.props";
|
||||||
|
describe("Disclosure component schemas", () => {
|
||||||
|
it("Accordion validates", () => {
|
||||||
|
expect(AccordionRootPropsSchema.safeParse({ value: "item-1" }).success).toBe(true);
|
||||||
|
expect(AccordionRootPropsSchema.safeParse({ value: ["item-1", "item-2"], multiple: true }).success).toBe(true);
|
||||||
|
});
|
||||||
|
it("Collapsible validates", () => { expect(CollapsibleRootPropsSchema.safeParse({ open: true, disabled: false }).success).toBe(true); });
|
||||||
|
it("AlertDialog validates", () => { expect(AlertDialogRootPropsSchema.safeParse({ open: false }).success).toBe(true); });
|
||||||
|
it("Breadcrumbs validates", () => { expect(BreadcrumbsRootPropsSchema.safeParse({ separator: ">" }).success).toBe(true); });
|
||||||
|
it("Tabs validates", () => {
|
||||||
|
expect(TabsRootPropsSchema.safeParse({ value: "tab1", orientation: "vertical", activationMode: "manual" }).success).toBe(true);
|
||||||
|
expect(TabsRootPropsSchema.safeParse({ orientation: "diagonal" }).success).toBe(false);
|
||||||
|
});
|
||||||
|
it("all Meta objects valid", () => {
|
||||||
|
const metas = [AccordionMeta, CollapsibleMeta, AlertDialogMeta, BreadcrumbsMeta, TabsMeta];
|
||||||
|
for (const meta of metas) {
|
||||||
|
expect(meta.name).toBeTruthy();
|
||||||
|
expect(meta.description).toBeTruthy();
|
||||||
|
expect(meta.parts.length).toBeGreaterThan(0);
|
||||||
|
expect(meta.requiredParts.length).toBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
41
packages/core/tests/schemas/form-components.test.ts
Normal file
41
packages/core/tests/schemas/form-components.test.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { TextFieldRootPropsSchema, TextFieldMeta } from "../../src/components/text-field/text-field.props";
|
||||||
|
import { CheckboxRootPropsSchema, CheckboxMeta } from "../../src/components/checkbox/checkbox.props";
|
||||||
|
import { SwitchRootPropsSchema, SwitchMeta } from "../../src/components/switch/switch.props";
|
||||||
|
import { RadioGroupRootPropsSchema, RadioGroupMeta } from "../../src/components/radio-group/radio-group.props";
|
||||||
|
import { SliderRootPropsSchema, SliderMeta } from "../../src/components/slider/slider.props";
|
||||||
|
import { NumberFieldRootPropsSchema, NumberFieldMeta } from "../../src/components/number-field/number-field.props";
|
||||||
|
import { ToggleGroupRootPropsSchema, ToggleGroupMeta } from "../../src/components/toggle-group/toggle-group.props";
|
||||||
|
describe("Form component schemas", () => {
|
||||||
|
it("TextField validates", () => {
|
||||||
|
expect(TextFieldRootPropsSchema.safeParse({ value: "hello", disabled: false }).success).toBe(true);
|
||||||
|
});
|
||||||
|
it("Checkbox validates", () => {
|
||||||
|
expect(CheckboxRootPropsSchema.safeParse({ checked: true, name: "agree" }).success).toBe(true);
|
||||||
|
});
|
||||||
|
it("Switch validates", () => {
|
||||||
|
expect(SwitchRootPropsSchema.safeParse({ checked: false }).success).toBe(true);
|
||||||
|
});
|
||||||
|
it("RadioGroup validates", () => {
|
||||||
|
expect(RadioGroupRootPropsSchema.safeParse({ value: "opt1", orientation: "horizontal" }).success).toBe(true);
|
||||||
|
expect(RadioGroupRootPropsSchema.safeParse({ orientation: "invalid" }).success).toBe(false);
|
||||||
|
});
|
||||||
|
it("Slider validates", () => {
|
||||||
|
expect(SliderRootPropsSchema.safeParse({ value: [25, 75], min: 0, max: 100, step: 5 }).success).toBe(true);
|
||||||
|
});
|
||||||
|
it("NumberField validates", () => {
|
||||||
|
expect(NumberFieldRootPropsSchema.safeParse({ value: 42, min: 0, max: 100 }).success).toBe(true);
|
||||||
|
});
|
||||||
|
it("ToggleGroup validates", () => {
|
||||||
|
expect(ToggleGroupRootPropsSchema.safeParse({ value: "a" }).success).toBe(true);
|
||||||
|
expect(ToggleGroupRootPropsSchema.safeParse({ value: ["a", "b"], multiple: true }).success).toBe(true);
|
||||||
|
});
|
||||||
|
it("all Meta objects valid", () => {
|
||||||
|
const metas = [TextFieldMeta, CheckboxMeta, SwitchMeta, RadioGroupMeta, SliderMeta, NumberFieldMeta, ToggleGroupMeta];
|
||||||
|
for (const meta of metas) {
|
||||||
|
expect(meta.name).toBeTruthy();
|
||||||
|
expect(meta.description).toBeTruthy();
|
||||||
|
expect(meta.parts.length).toBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user