@charcuterie/ui 1.1.0 → 2.0.1

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 (116) hide show
  1. package/dist/Combobox/Combobox.d.ts +54 -0
  2. package/dist/Combobox/Combobox.d.ts.map +1 -0
  3. package/dist/Combobox/Combobox.js +216 -0
  4. package/dist/Combobox/Combobox.js.map +1 -0
  5. package/dist/Combobox/ComboboxOption.d.ts +36 -0
  6. package/dist/Combobox/ComboboxOption.d.ts.map +1 -0
  7. package/dist/Combobox/ComboboxOption.js +32 -0
  8. package/dist/Combobox/ComboboxOption.js.map +1 -0
  9. package/dist/Dialog/Dialog.d.ts +53 -0
  10. package/dist/Dialog/Dialog.d.ts.map +1 -0
  11. package/dist/Dialog/Dialog.js +52 -0
  12. package/dist/Dialog/Dialog.js.map +1 -0
  13. package/dist/Lightbox/Lightbox.d.ts +7 -7
  14. package/dist/Lightbox/Lightbox.js +9 -9
  15. package/dist/Lightbox/Lightbox.js.map +1 -1
  16. package/dist/Listbox/Listbox.d.ts +56 -0
  17. package/dist/Listbox/Listbox.d.ts.map +1 -0
  18. package/dist/Listbox/Listbox.js +151 -0
  19. package/dist/Listbox/Listbox.js.map +1 -0
  20. package/dist/Listbox/ListboxOption.d.ts +36 -0
  21. package/dist/Listbox/ListboxOption.d.ts.map +1 -0
  22. package/dist/Listbox/ListboxOption.js +42 -0
  23. package/dist/Listbox/ListboxOption.js.map +1 -0
  24. package/dist/Menu/Menu.d.ts.map +1 -1
  25. package/dist/Menu/Menu.js +47 -97
  26. package/dist/Menu/Menu.js.map +1 -1
  27. package/dist/Modal/Modal.d.ts +11 -59
  28. package/dist/Modal/Modal.d.ts.map +1 -1
  29. package/dist/Modal/Modal.js +28 -101
  30. package/dist/Modal/Modal.js.map +1 -1
  31. package/dist/Overlay/OverlayPanel.d.ts +50 -0
  32. package/dist/Overlay/OverlayPanel.d.ts.map +1 -0
  33. package/dist/Overlay/OverlayPanel.js +72 -0
  34. package/dist/Overlay/OverlayPanel.js.map +1 -0
  35. package/dist/Overlay/OverlayStack.d.ts +47 -0
  36. package/dist/Overlay/OverlayStack.d.ts.map +1 -0
  37. package/dist/Overlay/OverlayStack.js +46 -0
  38. package/dist/Overlay/OverlayStack.js.map +1 -0
  39. package/dist/Overlay/SharedBackdrop.d.ts +24 -0
  40. package/dist/Overlay/SharedBackdrop.d.ts.map +1 -0
  41. package/dist/Overlay/SharedBackdrop.js +28 -0
  42. package/dist/Overlay/SharedBackdrop.js.map +1 -0
  43. package/dist/Overlay/lockScrollBehind.d.ts.map +1 -0
  44. package/dist/Overlay/lockScrollBehind.js.map +1 -0
  45. package/dist/Overlay/overlayPanelClass.d.ts +17 -0
  46. package/dist/Overlay/overlayPanelClass.d.ts.map +1 -0
  47. package/dist/Overlay/overlayPanelClass.js +17 -0
  48. package/dist/Overlay/overlayPanelClass.js.map +1 -0
  49. package/dist/Overlay/useAnchoredOverlay.d.ts +97 -0
  50. package/dist/Overlay/useAnchoredOverlay.d.ts.map +1 -0
  51. package/dist/Overlay/useAnchoredOverlay.js +81 -0
  52. package/dist/Overlay/useAnchoredOverlay.js.map +1 -0
  53. package/dist/Popover/Popover.d.ts +16 -24
  54. package/dist/Popover/Popover.d.ts.map +1 -1
  55. package/dist/Popover/Popover.js +32 -68
  56. package/dist/Popover/Popover.js.map +1 -1
  57. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  58. package/dist/Tooltip/Tooltip.js +25 -25
  59. package/dist/Tooltip/Tooltip.js.map +1 -1
  60. package/dist/index.d.ts +7 -1
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +3 -0
  63. package/dist/index.js.map +1 -1
  64. package/dist/mountStory.testHelpers.d.ts +1 -0
  65. package/dist/mountStory.testHelpers.d.ts.map +1 -1
  66. package/dist/mountStory.testHelpers.js +21 -1
  67. package/dist/mountStory.testHelpers.js.map +1 -1
  68. package/package.json +3 -2
  69. package/src/Combobox/Combobox.mdx +71 -0
  70. package/src/Combobox/Combobox.stories.tsx +245 -0
  71. package/src/Combobox/Combobox.test.tsx +289 -0
  72. package/src/Combobox/Combobox.tsx +513 -0
  73. package/src/Combobox/ComboboxOption.tsx +88 -0
  74. package/src/Dialog/Dialog.mdx +87 -0
  75. package/src/Dialog/Dialog.stories.tsx +397 -0
  76. package/src/Dialog/Dialog.test.tsx +340 -0
  77. package/src/Dialog/Dialog.tsx +133 -0
  78. package/src/Field/Field.test.tsx +6 -4
  79. package/src/Lightbox/Lightbox.mdx +9 -8
  80. package/src/Lightbox/Lightbox.stories.tsx +5 -5
  81. package/src/Lightbox/Lightbox.test.tsx +25 -36
  82. package/src/Lightbox/Lightbox.tsx +10 -10
  83. package/src/Listbox/Listbox.mdx +68 -0
  84. package/src/Listbox/Listbox.stories.tsx +205 -0
  85. package/src/Listbox/Listbox.test.tsx +198 -0
  86. package/src/Listbox/Listbox.tsx +295 -0
  87. package/src/Listbox/ListboxOption.tsx +104 -0
  88. package/src/Menu/Menu.mdx +11 -13
  89. package/src/Menu/Menu.test.tsx +47 -38
  90. package/src/Menu/Menu.tsx +81 -142
  91. package/src/Modal/Modal.mdx +29 -82
  92. package/src/Modal/Modal.stories.tsx +55 -188
  93. package/src/Modal/Modal.test.tsx +44 -177
  94. package/src/Modal/Modal.tsx +54 -204
  95. package/src/Overlay/OverlayPanel.tsx +195 -0
  96. package/src/Overlay/OverlayStack.test.tsx +235 -0
  97. package/src/Overlay/OverlayStack.tsx +132 -0
  98. package/src/Overlay/SharedBackdrop.tsx +40 -0
  99. package/src/Overlay/overlayPanelClass.ts +17 -0
  100. package/src/Overlay/useAnchoredOverlay.ts +197 -0
  101. package/src/Popover/Popover.mdx +17 -20
  102. package/src/Popover/Popover.stories.tsx +33 -0
  103. package/src/Popover/Popover.test.tsx +68 -32
  104. package/src/Popover/Popover.tsx +56 -95
  105. package/src/Tooltip/Tooltip.mdx +12 -9
  106. package/src/Tooltip/Tooltip.test.tsx +17 -19
  107. package/src/Tooltip/Tooltip.tsx +35 -32
  108. package/src/index.ts +13 -4
  109. package/src/mountStory.testHelpers.ts +25 -1
  110. package/src/sourceRules.test.ts +12 -3
  111. package/src/storyControls.test.ts +25 -12
  112. package/dist/Modal/lockScrollBehind.d.ts.map +0 -1
  113. package/dist/Modal/lockScrollBehind.js.map +0 -1
  114. /package/dist/{Modal → Overlay}/lockScrollBehind.d.ts +0 -0
  115. /package/dist/{Modal → Overlay}/lockScrollBehind.js +0 -0
  116. /package/src/{Modal → Overlay}/lockScrollBehind.ts +0 -0
