@dowel-ui/react 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +187 -0
  3. package/dist/components/ai-action-ledger/ai-action-ledger.d.ts +77 -0
  4. package/dist/components/ai-action-ledger/ai-action-ledger.d.ts.map +1 -0
  5. package/dist/components/ai-action-ledger/ai-action-ledger.js +224 -0
  6. package/dist/components/ai-action-ledger/ai-action-ledger.js.map +1 -0
  7. package/dist/components/ai-action-ledger/index.d.ts +2 -0
  8. package/dist/components/ai-action-ledger/index.js +2 -0
  9. package/dist/components/ai-action-ledger/meta.js +17 -0
  10. package/dist/components/ai-action-ledger/meta.js.map +1 -0
  11. package/dist/components/ai-agent-status/ai-agent-status.d.ts +1 -1
  12. package/dist/components/ai-inline-completion/ai-inline-completion.d.ts +45 -0
  13. package/dist/components/ai-inline-completion/ai-inline-completion.d.ts.map +1 -0
  14. package/dist/components/ai-inline-completion/ai-inline-completion.js +110 -0
  15. package/dist/components/ai-inline-completion/ai-inline-completion.js.map +1 -0
  16. package/dist/components/ai-inline-completion/index.d.ts +2 -0
  17. package/dist/components/ai-inline-completion/index.js +2 -0
  18. package/dist/components/ai-inline-completion/meta.js +17 -0
  19. package/dist/components/ai-inline-completion/meta.js.map +1 -0
  20. package/dist/components/ai-structured-output/ai-structured-output.d.ts +75 -0
  21. package/dist/components/ai-structured-output/ai-structured-output.d.ts.map +1 -0
  22. package/dist/components/ai-structured-output/ai-structured-output.js +126 -0
  23. package/dist/components/ai-structured-output/ai-structured-output.js.map +1 -0
  24. package/dist/components/ai-structured-output/index.d.ts +2 -0
  25. package/dist/components/ai-structured-output/index.js +2 -0
  26. package/dist/components/ai-structured-output/meta.js +17 -0
  27. package/dist/components/ai-structured-output/meta.js.map +1 -0
  28. package/dist/components/ai-tool/ai-tool.d.ts +1 -1
  29. package/dist/components/alert/alert.d.ts +1 -1
  30. package/dist/components/avatar/avatar.d.ts +1 -1
  31. package/dist/components/badge/badge.d.ts +1 -1
  32. package/dist/components/button/button.d.ts +1 -1
  33. package/dist/components/meter/index.d.ts +2 -0
  34. package/dist/components/meter/index.js +2 -0
  35. package/dist/components/meter/meta.js +17 -0
  36. package/dist/components/meter/meta.js.map +1 -0
  37. package/dist/components/meter/meter.d.ts +69 -0
  38. package/dist/components/meter/meter.d.ts.map +1 -0
  39. package/dist/components/meter/meter.js +144 -0
  40. package/dist/components/meter/meter.js.map +1 -0
  41. package/dist/components/metric-delta/index.d.ts +2 -0
  42. package/dist/components/metric-delta/index.js +2 -0
  43. package/dist/components/metric-delta/meta.js +17 -0
  44. package/dist/components/metric-delta/meta.js.map +1 -0
  45. package/dist/components/metric-delta/metric-delta.d.ts +59 -0
  46. package/dist/components/metric-delta/metric-delta.d.ts.map +1 -0
  47. package/dist/components/metric-delta/metric-delta.js +82 -0
  48. package/dist/components/metric-delta/metric-delta.js.map +1 -0
  49. package/dist/components/progress/progress.d.ts +1 -1
  50. package/dist/components/record-diff/index.d.ts +2 -0
  51. package/dist/components/record-diff/index.js +2 -0
  52. package/dist/components/record-diff/meta.js +17 -0
  53. package/dist/components/record-diff/meta.js.map +1 -0
  54. package/dist/components/record-diff/record-diff.d.ts +56 -0
  55. package/dist/components/record-diff/record-diff.d.ts.map +1 -0
  56. package/dist/components/record-diff/record-diff.js +199 -0
  57. package/dist/components/record-diff/record-diff.js.map +1 -0
  58. package/dist/components/spinner/spinner.d.ts +1 -1
  59. package/dist/components/toast/toast.d.ts +1 -1
  60. package/dist/index.d.ts +13 -1
  61. package/dist/index.js +7 -1
  62. package/dist/registry/components.d.ts.map +1 -1
  63. package/dist/registry/components.js +57 -45
  64. package/dist/registry/components.js.map +1 -1
  65. package/package.json +40 -5
  66. package/src/components/ai-action-ledger/ai-action-ledger.tsx +381 -0
  67. package/src/components/ai-action-ledger/index.ts +14 -0
  68. package/src/components/ai-action-ledger/meta.ts +19 -0
  69. package/src/components/ai-inline-completion/ai-inline-completion.tsx +214 -0
  70. package/src/components/ai-inline-completion/index.ts +1 -0
  71. package/src/components/ai-inline-completion/meta.ts +20 -0
  72. package/src/components/ai-structured-output/ai-structured-output.tsx +244 -0
  73. package/src/components/ai-structured-output/index.ts +10 -0
  74. package/src/components/ai-structured-output/meta.ts +18 -0
  75. package/src/components/meter/index.ts +8 -0
  76. package/src/components/meter/meta.ts +20 -0
  77. package/src/components/meter/meter.tsx +219 -0
  78. package/src/components/metric-delta/index.ts +6 -0
  79. package/src/components/metric-delta/meta.ts +18 -0
  80. package/src/components/metric-delta/metric-delta.tsx +180 -0
  81. package/src/components/record-diff/index.ts +10 -0
  82. package/src/components/record-diff/meta.ts +19 -0
  83. package/src/components/record-diff/record-diff.tsx +341 -0
  84. package/src/index.ts +6 -0
  85. package/src/registry/components.ts +12 -0
  86. package/src/blocks/admin-users/admin-users.stories.tsx +0 -83
  87. package/src/blocks/admin-users/admin-users.test.tsx +0 -159
  88. package/src/blocks/ai-chat/ai-chat.stories.tsx +0 -149
  89. package/src/blocks/ai-chat/ai-chat.test.tsx +0 -166
  90. package/src/blocks/dashboard/dashboard.stories.tsx +0 -106
  91. package/src/blocks/dashboard/dashboard.test.tsx +0 -94
  92. package/src/blocks/forgot-password/forgot-password.stories.tsx +0 -41
  93. package/src/blocks/forgot-password/forgot-password.test.tsx +0 -62
  94. package/src/blocks/login/login.stories.tsx +0 -46
  95. package/src/blocks/login/login.test.tsx +0 -100
  96. package/src/blocks/pricing/pricing.stories.tsx +0 -62
  97. package/src/blocks/pricing/pricing.test.tsx +0 -101
  98. package/src/blocks/settings/settings.stories.tsx +0 -94
  99. package/src/blocks/settings/settings.test.tsx +0 -167
  100. package/src/blocks/signup/signup.stories.tsx +0 -27
  101. package/src/blocks/signup/signup.test.tsx +0 -97
  102. package/src/components/accordion/accordion.stories.tsx +0 -90
  103. package/src/components/accordion/accordion.test.tsx +0 -156
  104. package/src/components/activity-feed/activity-feed.stories.tsx +0 -163
  105. package/src/components/activity-feed/activity-feed.test.tsx +0 -125
  106. package/src/components/ai-agent-status/ai-agent-status.stories.tsx +0 -86
  107. package/src/components/ai-agent-status/ai-agent-status.test.tsx +0 -66
  108. package/src/components/ai-conversation/ai-conversation.stories.tsx +0 -126
  109. package/src/components/ai-conversation/ai-conversation.test.tsx +0 -144
  110. package/src/components/ai-message/ai-message.stories.tsx +0 -106
  111. package/src/components/ai-message/ai-message.test.tsx +0 -160
  112. package/src/components/ai-model-selector/ai-model-selector.stories.tsx +0 -66
  113. package/src/components/ai-model-selector/ai-model-selector.test.tsx +0 -120
  114. package/src/components/ai-prompt-input/ai-prompt-input.stories.tsx +0 -145
  115. package/src/components/ai-prompt-input/ai-prompt-input.test.tsx +0 -273
  116. package/src/components/ai-reasoning/ai-reasoning.stories.tsx +0 -60
  117. package/src/components/ai-reasoning/ai-reasoning.test.tsx +0 -95
  118. package/src/components/ai-response/ai-response.stories.tsx +0 -75
  119. package/src/components/ai-response/ai-response.test.tsx +0 -96
  120. package/src/components/ai-sources/ai-sources.stories.tsx +0 -93
  121. package/src/components/ai-sources/ai-sources.test.tsx +0 -102
  122. package/src/components/ai-token-usage/ai-token-usage.stories.tsx +0 -59
  123. package/src/components/ai-token-usage/ai-token-usage.test.tsx +0 -83
  124. package/src/components/ai-tool/ai-tool.stories.tsx +0 -96
  125. package/src/components/ai-tool/ai-tool.test.tsx +0 -137
  126. package/src/components/alert/alert.stories.tsx +0 -101
  127. package/src/components/alert/alert.test.tsx +0 -94
  128. package/src/components/avatar/avatar.stories.tsx +0 -58
  129. package/src/components/avatar/avatar.test.tsx +0 -64
  130. package/src/components/badge/badge.stories.tsx +0 -68
  131. package/src/components/badge/badge.test.tsx +0 -61
  132. package/src/components/button/button.stories.tsx +0 -128
  133. package/src/components/button/button.test.tsx +0 -224
  134. package/src/components/calendar/calendar.stories.tsx +0 -105
  135. package/src/components/calendar/calendar.test.tsx +0 -255
  136. package/src/components/card/card.stories.tsx +0 -61
  137. package/src/components/card/card.test.tsx +0 -66
  138. package/src/components/checkbox/checkbox.stories.tsx +0 -124
  139. package/src/components/checkbox/checkbox.test.tsx +0 -121
  140. package/src/components/code-block/code-block.stories.tsx +0 -62
  141. package/src/components/code-block/code-block.test.tsx +0 -139
  142. package/src/components/combobox/combobox.stories.tsx +0 -171
  143. package/src/components/combobox/combobox.test.tsx +0 -429
  144. package/src/components/command/command.stories.tsx +0 -152
  145. package/src/components/command/command.test.tsx +0 -380
  146. package/src/components/data-table/data-table.stories.tsx +0 -151
  147. package/src/components/data-table/data-table.test.tsx +0 -288
  148. package/src/components/date-picker/date-picker.stories.tsx +0 -103
  149. package/src/components/date-picker/date-picker.test.tsx +0 -205
  150. package/src/components/dialog/dialog.stories.tsx +0 -140
  151. package/src/components/dialog/dialog.test.tsx +0 -186
  152. package/src/components/drawer/drawer.stories.tsx +0 -111
  153. package/src/components/drawer/drawer.test.tsx +0 -187
  154. package/src/components/dropdown-menu/dropdown-menu.stories.tsx +0 -128
  155. package/src/components/dropdown-menu/dropdown-menu.test.tsx +0 -321
  156. package/src/components/empty-state/empty-state.stories.tsx +0 -70
  157. package/src/components/empty-state/empty-state.test.tsx +0 -97
  158. package/src/components/form/form.stories.tsx +0 -144
  159. package/src/components/form/form.test.tsx +0 -203
  160. package/src/components/input/input.stories.tsx +0 -84
  161. package/src/components/input/input.test.tsx +0 -108
  162. package/src/components/label/label.stories.tsx +0 -51
  163. package/src/components/label/label.test.tsx +0 -55
  164. package/src/components/pagination/pagination.stories.tsx +0 -129
  165. package/src/components/pagination/pagination.test.tsx +0 -119
  166. package/src/components/popover/popover.stories.tsx +0 -84
  167. package/src/components/popover/popover.test.tsx +0 -163
  168. package/src/components/progress/progress.stories.tsx +0 -90
  169. package/src/components/progress/progress.test.tsx +0 -78
  170. package/src/components/radio-group/radio-group.stories.tsx +0 -102
  171. package/src/components/radio-group/radio-group.test.tsx +0 -152
  172. package/src/components/select/select.stories.tsx +0 -137
  173. package/src/components/select/select.test.tsx +0 -209
  174. package/src/components/separator/separator.stories.tsx +0 -42
  175. package/src/components/separator/separator.test.tsx +0 -46
  176. package/src/components/sheet/sheet.stories.tsx +0 -107
  177. package/src/components/sheet/sheet.test.tsx +0 -152
  178. package/src/components/skeleton/skeleton.stories.tsx +0 -46
  179. package/src/components/skeleton/skeleton.test.tsx +0 -57
  180. package/src/components/slider/slider.stories.tsx +0 -87
  181. package/src/components/slider/slider.test.tsx +0 -148
  182. package/src/components/spinner/spinner.stories.tsx +0 -47
  183. package/src/components/spinner/spinner.test.tsx +0 -41
  184. package/src/components/switch/switch.stories.tsx +0 -70
  185. package/src/components/switch/switch.test.tsx +0 -101
  186. package/src/components/table/table.stories.tsx +0 -131
  187. package/src/components/table/table.test.tsx +0 -153
  188. package/src/components/tabs/tabs.stories.tsx +0 -139
  189. package/src/components/tabs/tabs.test.tsx +0 -148
  190. package/src/components/toast/toast.stories.tsx +0 -167
  191. package/src/components/toast/toast.test.tsx +0 -306
  192. package/src/components/tooltip/tooltip.stories.tsx +0 -89
  193. package/src/components/tooltip/tooltip.test.tsx +0 -120
  194. package/src/lib/utils.test.ts +0 -32
  195. package/src/registry/meta.test.ts +0 -234
