72 lines
3.3 KiB
TypeScript
72 lines
3.3 KiB
TypeScript
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
|
// A screen is a declaration: data from `load`, columns, actions and labels, handed to the kit's patterns
|
|
// (docs/how-to/compose-a-page.md in the platform's documentation). So a screen has no styling of its own: no `className`, no
|
|
// colour value. React Aria is out of a screen's reach too, by the `ui-comes-from-the-kit` rule of
|
|
// scripts/dep-rules.cjs. A screen that is truly domain-specific says so, and why, in its first lines:
|
|
// `// kit:custom-ui <why>`. `pnpm boundaries` and this suite read the same marker.
|
|
|
|
import { readFileSync } from "node:fs"
|
|
import { createRequire } from "node:module"
|
|
import { join } from "node:path"
|
|
import { describe, expect, it } from "vitest"
|
|
|
|
const require = createRequire(import.meta.url)
|
|
const { buildForbidden, listCustomScreens, listScreens } = require("../scripts/dep-rules.cjs") as {
|
|
buildForbidden(
|
|
moduleIds: string[],
|
|
custom?: string[],
|
|
): { name: string; from: { path?: string; pathNot?: string | string[] }; to: { path?: string | string[] } }[]
|
|
listCustomScreens(root: string): string[]
|
|
listScreens(root: string): string[]
|
|
}
|
|
|
|
const root = new URL("..", import.meta.url).pathname
|
|
const COLOUR = /["'`](?:#[0-9a-fA-F]{3,8}|rgba?\(|hsla?\(|oklch\()/
|
|
|
|
/** What a declared screen may not hold, by file, with the way out in the message. */
|
|
function styling(source: string): string[] {
|
|
const found: string[] = []
|
|
if (/<(?:button|input|select|textarea|table)(?:\s|>)/.test(source))
|
|
found.push("raw control: compose kit primitives or patterns")
|
|
if (/\bclassName=/.test(source)) found.push("className: use a pattern's slot, or a variant")
|
|
if (COLOUR.test(source)) found.push("a colour value: the kit's roles carry every colour")
|
|
return found
|
|
}
|
|
|
|
describe("a screen is a declaration", () => {
|
|
const custom = new Set(listCustomScreens(root))
|
|
|
|
it("holds no styling of its own unless it declares itself custom", () => {
|
|
const problems = listScreens(root)
|
|
.filter((file) => !custom.has(file))
|
|
.flatMap((file) =>
|
|
styling(readFileSync(join(root, file), "utf8")).map(
|
|
(problem) => `${file}: ${problem}, or \`// kit:custom-ui <why>\``,
|
|
),
|
|
)
|
|
expect(problems).toEqual([])
|
|
})
|
|
|
|
it("catches what it means to", () => {
|
|
expect(styling(`<p className="x" />`)).toHaveLength(1)
|
|
expect(styling(`const c = "#123abc"`)).toHaveLength(1)
|
|
expect(styling(`const c = "oklch(0.5 0 0)"`)).toHaveLength(1)
|
|
expect(styling(`<a href="#section" />`)).toEqual([])
|
|
expect(styling(`<button type="button" />`)).toHaveLength(1)
|
|
expect(styling(`<PageHeader title={title} />`)).toEqual([])
|
|
})
|
|
|
|
it("keeps React Aria behind the kit, for every screen that has not declared itself", () => {
|
|
const rule = buildForbidden([], ["modules/x/ui/own.tsx"]).find(({ name }) => name === "ui-comes-from-the-kit")
|
|
const target = new RegExp(rule?.to.path as string)
|
|
expect(target.test("react-aria-components")).toBe(true)
|
|
expect(target.test("node_modules/.pnpm/x/node_modules/@react-aria/button/dist/index.js")).toBe(true)
|
|
expect(target.test("@conusai-kit/ui/patterns")).toBe(false)
|
|
expect(rule?.from.pathNot).toEqual(["^modules/x/ui/own\\.tsx$"])
|
|
})
|
|
|
|
it("leaves custom screens the exception", () => {
|
|
expect(custom.size).toBeLessThan(listScreens(root).length / 2)
|
|
})
|
|
})
|