UNPKG

alouette

Version:

A modern, customizable design system built on top of NativeWind v5 with configurable defaults

192 lines (175 loc) 6.01 kB
/* eslint-disable import-x/no-extraneous-dependencies, no-template-curly-in-string */ import ts from "typescript"; import { describe, expect, it } from "vitest"; import { transformSurfaceAndStacks } from "./surface-and-stacks.mjs"; function run(text, { filePath = "/app/src/Screen.tsx", groups } = {}) { return transformSurfaceAndStacks(text, filePath, ts, { groups }); } describe("stacks", () => { it("turns HStack into a flex-row View and VStack into a View", () => { const { output } = run(`import { HStack, Text, VStack } from "alouette"; export function Screen() { return ( <VStack className="gap-m"> <HStack className="items-center"> <Text>Hi</Text> </HStack> <HStack /> </VStack> ); } `); expect(output).toBe(`import { Text, View } from "alouette"; export function Screen() { return ( <View className="gap-m"> <View className="flex-row items-center"> <Text>Hi</Text> </View> <View className="flex-row" /> </View> ); } `); }); it("keeps a direction the caller already wrote instead of adding flex-row", () => { const { output } = run(`import { HStack, Stack } from "alouette"; <HStack className="flex-col md:flex-row" />; <Stack className="flex-nowrap" />; `); expect(output).toBe(`import { View } from "alouette"; <View className="flex-col md:flex-row" />; <View className="flex-row flex-nowrap" />; `); }); it("prepends to a dynamic className and warns", () => { const { output, warnings } = run(`import { HStack } from "alouette"; <HStack className={styles.row()} />; `); expect(output).toContain('className={`flex-row ${styles.row() ?? ""}`}'); expect(warnings).toHaveLength(1); expect(warnings[0].line).toBe(2); }); it("reuses a View that is already imported", () => { const { output } = run(`import { View } from "react-native"; import { VStack } from "alouette"; <VStack><View /></VStack>; `); expect(output).toBe(`import { View } from "react-native"; <View><View /></View>; `); }); it("renames the props types and non-JSX references", () => { const { output } = run(`import { HStack, type HStackProps } from "alouette"; type Meta = { component: typeof HStack }; function Row(props: HStackProps) {} `); expect(output).toBe(`import { View, type ViewProps } from "alouette"; type Meta = { component: typeof View }; function Row(props: ViewProps) {} `); }); it("points an internal relative import at primitives/View", () => { const { output } = run( `import { Text } from "../primitives/Text"; import { HStack, VStack } from "../stacks/stacks"; <VStack><HStack /></VStack>; `, { filePath: "/repo/packages/alouette/src/ui/data/Card.tsx" }, ); expect(output).toBe(`import { Text } from "../primitives/Text"; import { View } from "../primitives/View"; <View><View className="flex-row" /></View>; `); }); }); describe("surface", () => { it("maps the props to utilities in size, variant, shadow order", () => { const { output } = run(`import { Surface } from "alouette"; <Surface shadow="l" variant="lowered" size="sm" className="gap-m" accent="info"> x </Surface>; <Surface />; <Surface variant="surface" />; `); expect(output).toBe(`import { Box } from "alouette"; <Box className="surface surface-sm lowered shadow-l gap-m" accent="info"> x </Box>; <Box className="surface" />; <Box className="surface" />; `); }); it("leaves a Surface with a dynamic prop untouched and keeps its import", () => { const { output, warnings } = run(`import { Surface } from "alouette"; <Surface size={size} />; <Surface size="xs" />; `); expect(output).toBe(`import { Surface, Box } from "alouette"; <Surface size={size} />; <Box className="surface surface-xs" />; `); expect(warnings).toEqual([ { line: 2, message: "Surface size is not a static, known value: this Surface is left as is", }, ]); }); it("points an internal relative import at containers/Box", () => { const { output } = run( `import { Surface } from "./Surface"; <Surface size="lg" />; `, { filePath: "/repo/packages/alouette/src/ui/containers/Card.tsx" }, ); expect(output).toBe(`import { Box } from "./Box"; <Box className="surface surface-lg" />; `); }); it("runs one group only", () => { const { output } = run( `import { HStack, Surface } from "alouette"; <Surface><HStack /></Surface>; `, { groups: ["surface"] }, ); expect(output).toBe(`import { HStack, Box } from "alouette"; <Box className="surface"><HStack /></Box>; `); }); it("renames the editable sections, generic tag and props types included", () => { const { output } = run(`import { EditableSurface, FormEditableSurface, type FormEditableSurfaceProps, } from "alouette"; type Props = FormEditableSurfaceProps<Values>; <EditableSurface title="t" className="lowered" />; <FormEditableSurface<Values> title="t">x</FormEditableSurface>; `); expect(output) .toBe(`import { EditableSection, FormEditableSection, type FormEditableSectionProps } from "alouette"; type Props = FormEditableSectionProps<Values>; <EditableSection title="t" className="surface lowered" />; <FormEditableSection<Values> className="surface" title="t">x</FormEditableSection>; `); }); it("points internal editable imports at the renamed modules", () => { const { output } = run( `import { EditableSurface, type EditableSurfaceProps } from "../containers/EditableSurface"; <EditableSurface />; `, { filePath: "/repo/packages/alouette/src/ui/forms/FormCard.tsx" }, ); expect(output) .toBe(`import { EditableSection, type EditableSectionProps } from "../containers/EditableSection"; <EditableSection className="surface" />; `); }); it("leaves a file without the components as is", () => { const text = 'import { Box } from "alouette";\n<Box />;\n'; expect(run(text)).toEqual({ output: text, changed: false, warnings: [] }); }); });