@estiva-app/ui 0.22.0 → 0.24.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 (173) hide show
  1. package/README.md +21 -0
  2. package/dist/AppShell.d.ts.map +1 -1
  3. package/dist/AttachmentCard.d.ts.map +1 -1
  4. package/dist/Banner.d.ts +20 -2
  5. package/dist/Banner.d.ts.map +1 -1
  6. package/dist/ContainerHeader.d.ts +25 -0
  7. package/dist/ContainerHeader.d.ts.map +1 -0
  8. package/dist/EmptyState.d.ts +10 -1
  9. package/dist/EmptyState.d.ts.map +1 -1
  10. package/dist/Form.d.ts.map +1 -1
  11. package/dist/IconButton.d.ts +9 -1
  12. package/dist/IconButton.d.ts.map +1 -1
  13. package/dist/PreviewCard.d.ts +9 -2
  14. package/dist/PreviewCard.d.ts.map +1 -1
  15. package/dist/SectionHeader.d.ts +7 -1
  16. package/dist/SectionHeader.d.ts.map +1 -1
  17. package/dist/Select.d.ts +11 -3
  18. package/dist/Select.d.ts.map +1 -1
  19. package/dist/Tooltip.d.ts +13 -1
  20. package/dist/Tooltip.d.ts.map +1 -1
  21. package/dist/eslint/index.js +2 -1
  22. package/dist/eslint/index.js.map +2 -2
  23. package/dist/eslint/no-restyled-part.d.ts.map +1 -1
  24. package/dist/gates/app-checks.d.ts.map +1 -1
  25. package/dist/gates/{chunk-ZGJ2J5NU.js → chunk-EA33NP5B.js} +275 -11
  26. package/dist/gates/chunk-EA33NP5B.js.map +7 -0
  27. package/dist/gates/{chunk-AUXD4GCY.js → chunk-GTQZEHPC.js} +15 -1
  28. package/dist/gates/chunk-GTQZEHPC.js.map +7 -0
  29. package/dist/gates/cli.js +1 -1
  30. package/dist/gates/create-app.d.ts +2 -0
  31. package/dist/gates/create-app.d.ts.map +1 -1
  32. package/dist/gates/create-app.js +1 -1
  33. package/dist/gates/index.js +25 -3
  34. package/dist/gates/index.js.map +2 -2
  35. package/dist/gates/status.d.ts +2 -0
  36. package/dist/gates/status.d.ts.map +1 -1
  37. package/dist/index.d.ts +1 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +196 -156
  40. package/dist/index.js.map +4 -4
  41. package/dist/registry/app-DQI6WLHR.js +10 -0
  42. package/dist/registry/app.d.ts +21 -0
  43. package/dist/registry/app.d.ts.map +1 -0
  44. package/dist/registry/build-LGFCCOLR.js +27 -0
  45. package/dist/registry/build-LGFCCOLR.js.map +7 -0
  46. package/dist/registry/build.d.ts +68 -2
  47. package/dist/registry/build.d.ts.map +1 -1
  48. package/dist/registry/{chunk-MRSBS5OP.js → chunk-E4JNV7PC.js} +68 -12
  49. package/dist/registry/chunk-E4JNV7PC.js.map +7 -0
  50. package/dist/registry/{chunk-QDYGB3QN.js → chunk-NJN4MQAM.js} +60 -15
  51. package/dist/registry/chunk-NJN4MQAM.js.map +7 -0
  52. package/dist/registry/{chunk-IJNCYVH4.js → chunk-W2B2G7OE.js} +34 -9
  53. package/dist/registry/chunk-W2B2G7OE.js.map +7 -0
  54. package/dist/registry/chunk-WMFF3MPP.js +648 -0
  55. package/dist/registry/chunk-WMFF3MPP.js.map +7 -0
  56. package/dist/registry/cli.js +81 -26
  57. package/dist/registry/cli.js.map +3 -3
  58. package/dist/registry/find.d.ts +12 -1
  59. package/dist/registry/find.d.ts.map +1 -1
  60. package/dist/registry/index.d.ts +5 -2
  61. package/dist/registry/index.d.ts.map +1 -1
  62. package/dist/registry/index.js +11 -3
  63. package/dist/registry/schema.d.ts +83 -6
  64. package/dist/registry/schema.d.ts.map +1 -1
  65. package/package.json +2 -2
  66. package/registry.json +283 -95
  67. package/src/AppShell.mdx +10 -0
  68. package/src/AppShell.test.tsx +16 -0
  69. package/src/AppShell.tsx +4 -1
  70. package/src/AttachmentCard.mdx +9 -0
  71. package/src/AttachmentCard.test.tsx +10 -0
  72. package/src/AttachmentCard.tsx +8 -5
  73. package/src/Avatar.mdx +13 -3
  74. package/src/AvatarGroup.mdx +4 -0
  75. package/src/Banner.mdx +30 -7
  76. package/src/Banner.stories.tsx +22 -0
  77. package/src/Banner.test.tsx +35 -0
  78. package/src/Banner.tsx +34 -9
  79. package/src/Breadcrumb.mdx +10 -0
  80. package/src/Button.mdx +14 -3
  81. package/src/Card.mdx +9 -0
  82. package/src/Checkbox.mdx +10 -0
  83. package/src/Chip.mdx +4 -0
  84. package/src/ChipInput.mdx +12 -0
  85. package/src/CollapsibleSection.mdx +9 -0
  86. package/src/CommandPalette.mdx +14 -2
  87. package/src/ConfirmDialog.mdx +11 -0
  88. package/src/ContainerHeader.mdx +60 -0
  89. package/src/ContainerHeader.stories.tsx +62 -0
  90. package/src/ContainerHeader.test.tsx +47 -0
  91. package/src/ContainerHeader.tsx +45 -0
  92. package/src/DialogShell.mdx +14 -0
  93. package/src/Divider.mdx +9 -1
  94. package/src/EditableText.mdx +11 -0
  95. package/src/EmptyState.mdx +27 -4
  96. package/src/EmptyState.stories.tsx +6 -1
  97. package/src/EmptyState.test.tsx +18 -1
  98. package/src/EmptyState.tsx +14 -1
  99. package/src/Field.mdx +10 -1
  100. package/src/FieldLine.mdx +9 -1
  101. package/src/FilePicker.mdx +8 -0
  102. package/src/Form.mdx +10 -0
  103. package/src/Form.test.tsx +26 -0
  104. package/src/Form.tsx +7 -0
  105. package/src/IconButton.mdx +16 -2
  106. package/src/IconButton.stories.tsx +3 -0
  107. package/src/IconButton.test.tsx +25 -0
  108. package/src/IconButton.tsx +61 -46
  109. package/src/IdentityMenu.mdx +9 -0
  110. package/src/InlineChip.mdx +8 -0
  111. package/src/Kbd.mdx +4 -0
  112. package/src/Link.mdx +10 -0
  113. package/src/Menu.mdx +16 -1
  114. package/src/MenuItem.mdx +12 -2
  115. package/src/NavItem.mdx +8 -0
  116. package/src/Person.mdx +4 -0
  117. package/src/PersonTrigger.mdx +9 -0
  118. package/src/Popover.mdx +21 -0
  119. package/src/PreviewCard.mdx +19 -4
  120. package/src/PreviewCard.tsx +11 -4
  121. package/src/ProgressBar.mdx +8 -0
  122. package/src/Property.mdx +4 -0
  123. package/src/Rail.mdx +9 -1
  124. package/src/RailItem.mdx +8 -0
  125. package/src/Reaction.mdx +9 -0
  126. package/src/ReactionPicker.mdx +8 -0
  127. package/src/ScrollArea.mdx +13 -2
  128. package/src/SearchInput.mdx +9 -0
  129. package/src/SectionHeader.mdx +13 -0
  130. package/src/SectionHeader.stories.tsx +9 -0
  131. package/src/SectionHeader.test.tsx +9 -0
  132. package/src/SectionHeader.tsx +8 -2
  133. package/src/SectionLabel.mdx +4 -0
  134. package/src/Select.mdx +21 -2
  135. package/src/Select.stories.tsx +4 -1
  136. package/src/Select.test.tsx +17 -0
  137. package/src/Select.tsx +34 -13
  138. package/src/Sidebar.mdx +8 -0
  139. package/src/Skeleton.mdx +4 -0
  140. package/src/Tabs.mdx +12 -1
  141. package/src/TextInput.mdx +8 -0
  142. package/src/Textarea.mdx +8 -0
  143. package/src/Toast.mdx +12 -1
  144. package/src/Toolbar.mdx +11 -0
  145. package/src/Tooltip.mdx +26 -2
  146. package/src/Tooltip.stories.tsx +26 -0
  147. package/src/Tooltip.test.tsx +29 -0
  148. package/src/Tooltip.tsx +17 -4
  149. package/src/TopBar.mdx +4 -0
  150. package/src/eslint/no-rebuilt-behaviour.ts +1 -1
  151. package/src/eslint/no-restyled-part.ts +1 -0
  152. package/src/gates/app-checks.ts +23 -1
  153. package/src/gates/create-app.test.ts +77 -6
  154. package/src/gates/create-app.ts +288 -15
  155. package/src/gates/status.ts +16 -0
  156. package/src/index.ts +1 -0
  157. package/src/pages.test.ts +142 -0
  158. package/src/registry/app.test.ts +562 -0
  159. package/src/registry/app.ts +854 -0
  160. package/src/registry/build.ts +114 -25
  161. package/src/registry/cli.ts +105 -42
  162. package/src/registry/find.ts +45 -8
  163. package/src/registry/index.ts +8 -1
  164. package/src/registry/registry.test.ts +5 -5
  165. package/src/registry/schema.ts +163 -12
  166. package/tailwind-preset.js +22 -1
  167. package/dist/gates/chunk-AUXD4GCY.js.map +0 -7
  168. package/dist/gates/chunk-ZGJ2J5NU.js.map +0 -7
  169. package/dist/registry/build-GOVLABI6.js +0 -13
  170. package/dist/registry/chunk-IJNCYVH4.js.map +0 -7
  171. package/dist/registry/chunk-MRSBS5OP.js.map +0 -7
  172. package/dist/registry/chunk-QDYGB3QN.js.map +0 -7
  173. /package/dist/registry/{build-GOVLABI6.js.map → app-DQI6WLHR.js.map} +0 -0