package/src/index.ts CHANGED
@@ -59,6 +59,8 @@ export type {
59
59
  ColorSchemeToggleProps,
60
60
  } from "./ColorSchemeToggle/ColorSchemeToggle.tsx"
61
61
  export { ColorSchemeToggle } from "./ColorSchemeToggle/ColorSchemeToggle.tsx"
62
+ export type { ComboboxProps } from "./Combobox/Combobox.tsx"
63
+ export { Combobox } from "./Combobox/Combobox.tsx"
62
64
  export type { BadgeSize } from "./controlStyles.ts"
63
65
  export {
64
66
  BADGE_SIZE_CLASS,
@@ -68,6 +70,11 @@ export {
68
70
  MIN_TOUCH_TARGET_CLASS,
69
71
  SPINNER_SIZE_CLASS,
70
72
  } from "./controlStyles.ts"
73
+ export type {
74
+ DialogProps,
75
+ DialogSize,
76
+ } from "./Dialog/Dialog.tsx"
77
+ export { Dialog } from "./Dialog/Dialog.tsx"
71
78
  export type {
72
79
  EmptyStateProps,
73
80
  EmptyStateSize,
@@ -92,6 +99,11 @@ export {
92
99
  } from "./intentStyles.ts"
93
100
  export type { LightboxProps } from "./Lightbox/Lightbox.tsx"
94
101
  export { Lightbox } from "./Lightbox/Lightbox.tsx"
102
+ export type {
103
+ ListboxItem,
104
+ ListboxProps,
105
+ } from "./Listbox/Listbox.tsx"
106
+ export { Listbox } from "./Listbox/Listbox.tsx"
95
107
  export type {
96
108
  LiveStatusIndicatorProps,
97
109
  LiveStatusIndicatorSize,
@@ -114,10 +126,7 @@ export type {
114
126
  MenuProps,
115
127
  } from "./Menu/Menu.tsx"
116
128
  export { Menu } from "./Menu/Menu.tsx"
117
- export type {
118
- ModalProps,
119
- ModalSize,
120
- } from "./Modal/Modal.tsx"
129
+ export type { ModalProps } from "./Modal/Modal.tsx"
121
130
  export { Modal } from "./Modal/Modal.tsx"
122
131
  export type { PopoverProps } from "./Popover/Popover.tsx"
123
132
  export { Popover } from "./Popover/Popover.tsx"
@@ -46,12 +46,26 @@ type MountableStory = {
46
46
  * The stale tree is removed rather than unmounted because portable
47
47
  * stories expose no unmount. Detaching is enough: it takes the node
48
48
  * out of the layout, which is the entire problem.
49
+ *
50
+ * ### Portalled panels leave the canvas
51
+ *
52
+ * Once the overlays portal to `document.body`, their panels are no
53
+ * longer inside `canvasElement`, and floating-ui's portal roots
54
+ * (`[data-floating-ui-portal]`) are body children a portable story
55
+ * never unmounts. A previous test's panel therefore lingers next to
56
+ * the live one, and `expectAgentDrivable(body, …)` fails with "2
57
+ * elements match" — the portal analogue of the fixed-position
58
+ * `ToastRegion` bug above. So the same cleanup that detaches stale
59
+ * canvases also removes stale portal roots, and `mountStory` now hands
60
+ * back `body` (scoped to `document.body`) for querying the panels the
61
+ * triggers still open from inside `canvas`.
49
62
  */
50
63
  const mountedCanvases: HTMLElement[] = []
51
64
 
52
65
  export const mountStory = async (
53
66
  story: MountableStory,
54
67
  ): Promise<{
68
+ body: Canvas
55
69
  canvas: Canvas
56
70
  canvasElement: HTMLElement
57
71
  }> => {
@@ -59,6 +73,12 @@ export const mountStory = async (
59
73
  previous.remove()
60
74
  }
61
75
 
76
+ for (const portalRoot of document.querySelectorAll(
77
+ "[data-floating-ui-portal]",
78
+ )) {
79
+ portalRoot.remove()
80
+ }
81
+
62
82
  const canvasElement = document.createElement("div")
63
83
 
64
84
  document.body.append(canvasElement)
@@ -67,5 +87,9 @@ export const mountStory = async (
67
87
 
68
88
  await story.run({ canvasElement })
69
89
 
70
- return { canvas: within(canvasElement), canvasElement }
90
+ return {
91
+ body: within(document.body),
92
+ canvas: within(canvasElement),
93
+ canvasElement,
94
+ }
71
95
  }
@@ -293,9 +293,14 @@ test("the barrel is the only place components are re-exported", async () => {
293
293
  //
294
294
  // 2026-08-03 adds the two colour-scheme controls — `ColorSchemeToggle`
295
295
  // (Layer 2, presentational) and `ColorSchemeSwitcher` (Layer 3,
296
- // connected) — plus `Lightbox` (landed the same day on its own
297
- // branch), taking the count to 28.
298
- expect(componentNames.length).toBe(28)
296
+ // connected) — and `Lightbox`, the thumbnail-over-`Dialog` viewer —
297
+ // taking the count to 28.
298
+ //
299
+ // The M8 overlay rebuild renames the old chrome-bearing `Modal` to
300
+ // `Dialog` and reintroduces `Modal` as the base layer, so both are
301
+ // components: +1 → 29. The picker family adds `Listbox` (single-select)
302
+ // and `Combobox` (searchable/virtualized): +2 → 31.
303
+ expect(componentNames.length).toBe(31)
299
304
 
300
305
  for (const name of componentNames) {
301
306
  expect(barrel).toContain(`export { ${name} }`)
@@ -394,6 +399,10 @@ const ENTRY_POINT_RUNTIMES: Record<
394
399
  "@charcuterie/logic/browser",
395
400
  "@charcuterie/tokens",
396
401
  "@floating-ui/react",
402
+ // `Combobox`'s virtualization — the second runtime dependency
403
+ // this package has ever taken. MIT, US-origin (Tanner Linsley),
404
+ // tree-shakeable, ~4 KB gz.
405
+ "@tanstack/react-virtual",
397
406
  "react",
398
407
  ],
399
408
  "./testing": [],
@@ -24,21 +24,32 @@ import { expect, test } from "vitest"
24
24
  const sourceDirectory = resolve(import.meta.dirname)
25
25
 
26
26
  const readComponent = async (name: string) => {
27
- const [component, story] = await Promise.all([
28
- readFile(
27
+ // A `<Name>/<Name>.tsx` is the component-folder signal. An
28
+ // uppercase folder without one — `Overlay/`, which holds the shared
29
+ // portal/backdrop/anchoring infrastructure and no storied component
30
+ // — is not a component and is skipped, so this rule stays about the
31
+ // things that have a Controls panel to get wrong. A real component
32
+ // missing only its *story* still throws below, which is the guard.
33
+ let component: string
34
+
35
+ try {
36
+ component = await readFile(
29
37
  join(sourceDirectory, name, `${name}.tsx`),
30
38
  "utf8",
31
- ),
32
- readFile(
33
- join(sourceDirectory, name, `${name}.stories.tsx`),
34
- "utf8",
35
- ),
36
- ])
39
+ )
40
+ } catch {
41
+ return null
42
+ }
43
+
44
+ const story = await readFile(
45
+ join(sourceDirectory, name, `${name}.stories.tsx`),
46
+ "utf8",
47
+ )
37
48
 
38
49
  return { component, name, story }
39
50
  }
40
51
 
41
- const componentNames = (
52
+ const componentDirectories = (
42
53
  await readdir(sourceDirectory, { withFileTypes: true })
43
54
  )
44
55
  .filter(
@@ -48,9 +59,11 @@ const componentNames = (
48
59
  .map((entry) => entry.name)
49
60
  .sort()
50
61
 
51
- const components = await Promise.all(
52
- componentNames.map(readComponent),
53
- )
62
+ const components = (
63
+ await Promise.all(componentDirectories.map(readComponent))
64
+ ).filter((one) => one !== null)
65
+
66
+ const componentNames = components.map((one) => one.name)
54
67
 
55
68
  /**
56
69
  * `import type { A, B } from "@scope/pkg"` — bare specifiers only.
@@ -1 +0,0 @@
1
- {"version":3,"file":"lockScrollBehind.d.ts","sourceRoot":"","sources":["../../src/Modal/lockScrollBehind.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,eAAO,MAAM,gBAAgB,QAAO,CAAC,MAAM,IAAI,CAiC9C,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"lockScrollBehind.js","sourceRoot":"","sources":["../../src/Modal/lockScrollBehind.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,IAAI,SAAS,GAAG,CAAC,CAAA;AAEjB,IAAI,gBAAgB,GAAG,EAAE,CAAA;AAEzB,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAiB,EAAE;IACjD,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,OAAO,GAAG,EAAE,GAAE,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,EAAE,eAAe,EAAE,GAAG,QAAQ,CAAA;IAEpC,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;QACpB,gBAAgB,GAAG,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAA;QAEjD,eAAe,CAAC,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAA;IAC3C,CAAC;IAED,SAAS,IAAI,CAAC,CAAA;IAEd,IAAI,UAAU,GAAG,KAAK,CAAA;IAEtB,OAAO,GAAG,EAAE;QACV,6DAA6D;QAC7D,6DAA6D;QAC7D,qCAAqC;QACrC,IAAI,UAAU,EAAE,CAAC;YACf,OAAM;QACR,CAAC;QAED,UAAU,GAAG,IAAI,CAAA;QAEjB,SAAS,IAAI,CAAC,CAAA;QAEd,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;YACpB,eAAe,CAAC,KAAK,CAAC,QAAQ,GAAG,gBAAgB,CAAA;QACnD,CAAC;IACH,CAAC,CAAA;AACH,CAAC,CAAA"}
File without changes
File without changes
File without changes