@@ -1,120 +0,0 @@
1
- import { render, screen, waitFor } from "@testing-library/react";
2
- import userEvent from "@testing-library/user-event";
3
- import { describe, expect, it } from "vitest";
4
-
5
- import { expectNoA11yViolations } from "../../../test/a11y";
6
- import { Button } from "../button";
7
- import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip";
8
-
9
- function Example({ delayDuration = 0 }: { delayDuration?: number } = {}) {
10
- return (
11
- <Tooltip delayDuration={delayDuration}>
12
- <TooltipTrigger asChild>
13
- <Button size="icon" aria-label="Add item">
14
- +
15
- </Button>
16
- </TooltipTrigger>
17
- <TooltipContent>Add item</TooltipContent>
18
- </Tooltip>
19
- );
20
- }
21
-
22
- describe("Tooltip", () => {
23
- it("is hidden until the trigger is hovered", async () => {
24
- const user = userEvent.setup();
25
- render(<Example />);
26
-
27
- expect(screen.queryByRole("tooltip")).not.toBeInTheDocument();
28
- await user.hover(screen.getByRole("button", { name: "Add item" }));
29
- expect(await screen.findByRole("tooltip")).toHaveTextContent("Add item");
30
- });
31
-
32
- it("opens on keyboard focus, not only on hover", async () => {
33
- const user = userEvent.setup();
34
- render(<Example />);
35
-
36
- await user.tab();
37
- expect(screen.getByRole("button", { name: "Add item" })).toHaveFocus();
38
- expect(await screen.findByRole("tooltip")).toBeInTheDocument();
39
- });
40
-
41
- it("closes when the pointer leaves", async () => {
42
- const user = userEvent.setup();
43
- // disableHoverableContent removes the grace area that normally lets the
44
- // pointer travel from trigger to tooltip. That grace area is a polygon
45
- // computed from measured rects, and jsdom reports every rect as zero-sized,
46
- // so leaving it enabled would exercise the stub rather than the behaviour.
47
- render(
48
- <Tooltip delayDuration={0} disableHoverableContent>
49
- <TooltipTrigger>Trigger</TooltipTrigger>
50
- <TooltipContent>Hint</TooltipContent>
51
- </Tooltip>,
52
- );
53
-
54
- const trigger = screen.getByText("Trigger");
55
- await user.hover(trigger);
56
- await screen.findByRole("tooltip");
57
-
58
- await user.unhover(trigger);
59
- await waitFor(() => {
60
- expect(screen.queryByRole("tooltip")).not.toBeInTheDocument();
61
- });
62
- });
63
-
64
- it("closes on Escape", async () => {
65
- const user = userEvent.setup();
66
- render(<Example />);
67
-
68
- await user.tab();
69
- await screen.findByRole("tooltip");
70
-
71
- await user.keyboard("{Escape}");
72
- await waitFor(() => {
73
- expect(screen.queryByRole("tooltip")).not.toBeInTheDocument();
74
- });
75
- });
76
-
77
- it("works without an explicit provider", async () => {
78
- const user = userEvent.setup();
79
- render(<Example />);
80
-
81
- await user.hover(screen.getByRole("button", { name: "Add item" }));
82
- expect(await screen.findByRole("tooltip")).toBeInTheDocument();
83
- });
84
-
85
- it("works inside a shared provider", async () => {
86
- const user = userEvent.setup();
87
- render(
88
- <TooltipProvider delayDuration={0}>
89
- <Example />
90
- </TooltipProvider>,
91
- );
92
-
93
- await user.hover(screen.getByRole("button", { name: "Add item" }));
94
- expect(await screen.findByRole("tooltip")).toBeInTheDocument();
95
- });
96
-
97
- it("merges a consumer className", async () => {
98
- const user = userEvent.setup();
99
- render(
100
- <Tooltip delayDuration={0}>
101
- <TooltipTrigger>Trigger</TooltipTrigger>
102
- <TooltipContent className="max-w-xs">Hint</TooltipContent>
103
- </Tooltip>,
104
- );
105
-
106
- await user.hover(screen.getByText("Trigger"));
107
- const tooltip = await screen.findByRole("tooltip");
108
- expect(tooltip).toHaveClass("max-w-xs");
109
- expect(tooltip).not.toHaveClass("max-w-64");
110
- });
111
-
112
- it("has no accessibility violations while open", async () => {
113
- const user = userEvent.setup();
114
- const { baseElement } = render(<Example />);
115
- await user.hover(screen.getByRole("button", { name: "Add item" }));
116
- await screen.findByRole("tooltip");
117
-
118
- await expectNoA11yViolations(baseElement);
119
- });
120
- });
@@ -1,32 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
-
3
- import { cn } from "./utils";
4
-
5
- describe("cn", () => {
6
- it("joins class names", () => {
7
- expect(cn("a", "b")).toBe("a b");
8
- });
9
-
10
- it("drops falsy values", () => {
11
- expect(cn("a", false, undefined, null, "b")).toBe("a b");
12
- });
13
-
14
- it("lets the later Tailwind utility win a conflict", () => {
15
- expect(cn("px-2", "px-4")).toBe("px-4");
16
- });
17
-
18
- it("resolves conflicts across our custom font-size scale", () => {
19
- // Without the extendTailwindMerge config this returns "text-sm text-2xs",
20
- // and a consumer's size override would silently do nothing.
21
- expect(cn("text-sm", "text-2xs")).toBe("text-2xs");
22
- expect(cn("text-2xs", "text-lg")).toBe("text-lg");
23
- });
24
-
25
- it("keeps non-conflicting utilities", () => {
26
- expect(cn("rounded-md border", "bg-primary")).toBe("rounded-md border bg-primary");
27
- });
28
-
29
- it("accepts arrays and objects", () => {
30
- expect(cn(["a", { b: true, c: false }])).toBe("a b");
31
- });
32
- });
@@ -1,234 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
-
3
- import { blockMetas } from "./blocks";
4
- import { componentMetas } from "./components";
5
- import { COMPONENT_CATEGORIES, COMPONENT_STATUSES, type ComponentMeta } from "./schema";
6
-
7
- /**
8
- * Registry integrity.
9
- *
10
- * A registry that lies about a component's dependencies produces a broken
11
- * install for every user who runs `add`, and the failure surfaces in *their*
12
- * project rather than ours. So the declared metadata is checked against the
13
- * imports actually present in the source on every test run.
14
- */
15
-
16
- const metaModules = {
17
- ...import.meta.glob<{ meta: ComponentMeta }>("../components/*/meta.ts", { eager: true }),
18
- ...import.meta.glob<{ meta: ComponentMeta }>("../blocks/*/meta.ts", { eager: true }),
19
- };
20
-
21
- const sourceFiles = {
22
- ...import.meta.glob<string>("../components/*/*.{ts,tsx}", {
23
- eager: true,
24
- query: "?raw",
25
- import: "default",
26
- }),
27
- ...import.meta.glob<string>("../blocks/*/*.{ts,tsx}", {
28
- eager: true,
29
- query: "?raw",
30
- import: "default",
31
- }),
32
- };
33
-
34
- const packageJson = import.meta.glob<string>("../../package.json", {
35
- eager: true,
36
- query: "?raw",
37
- import: "default",
38
- });
39
-
40
- const declaredNpmDependencies = new Set(
41
- Object.keys(
42
- (
43
- JSON.parse(Object.values(packageJson)[0] ?? "{}") as {
44
- dependencies?: Record<string, string>;
45
- }
46
- ).dependencies ?? {},
47
- ),
48
- );
49
-
50
- /** Provided by the consuming app, never installed by the CLI. */
51
- const PEER_PACKAGES = new Set(["react", "react-dom", "react/jsx-runtime"]);
52
-
53
- /** Written by `<cli> init`, so components may import it without declaring it. */
54
- const INSTALLED_BY_INIT = new Set(["@/lib/utils", "@/lib/styles"]);
55
-
56
- function componentDir(metaPath: string): string {
57
- return metaPath.replace(/\/meta\.ts$/, "");
58
- }
59
-
60
- function componentName(metaPath: string): string {
61
- return componentDir(metaPath).split("/").pop() ?? "";
62
- }
63
-
64
- /** Collects module specifiers from `import`/`export ... from` and side-effect imports. */
65
- function extractSpecifiers(source: string): string[] {
66
- const specifiers: string[] = [];
67
- for (const match of source.matchAll(/\bfrom\s+["']([^"']+)["']/g)) {
68
- if (match[1]) specifiers.push(match[1]);
69
- }
70
- for (const match of source.matchAll(/\bimport\s+["']([^"']+)["']/g)) {
71
- if (match[1]) specifiers.push(match[1]);
72
- }
73
- return specifiers;
74
- }
75
-
76
- /** `@scope/pkg/sub` -> `@scope/pkg`, `pkg/sub` -> `pkg`. */
77
- function packageNameOf(specifier: string): string {
78
- const segments = specifier.split("/");
79
- return specifier.startsWith("@")
80
- ? segments.slice(0, 2).join("/")
81
- : (segments[0] ?? specifier);
82
- }
83
-
84
- interface ResolvedImports {
85
- npm: Set<string>;
86
- registry: Set<string>;
87
- internal: Set<string>;
88
- relative: Set<string>;
89
- }
90
-
91
- function resolveImports(sources: string[]): ResolvedImports {
92
- const resolved: ResolvedImports = {
93
- npm: new Set(),
94
- registry: new Set(),
95
- internal: new Set(),
96
- relative: new Set(),
97
- };
98
-
99
- for (const source of sources) {
100
- for (const specifier of extractSpecifiers(source)) {
101
- if (PEER_PACKAGES.has(specifier)) continue;
102
-
103
- if (specifier.startsWith("@/components/")) {
104
- const name = specifier.split("/")[2];
105
- if (name) resolved.registry.add(name);
106
- } else if (specifier.startsWith("@/")) {
107
- resolved.internal.add(specifier);
108
- } else if (specifier.startsWith(".")) {
109
- resolved.relative.add(specifier);
110
- } else {
111
- resolved.npm.add(packageNameOf(specifier));
112
- }
113
- }
114
- }
115
-
116
- return resolved;
117
- }
118
-
119
- const metaEntries = Object.entries(metaModules);
120
-
121
- describe("registry metadata", () => {
122
- it("finds at least one component", () => {
123
- expect(metaEntries.length).toBeGreaterThan(0);
124
- });
125
-
126
- describe.each(metaEntries)("%s", (metaPath, module) => {
127
- const meta = module.meta;
128
- const dir = componentDir(metaPath);
129
- const dirName = componentName(metaPath);
130
-
131
- const shippedSources = meta.files
132
- .map((file) => sourceFiles[`${dir}/${file}`])
133
- .filter((source): source is string => typeof source === "string");
134
-
135
- it("exports a well-formed meta object", () => {
136
- expect(meta.name).toMatch(/^[a-z][a-z0-9-]*$/);
137
- expect(meta.title.length).toBeGreaterThan(0);
138
- expect(meta.description.length).toBeGreaterThan(9);
139
- expect(COMPONENT_CATEGORIES).toContain(meta.category);
140
- expect(COMPONENT_STATUSES).toContain(meta.status);
141
- expect(meta.files.length).toBeGreaterThan(0);
142
- });
143
-
144
- it("uses its directory name as its registry name", () => {
145
- expect(meta.name).toBe(dirName);
146
- });
147
-
148
- it("lists only files that exist", () => {
149
- for (const file of meta.files) {
150
- expect(
151
- sourceFiles[`${dir}/${file}`],
152
- `${dir}/${file} is listed but missing`,
153
- ).toBeTypeOf("string");
154
- }
155
- expect(shippedSources).toHaveLength(meta.files.length);
156
- });
157
-
158
- it("ships an index, a test and a story", () => {
159
- expect(sourceFiles[`${dir}/index.ts`]).toBeTypeOf("string");
160
- expect(sourceFiles[`${dir}/${dirName}.test.tsx`]).toBeTypeOf("string");
161
- expect(sourceFiles[`${dir}/${dirName}.stories.tsx`]).toBeTypeOf("string");
162
- });
163
-
164
- it("declares exactly the npm packages its source imports", () => {
165
- const { npm } = resolveImports(shippedSources);
166
- expect([...npm].sort()).toEqual([...meta.dependencies].sort());
167
- });
168
-
169
- it("declares exactly the registry entries its source imports", () => {
170
- const { registry } = resolveImports(shippedSources);
171
- expect([...registry].sort()).toEqual([...meta.registryDependencies].sort());
172
- });
173
-
174
- it("does not let a component depend on a block", () => {
175
- // Blocks are assembled from components; the reverse would make a
176
- // component drag a whole page section into a project that wanted a button.
177
- if (meta.kind === "block") return;
178
- const blockNames = new Set(blockMetas.map((block) => block.name));
179
- for (const dependency of meta.registryDependencies) {
180
- expect(
181
- blockNames.has(dependency),
182
- `${meta.name} depends on block "${dependency}"`,
183
- ).toBe(false);
184
- }
185
- });
186
-
187
- it("only imports internal modules that `init` installs", () => {
188
- const { internal } = resolveImports(shippedSources);
189
- for (const specifier of internal) {
190
- expect(
191
- INSTALLED_BY_INIT.has(specifier),
192
- `${specifier} is imported but no CLI step installs it`,
193
- ).toBe(true);
194
- }
195
- });
196
-
197
- it("only uses relative imports that resolve inside the shipped file set", () => {
198
- const { relative } = resolveImports(shippedSources);
199
- for (const specifier of relative) {
200
- const target = specifier.replace(/^\.\//, "");
201
- const matches = meta.files.some((file) => file.replace(/\.tsx?$/, "") === target);
202
- expect(matches, `${specifier} is imported but not in meta.files`).toBe(true);
203
- }
204
- });
205
-
206
- it("declares dependencies that are actually installed in this workspace", () => {
207
- for (const dependency of meta.dependencies) {
208
- expect(
209
- declaredNpmDependencies.has(dependency),
210
- `${dependency} is declared in meta but missing from @dowel-ui/react dependencies`,
211
- ).toBe(true);
212
- }
213
- });
214
- });
215
-
216
- it("lists every entry in the registry barrels", () => {
217
- // The barrels are what the registry build reads. An entry that exists on
218
- // disk but is missing from one would simply never be publishable, with no
219
- // other signal that anything was wrong.
220
- const onDisk = metaEntries.map(([, module]) => module.meta.name).sort();
221
- const inBarrels = [...componentMetas, ...blockMetas].map((meta) => meta.name).sort();
222
- expect(inBarrels).toEqual(onDisk);
223
- });
224
-
225
- it("keeps components and blocks in separate barrels", () => {
226
- expect(componentMetas.every((meta) => meta.kind !== "block")).toBe(true);
227
- expect(blockMetas.every((meta) => meta.kind === "block")).toBe(true);
228
- });
229
-
230
- it("has no duplicate registry names", () => {
231
- const names = metaEntries.map(([, module]) => module.meta.name);
232
- expect(new Set(names).size).toBe(names.length);
233
- });
234
- });