@@ -0,0 +1,142 @@
1
+ /**
2
+ * The contract every component page keeps (UIG-14, with UIG-15 and UIG-16 folded in).
3
+ *
4
+ * A page opens with a line saying what the part is, then has `When`, `When not`,
5
+ * `How` (with code) and `What it owns`, in that order, before `Keys` and `Props`.
6
+ *
7
+ * `What it owns` reads the checker's own list back. A row ticked ✓ in its third
8
+ * column is a behaviour from `OWNED_BEHAVIOURS` that one of the page's parts owns,
9
+ * worded as `ROW` below; every behaviour the page's parts own has one; and a row
10
+ * worded as a behaviour the parts do not own fails. So the page and the checker
11
+ * cannot say different things. A part that owns nothing says so in one line.
12
+ *
13
+ * A `When not` line names only parts that exist: a bold name is a value export of
14
+ * `index.ts`, or a page.
15
+ */
16
+ import { readdirSync, readFileSync } from 'node:fs'
17
+ import { describe, expect, it } from 'vitest'
18
+ import { OWNED_BEHAVIOURS } from './eslint/no-rebuilt-behaviour'
19
+
20
+ /** How a page words each behaviour. `base-ui` is how a part is built, not what it does for a caller. */
21
+ const ROW: Record<string, RegExp> = {
22
+ portal: /^Floats on top of the page\b/,
23
+ 'press-outside': /^Closes on a press outside\b/,
24
+ 'page-keys': /^Takes its keys by itself\b/,
25
+ focus: /^Holds focus inside while open\b/,
26
+ 'scroll-lock': /^Stops the page behind it scrolling\b/,
27
+ follow: /^Stays attached to its anchor\b/,
28
+ walking: /^Moves through its items with the arrow keys\b/,
29
+ role: /^Says what it is to assistive technology\b/,
30
+ 'tab-stop': /^Is reachable with Tab\b/,
31
+ scroll: /^Scrolls with our scrollbar\b/,
32
+ }
33
+ const NOT_A_ROW = ['base-ui']
34
+
35
+ /** A page whose part another ticket is changing, while it is; empty when none is. */
36
+ const WAITING: string[] = []
37
+
38
+ const NOTHING = 'Nothing. It only draws. Clicks and keys are yours.'
39
+
40
+ const dir = new URL('.', import.meta.url)
41
+ const read = (file: string) => readFileSync(new URL(file, dir), 'utf8').replace(/\r\n/g, '\n')
42
+ const pages = readdirSync(dir)
43
+ .filter((f) => f.endsWith('.mdx'))
44
+ .map((f) => f.slice(0, -4))
45
+
46
+ const index = read('index.ts')
47
+ /** Value exports of index.ts, by the module they come from. */
48
+ const byModule = new Map<string, string[]>()
49
+ for (const m of index.matchAll(/export\s*\{([^}]*)\}\s*from\s*'\.\/([\w/]+)'/g)) {
50
+ const names = m[1]
51
+ .split(',')
52
+ .map((s) => s.trim())
53
+ .filter((s) => s && !s.startsWith('type '))
54
+ .map((s) => s.split(/\s+as\s+/).pop()!)
55
+ byModule.set(m[2], [...(byModule.get(m[2]) ?? []), ...names])
56
+ }
57
+ const exported = new Set([...byModule.values()].flat())
58
+
59
+ /** The parts a page documents: its own name, and its module's exports that have no page of their own. */
60
+ const partsOf = (page: string) => [page, ...(byModule.get(page) ?? []).filter((n) => n !== page && !pages.includes(n))]
61
+
62
+ const section = (t: string, name: string) => {
63
+ const head = `\n## ${name}\n`
64
+ const at = t.indexOf(head)
65
+ if (at < 0) return null
66
+ const rest = t.slice(at + head.length)
67
+ const end = rest.search(/^## /m)
68
+ return end < 0 ? rest : rest.slice(0, end)
69
+ }
70
+
71
+ const owns = (t: string) =>
72
+ (section(t, 'What it owns') ?? '')
73
+ .split('\n')
74
+ .filter((l) => l.startsWith('| ') && !l.startsWith('| It does |'))
75
+ .map((l) => l.split('|').map((c) => c.trim()))
76
+ .map(([, does, never, ticked]) => ({ does, never, ticked: ticked === '✓' }))
77
+
78
+ describe('the page contract', () => {
79
+ it('words every behaviour the checker knows', () => {
80
+ const ids = OWNED_BEHAVIOURS.map((b) => b.id).filter((id) => !NOT_A_ROW.includes(id))
81
+ expect(Object.keys(ROW).sort()).toEqual(ids.sort())
82
+ })
83
+
84
+ it('finds the pages', () => {
85
+ expect(pages.length).toBeGreaterThanOrEqual(54)
86
+ })
87
+
88
+ for (const page of pages) {
89
+ describe(page, () => {
90
+ const t = read(`${page}.mdx`)
91
+ const headings = [...t.matchAll(/^## (.+)$/gm)].map((m) => m[1])
92
+
93
+ it('opens with a line saying what it is', () => {
94
+ const opening = t.split(/^# .*$/m)[1]?.split(/\n\s*\n/).map((s) => s.trim()).find((s) => s && !s.startsWith('<'))
95
+ expect(opening, 'no opening line under the title').toBeTruthy()
96
+ })
97
+
98
+ it('has each section once', () => {
99
+ expect(headings.filter((h, i) => headings.indexOf(h) !== i)).toEqual([])
100
+ })
101
+
102
+ it('has its sections in order', () => {
103
+ const want = WAITING.includes(page) ? ['When', 'When not', 'How'] : ['When', 'When not', 'How', 'What it owns']
104
+ const at = want.map((h) => headings.indexOf(h))
105
+ expect(at.filter((i) => i < 0).length, `missing: ${want.filter((_, i) => at[i] < 0).join(', ')}`).toBe(0)
106
+ expect([...at].sort((a, b) => a - b)).toEqual(at)
107
+ for (const after of ['Keys', 'Props']) {
108
+ const i = headings.indexOf(after)
109
+ if (i >= 0) expect(at[at.length - 1], `${want[want.length - 1]} comes before ${after}`).toBeLessThan(i)
110
+ }
111
+ })
112
+
113
+ it('shows code under How', () => {
114
+ expect(section(t, 'How')).toMatch(/```tsx?\n/)
115
+ })
116
+
117
+ it('names only parts that exist under When not', () => {
118
+ const names = [...(section(t, 'When not') ?? '').matchAll(/\*\*`?([A-Z][A-Za-z0-9]*)`?\*\*/g)].map((m) => m[1])
119
+ expect(names.filter((n) => !exported.has(n) && !pages.includes(n))).toEqual([])
120
+ })
121
+
122
+ if (WAITING.includes(page)) return
123
+
124
+ it('says what it owns, as the checker does', () => {
125
+ const parts = partsOf(page)
126
+ const owned = OWNED_BEHAVIOURS.filter((b) => !NOT_A_ROW.includes(b.id) && b.owners.some((o) => parts.includes(o))).map((b) => b.id)
127
+ const rows = owns(t)
128
+ const body = (section(t, 'What it owns') ?? '').trim()
129
+ if (!rows.length) {
130
+ expect(owned, `${page} owns ${owned.join(', ')}; the page says nothing`).toEqual([])
131
+ expect(body).toBe(NOTHING)
132
+ return
133
+ }
134
+ const said = rows.flatMap((r) => Object.entries(ROW).filter(([, re]) => re.test(r.does)).map(([id]) => ({ id, ticked: r.ticked, does: r.does })))
135
+ expect(said.filter((s) => !s.ticked).map((s) => s.does), 'a checker behaviour, not ticked').toEqual([])
136
+ expect(rows.filter((r) => r.ticked && !Object.values(ROW).some((re) => re.test(r.does))).map((r) => r.does), 'ticked, but not worded as a checker behaviour').toEqual([])
137
+ expect(said.map((s) => s.id).sort()).toEqual([...owned].sort())
138
+ expect(rows.every((r) => r.does && r.never), 'every row says what it does and what you never write').toBe(true)
139
+ })
140
+ })
141
+ }
142
+ })