@ponti-studios/ui 2.0.1 → 3.0.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 (319) hide show
  1. package/README.md +36 -3
  2. package/dist/auth/index.d.ts +3 -0
  3. package/dist/auth/index.js +3 -0
  4. package/dist/components/data-display/accordion.d.ts +10 -0
  5. package/dist/components/data-display/accordion.js +53 -0
  6. package/{src/components/data-display/index.ts → dist/components/data-display/index.d.ts} +1 -10
  7. package/dist/components/data-display/index.js +8 -0
  8. package/dist/components/data-display/metric-card.d.ts +9 -0
  9. package/dist/components/data-display/metric-card.js +25 -0
  10. package/dist/components/data-display/sort-controls.d.ts +13 -0
  11. package/dist/components/data-display/sort-controls.js +57 -0
  12. package/dist/components/data-display/sort-row.d.ts +10 -0
  13. package/dist/components/data-display/sort-row.js +52 -0
  14. package/dist/components/data-display/table.d.ts +10 -0
  15. package/dist/components/data-display/table.js +57 -0
  16. package/dist/components/feedback/alert.d.ts +9 -0
  17. package/dist/components/feedback/alert.js +38 -0
  18. package/dist/components/feedback/empty-state.d.ts +18 -0
  19. package/dist/components/feedback/empty-state.js +36 -0
  20. package/{src/components/feedback/index.ts → dist/components/feedback/index.d.ts} +2 -0
  21. package/dist/components/feedback/index.js +7 -0
  22. package/dist/components/feedback/progress.d.ts +6 -0
  23. package/dist/components/feedback/progress.js +25 -0
  24. package/dist/components/feedback/skeleton.d.ts +2 -0
  25. package/dist/components/feedback/skeleton.js +11 -0
  26. package/dist/components/feedback/spinner.d.ts +14 -0
  27. package/dist/components/feedback/spinner.js +27 -0
  28. package/dist/components/feedback/update-guard.d.ts +13 -0
  29. package/dist/components/feedback/update-guard.js +38 -0
  30. package/dist/components/filters/active-filters-bar.d.ts +14 -0
  31. package/dist/components/filters/active-filters-bar.js +44 -0
  32. package/dist/components/filters/filter-chip.d.ts +11 -0
  33. package/dist/components/filters/filter-chip.js +33 -0
  34. package/dist/components/filters/filter-select.d.ts +17 -0
  35. package/dist/components/filters/filter-select.js +46 -0
  36. package/dist/components/filters/index.js +4 -0
  37. package/dist/components/forms/checkbox.d.ts +4 -0
  38. package/dist/components/forms/checkbox.js +24 -0
  39. package/dist/components/forms/date-month-select.d.ts +11 -0
  40. package/dist/components/forms/date-month-select.js +47 -0
  41. package/dist/components/forms/date-picker.d.ts +60 -0
  42. package/dist/components/forms/date-picker.js +193 -0
  43. package/dist/components/forms/date-utils.d.ts +14 -0
  44. package/dist/components/forms/date-utils.js +18 -0
  45. package/dist/components/forms/drop-zone.d.ts +42 -0
  46. package/dist/components/forms/drop-zone.js +184 -0
  47. package/{src/components/forms/index.ts → dist/components/forms/index.d.ts} +3 -9
  48. package/dist/components/forms/index.js +17 -0
  49. package/dist/components/forms/input.d.ts +3 -0
  50. package/dist/components/forms/input.js +15 -0
  51. package/dist/components/forms/label.d.ts +3 -0
  52. package/dist/components/forms/label.js +14 -0
  53. package/dist/components/forms/passkey-management.d.ts +14 -0
  54. package/dist/components/forms/passkey-management.js +132 -0
  55. package/dist/components/forms/radio-group.d.ts +6 -0
  56. package/dist/components/forms/radio-group.js +28 -0
  57. package/dist/components/forms/select.d.ts +18 -0
  58. package/dist/components/forms/select.js +103 -0
  59. package/dist/components/forms/slider.d.ts +9 -0
  60. package/dist/components/forms/slider.js +25 -0
  61. package/dist/components/forms/stepper.d.ts +15 -0
  62. package/dist/components/forms/stepper.js +41 -0
  63. package/dist/components/forms/switch.d.ts +6 -0
  64. package/dist/components/forms/switch.js +21 -0
  65. package/dist/components/forms/text-field.d.ts +15 -0
  66. package/dist/components/forms/text-field.js +46 -0
  67. package/dist/components/forms/text-field.types.d.ts +8 -0
  68. package/dist/components/forms/textarea.d.ts +3 -0
  69. package/dist/components/forms/textarea.js +14 -0
  70. package/dist/components/layout/index.js +5 -0
  71. package/dist/components/layout/particle-background.d.ts +19 -0
  72. package/dist/components/layout/particle-background.js +171 -0
  73. package/dist/components/layout/scroll-area.d.ts +9 -0
  74. package/dist/components/layout/scroll-area.js +35 -0
  75. package/dist/components/layout/section-intro.d.ts +12 -0
  76. package/dist/components/layout/section-intro.js +28 -0
  77. package/dist/components/layout/surface-panel.d.ts +7 -0
  78. package/dist/components/layout/surface-panel.js +11 -0
  79. package/dist/components/navigation/app-navigation.d.ts +67 -0
  80. package/dist/components/navigation/app-navigation.js +106 -0
  81. package/{src/components/navigation/index.ts → dist/components/navigation/index.d.ts} +1 -7
  82. package/dist/components/navigation/index.js +4 -0
  83. package/dist/components/navigation/pagination-controls.d.ts +7 -0
  84. package/dist/components/navigation/pagination-controls.js +50 -0
  85. package/dist/components/navigation/tabs.d.ts +7 -0
  86. package/dist/components/navigation/tabs.js +35 -0
  87. package/dist/components/overlays/bottom-sheet.d.ts +20 -0
  88. package/dist/components/overlays/bottom-sheet.js +101 -0
  89. package/dist/components/overlays/command.d.ts +29 -0
  90. package/dist/components/overlays/command.js +123 -0
  91. package/dist/components/overlays/dialog.d.ts +30 -0
  92. package/dist/components/overlays/dialog.js +105 -0
  93. package/dist/components/overlays/dropdown-menu.d.ts +29 -0
  94. package/dist/components/overlays/dropdown-menu.js +128 -0
  95. package/dist/components/overlays/index.d.ts +7 -0
  96. package/dist/components/overlays/index.js +8 -0
  97. package/dist/components/overlays/popover.d.ts +14 -0
  98. package/dist/components/overlays/popover.js +56 -0
  99. package/dist/components/overlays/sheet.d.ts +27 -0
  100. package/dist/components/overlays/sheet.js +81 -0
  101. package/dist/components/overlays/tooltip.d.ts +10 -0
  102. package/dist/components/overlays/tooltip.js +23 -0
  103. package/dist/components/primitives/avatar.d.ts +14 -0
  104. package/dist/components/primitives/avatar.js +72 -0
  105. package/dist/components/primitives/badge.d.ts +7 -0
  106. package/dist/components/primitives/badge.js +27 -0
  107. package/dist/components/primitives/button.d.ts +13 -0
  108. package/dist/components/primitives/button.js +113 -0
  109. package/{src/components/primitives/button.types.ts → dist/components/primitives/button.types.d.ts} +4 -6
  110. package/dist/components/primitives/card.d.ts +9 -0
  111. package/dist/components/primitives/card.js +56 -0
  112. package/{src/components/primitives/index.ts → dist/components/primitives/index.d.ts} +1 -9
  113. package/dist/components/primitives/index.js +7 -0
  114. package/dist/components/primitives/segmented-control.d.ts +20 -0
  115. package/dist/components/primitives/segmented-control.js +27 -0
  116. package/dist/components/primitives/separator.d.ts +4 -0
  117. package/dist/components/primitives/separator.js +16 -0
  118. package/dist/constants/chart-colors.d.ts +33 -0
  119. package/dist/constants/chart-colors.js +31 -0
  120. package/dist/hooks/index.js +9 -0
  121. package/{src/hooks/sort.types.ts → dist/hooks/sort.types.d.ts} +2 -4
  122. package/dist/hooks/use-api-client.d.ts +30 -0
  123. package/dist/hooks/use-api-client.js +106 -0
  124. package/dist/hooks/use-debounced-value.d.ts +1 -0
  125. package/dist/hooks/use-debounced-value.js +12 -0
  126. package/dist/hooks/use-derived-filter-state.d.ts +20 -0
  127. package/dist/hooks/use-derived-filter-state.js +39 -0
  128. package/dist/hooks/use-filter-state.d.ts +12 -0
  129. package/dist/hooks/use-filter-state.js +37 -0
  130. package/dist/hooks/use-media-query.d.ts +6 -0
  131. package/dist/hooks/use-media-query.js +27 -0
  132. package/dist/hooks/use-mobile.d.ts +1 -0
  133. package/dist/hooks/use-mobile.js +14 -0
  134. package/dist/hooks/use-sort.d.ts +11 -0
  135. package/dist/hooks/use-sort.js +14 -0
  136. package/{src/index.ts → dist/index.d.ts} +4 -32
  137. package/dist/index.js +15 -0
  138. package/dist/integrations/service-worker.d.ts +2 -0
  139. package/dist/integrations/service-worker.js +2 -0
  140. package/dist/lib/auth.d.ts +2 -0
  141. package/dist/lib/auth.js +7 -0
  142. package/dist/lib/clipboard.d.ts +1 -0
  143. package/dist/lib/clipboard.js +10 -0
  144. package/dist/lib/index.js +4 -0
  145. package/{src/lib/slot.tsx → dist/lib/slot.d.ts} +2 -15
  146. package/dist/lib/slot.js +14 -0
  147. package/dist/lib/utils.d.ts +2 -0
  148. package/dist/lib/utils.js +8 -0
  149. package/dist/native/components.d.ts +41 -0
  150. package/dist/native/components.js +135 -0
  151. package/dist/native/index.js +3 -0
  152. package/dist/native/theme.d.ts +19 -0
  153. package/dist/native/theme.js +58 -0
  154. package/dist/styles/tokens/css-vars.d.ts +7 -0
  155. package/dist/styles/tokens/css-vars.js +5 -0
  156. package/dist/styles/tokens/generated/dark.colors.generated.d.ts +40 -0
  157. package/dist/styles/tokens/generated/dark.colors.generated.js +39 -0
  158. package/dist/styles/tokens/generated/foundations.generated.d.ts +323 -0
  159. package/dist/styles/tokens/generated/foundations.generated.js +453 -0
  160. package/dist/styles/tokens/generated/light.colors.generated.d.ts +40 -0
  161. package/dist/styles/tokens/generated/light.colors.generated.js +39 -0
  162. package/dist/styles/tokens/index.d.ts +317 -0
  163. package/dist/styles/tokens/index.js +14 -0
  164. package/dist/styles.css +1 -1
  165. package/dist/translations/en.d.ts +34 -0
  166. package/dist/translations/index.d.ts +39 -0
  167. package/dist/translations/ui-provider.d.ts +9 -0
  168. package/dist/translations/ui-provider.js +16 -0
  169. package/dist/types/upload.d.ts +36 -0
  170. package/package.json +50 -33
  171. package/src/components/data-display/accordion.stories.tsx +0 -57
  172. package/src/components/data-display/accordion.tsx +0 -86
  173. package/src/components/data-display/metric-card.stories.tsx +0 -48
  174. package/src/components/data-display/metric-card.tsx +0 -20
  175. package/src/components/data-display/sort-controls.stories.tsx +0 -45
  176. package/src/components/data-display/sort-controls.tsx +0 -96
  177. package/src/components/data-display/sort-row.tsx +0 -88
  178. package/src/components/data-display/table.stories.tsx +0 -75
  179. package/src/components/data-display/table.tsx +0 -80
  180. package/src/components/feedback/alert.stories.tsx +0 -51
  181. package/src/components/feedback/alert.tsx +0 -64
  182. package/src/components/feedback/empty-state.tsx +0 -76
  183. package/src/components/feedback/progress.stories.tsx +0 -54
  184. package/src/components/feedback/progress.tsx +0 -41
  185. package/src/components/feedback/skeleton.stories.tsx +0 -36
  186. package/src/components/feedback/skeleton.tsx +0 -7
  187. package/src/components/feedback/spinner.stories.tsx +0 -52
  188. package/src/components/feedback/spinner.tsx +0 -47
  189. package/src/components/feedback/update-guard.stories.tsx +0 -28
  190. package/src/components/feedback/update-guard.tsx +0 -57
  191. package/src/components/filters/active-filters-bar.stories.tsx +0 -114
  192. package/src/components/filters/active-filters-bar.tsx +0 -61
  193. package/src/components/filters/filter-chip.tsx +0 -57
  194. package/src/components/filters/filter-select.stories.tsx +0 -71
  195. package/src/components/filters/filter-select.tsx +0 -68
  196. package/src/components/forms/checkbox.stories.tsx +0 -35
  197. package/src/components/forms/checkbox.tsx +0 -38
  198. package/src/components/forms/date-month-select.stories.tsx +0 -26
  199. package/src/components/forms/date-month-select.tsx +0 -79
  200. package/src/components/forms/date-picker.stories.tsx +0 -188
  201. package/src/components/forms/date-picker.tsx +0 -303
  202. package/src/components/forms/date-utils.ts +0 -37
  203. package/src/components/forms/drop-zone.stories.tsx +0 -140
  204. package/src/components/forms/drop-zone.tsx +0 -298
  205. package/src/components/forms/input.stories.tsx +0 -47
  206. package/src/components/forms/input.tsx +0 -24
  207. package/src/components/forms/label.stories.tsx +0 -16
  208. package/src/components/forms/label.tsx +0 -25
  209. package/src/components/forms/passkey-management.stories.tsx +0 -85
  210. package/src/components/forms/passkey-management.tsx +0 -168
  211. package/src/components/forms/radio-group.stories.tsx +0 -46
  212. package/src/components/forms/radio-group.tsx +0 -44
  213. package/src/components/forms/select.stories.tsx +0 -207
  214. package/src/components/forms/select.tsx +0 -182
  215. package/src/components/forms/slider.stories.tsx +0 -70
  216. package/src/components/forms/slider.tsx +0 -50
  217. package/src/components/forms/stepper.stories.tsx +0 -111
  218. package/src/components/forms/stepper.tsx +0 -66
  219. package/src/components/forms/switch.stories.tsx +0 -144
  220. package/src/components/forms/switch.tsx +0 -39
  221. package/src/components/forms/text-field.stories.tsx +0 -87
  222. package/src/components/forms/text-field.tsx +0 -75
  223. package/src/components/forms/text-field.types.ts +0 -9
  224. package/src/components/forms/textarea.stories.tsx +0 -150
  225. package/src/components/forms/textarea.tsx +0 -22
  226. package/src/components/layout/particle-background.stories.tsx +0 -38
  227. package/src/components/layout/particle-background.tsx +0 -378
  228. package/src/components/layout/scroll-area.stories.tsx +0 -91
  229. package/src/components/layout/scroll-area.tsx +0 -79
  230. package/src/components/layout/section-intro.stories.tsx +0 -29
  231. package/src/components/layout/section-intro.tsx +0 -31
  232. package/src/components/layout/surface-panel.stories.tsx +0 -32
  233. package/src/components/layout/surface-panel.tsx +0 -23
  234. package/src/components/navigation/app-navigation.stories.tsx +0 -217
  235. package/src/components/navigation/app-navigation.tsx +0 -234
  236. package/src/components/navigation/pagination-controls.stories.tsx +0 -41
  237. package/src/components/navigation/pagination-controls.tsx +0 -46
  238. package/src/components/navigation/tabs.stories.tsx +0 -48
  239. package/src/components/navigation/tabs.tsx +0 -52
  240. package/src/components/overlays/bottom-sheet.stories.tsx +0 -52
  241. package/src/components/overlays/bottom-sheet.tsx +0 -179
  242. package/src/components/overlays/command.stories.tsx +0 -65
  243. package/src/components/overlays/command.tsx +0 -227
  244. package/src/components/overlays/dialog.stories.tsx +0 -148
  245. package/src/components/overlays/dialog.tsx +0 -203
  246. package/src/components/overlays/dropdown-menu.stories.tsx +0 -74
  247. package/src/components/overlays/dropdown-menu.tsx +0 -236
  248. package/src/components/overlays/index.ts +0 -56
  249. package/src/components/overlays/popover.stories.tsx +0 -51
  250. package/src/components/overlays/popover.tsx +0 -78
  251. package/src/components/overlays/sheet.stories.tsx +0 -68
  252. package/src/components/overlays/sheet.tsx +0 -155
  253. package/src/components/overlays/tooltip.stories.tsx +0 -29
  254. package/src/components/overlays/tooltip.tsx +0 -41
  255. package/src/components/primitives/avatar-placeholder.svg +0 -13
  256. package/src/components/primitives/avatar.stories.tsx +0 -72
  257. package/src/components/primitives/avatar.tsx +0 -136
  258. package/src/components/primitives/badge.stories.tsx +0 -43
  259. package/src/components/primitives/badge.tsx +0 -43
  260. package/src/components/primitives/button.stories.tsx +0 -113
  261. package/src/components/primitives/button.tsx +0 -131
  262. package/src/components/primitives/card.stories.tsx +0 -59
  263. package/src/components/primitives/card.tsx +0 -66
  264. package/src/components/primitives/segmented-control.stories.tsx +0 -54
  265. package/src/components/primitives/segmented-control.tsx +0 -73
  266. package/src/components/primitives/separator.stories.tsx +0 -34
  267. package/src/components/primitives/separator.tsx +0 -27
  268. package/src/constants/chart-colors.ts +0 -39
  269. package/src/env.d.ts +0 -9
  270. package/src/hooks/use-api-client.ts +0 -122
  271. package/src/hooks/use-debounced-value.ts +0 -12
  272. package/src/hooks/use-derived-filter-state.ts +0 -70
  273. package/src/hooks/use-filter-state.ts +0 -66
  274. package/src/hooks/use-media-query.ts +0 -69
  275. package/src/hooks/use-mobile.tsx +0 -19
  276. package/src/hooks/use-sort.ts +0 -20
  277. package/src/lib/auth.ts +0 -5
  278. package/src/lib/clipboard.ts +0 -8
  279. package/src/lib/utils.ts +0 -6
  280. package/src/native/components.tsx +0 -198
  281. package/src/native/theme.tsx +0 -69
  282. package/src/storybook/controls.ts +0 -117
  283. package/src/storybook/options.ts +0 -52
  284. package/src/styles/build.ts +0 -1
  285. package/src/styles/index.css +0 -423
  286. package/src/styles/nokia.css +0 -36
  287. package/src/styles/package.css +0 -3
  288. package/src/styles/tokens/chart-colors.stories.tsx +0 -189
  289. package/src/styles/tokens/color-systems.stories.tsx +0 -190
  290. package/src/styles/tokens/css-vars.ts +0 -15
  291. package/src/styles/tokens/generated/dark-class.css +0 -39
  292. package/src/styles/tokens/generated/dark.colors.generated.ts +0 -39
  293. package/src/styles/tokens/generated/dark.css +0 -39
  294. package/src/styles/tokens/generated/foundations.css +0 -149
  295. package/src/styles/tokens/generated/foundations.generated.ts +0 -454
  296. package/src/styles/tokens/generated/light.colors.generated.ts +0 -39
  297. package/src/styles/tokens/generated/light.css +0 -39
  298. package/src/styles/tokens/index.ts +0 -70
  299. package/src/styles/tokens/layout.stories.tsx +0 -50
  300. package/src/styles/tokens/scripts/check-foundations.mjs +0 -126
  301. package/src/styles/tokens/scripts/check-source.mjs +0 -53
  302. package/src/styles/tokens/source/dark.tokens.json +0 -138
  303. package/src/styles/tokens/source/foundations.tokens.json +0 -344
  304. package/src/styles/tokens/source/light.tokens.json +0 -138
  305. package/src/styles/tokens/style-dictionary.dark-class.json +0 -16
  306. package/src/styles/tokens/style-dictionary.dark-storybook.json +0 -16
  307. package/src/styles/tokens/style-dictionary.dark.json +0 -21
  308. package/src/styles/tokens/style-dictionary.foundations.json +0 -21
  309. package/src/styles/tokens/style-dictionary.light-storybook.json +0 -16
  310. package/src/styles/tokens/style-dictionary.light.json +0 -21
  311. package/src/styles/tokens/typography.stories.tsx +0 -157
  312. package/src/translations/en.ts +0 -34
  313. package/src/translations/index.ts +0 -32
  314. package/src/types/upload.ts +0 -42
  315. /package/{src/components/filters/index.ts → dist/components/filters/index.d.ts} +0 -0
  316. /package/{src/components/layout/index.ts → dist/components/layout/index.d.ts} +0 -0
  317. /package/{src/hooks/index.ts → dist/hooks/index.d.ts} +0 -0
  318. /package/{src/lib/index.ts → dist/lib/index.d.ts} +0 -0
  319. /package/{src/native/index.ts → dist/native/index.d.ts} +0 -0
@@ -1,114 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
- import { expect, userEvent, within } from "storybook/test";
3
- import { fn } from "storybook/test";
4
-
5
- import { ActiveFiltersBar } from "./active-filters-bar";
6
-
7
- const meta = {
8
- title: "Filters/ActiveFiltersBar",
9
- component: ActiveFiltersBar,
10
- tags: ["autodocs"],
11
- decorators: [
12
- (Story) => (
13
- <div className="w-[600px]">
14
- <Story />
15
- </div>
16
- ),
17
- ],
18
- } satisfies Meta<typeof ActiveFiltersBar>;
19
-
20
- export default meta;
21
- type Story = StoryObj<typeof meta>;
22
-
23
- export const Default: Story = {
24
- args: {
25
- filters: [
26
- { id: "1", label: "Status: Active", onRemove: fn() },
27
- { id: "2", label: "Created after Jan 1", onRemove: fn() },
28
- ],
29
- onClearAll: fn(),
30
- },
31
- play: async ({ canvasElement }) => {
32
- const canvas = within(canvasElement);
33
- const list = canvas.getByRole("list");
34
- await expect(list).toBeInTheDocument();
35
- const items = within(list).getAllByRole("listitem");
36
- expect(items.length).toBe(2);
37
- await expect(canvas.getByText("Status: Active")).toBeInTheDocument();
38
- await expect(canvas.getByText("Created after Jan 1")).toBeInTheDocument();
39
- await expect(canvas.getByRole("button", { name: "Clear all" })).toBeInTheDocument();
40
- },
41
- };
42
-
43
- export const WithClearAll: Story = {
44
- args: {
45
- filters: [
46
- { id: "1", label: "Status: Active", onRemove: fn() },
47
- { id: "2", label: "Role: Engineer", onRemove: fn() },
48
- ],
49
- onClearAll: fn(),
50
- },
51
- play: async ({ canvasElement, args }) => {
52
- const canvas = within(canvasElement);
53
- const list = canvas.getByRole("list");
54
- const items = within(list).getAllByRole("listitem");
55
- expect(items.length).toBe(2);
56
- const clearButton = canvas.getByRole("button", { name: "Clear all" });
57
- await expect(clearButton).toBeInTheDocument();
58
- await userEvent.click(clearButton);
59
- await expect(args.onClearAll).toHaveBeenCalledTimes(1);
60
- },
61
- };
62
-
63
- export const WithSortOnly: Story = {
64
- args: {
65
- filters: [{ id: "1", label: "Date", onRemove: fn(), variant: "sort", direction: "desc" }],
66
- onClearAll: fn(),
67
- },
68
- play: async ({ canvasElement }) => {
69
- const canvas = within(canvasElement);
70
- const list = canvas.getByRole("list");
71
- const items = within(list).getAllByRole("listitem");
72
- expect(items.length).toBe(1);
73
- await expect(canvas.getByText("Date")).toBeInTheDocument();
74
- await expect(canvas.getByRole("button", { name: "Clear all" })).toBeInTheDocument();
75
- },
76
- };
77
-
78
- export const WithSortAndFilter: Story = {
79
- args: {
80
- filters: [
81
- { id: "filter-1", label: "Status: Active", onRemove: fn(), variant: "filter" },
82
- { id: "sort-1", label: "Date", onRemove: fn(), variant: "sort", direction: "desc" },
83
- ],
84
- onClearAll: fn(),
85
- },
86
- play: async ({ canvasElement }) => {
87
- const canvas = within(canvasElement);
88
- const list = canvas.getByRole("list");
89
- const items = within(list).getAllByRole("listitem");
90
- expect(items.length).toBe(2);
91
- await expect(canvas.getByText("Status: Active")).toBeInTheDocument();
92
- await expect(canvas.getByText("Date")).toBeInTheDocument();
93
- await expect(canvas.getByRole("button", { name: "Clear all" })).toBeInTheDocument();
94
- },
95
- };
96
-
97
- export const Empty: Story = {
98
- args: { filters: [] },
99
- play: async ({ canvasElement }) => {
100
- const canvas = within(canvasElement);
101
- expect(canvas.queryByRole("list")).toBeNull();
102
- },
103
- };
104
-
105
- export const WithClassName: Story = {
106
- args: {
107
- filters: [{ id: "1", label: "Status: Active", onRemove: fn() }],
108
- className: "mt-4",
109
- },
110
- play: async ({ canvasElement }) => {
111
- const container = canvasElement.querySelector(".mt-4");
112
- await expect(container).not.toBeNull();
113
- },
114
- };
@@ -1,61 +0,0 @@
1
- import { X } from "lucide-react";
2
-
3
- import { cn } from "../../lib/utils";
4
- import { FilterChip } from "./filter-chip";
5
-
6
- export interface ActiveFilter {
7
- id: string;
8
- label: string;
9
- onRemove: () => void;
10
- onClick?: () => void;
11
- variant?: "filter" | "sort";
12
- direction?: "asc" | "desc";
13
- }
14
-
15
- export interface ActiveFiltersBarProps {
16
- filters: ActiveFilter[];
17
- className?: string;
18
- onClearAll?: () => void;
19
- }
20
-
21
- export function ActiveFiltersBar({ filters, className, onClearAll }: ActiveFiltersBarProps) {
22
- if (filters.length === 0) {
23
- return null;
24
- }
25
-
26
- return (
27
- <div
28
- className={cn(
29
- "flex w-full items-center h-10 gap-2 rounded-lg border border-border pr-0",
30
- className,
31
- )}
32
- >
33
- <span className="text-muted-foreground shrink-0 self-stretch flex items-center text-sm border-r px-4">
34
- Filters
35
- </span>
36
- <ul className="flex flex-1 min-w-0 items-center gap-2 overflow-x-auto scroll-fade-x pl-4 py-1.5">
37
- {filters.map((filter) => (
38
- <li key={filter.id} className="shrink-0">
39
- <FilterChip
40
- label={filter.label}
41
- onRemove={filter.onRemove}
42
- onClick={filter.onClick}
43
- variant={filter.variant === "sort" ? "sort" : "default"}
44
- direction={filter.direction}
45
- />
46
- </li>
47
- ))}
48
- </ul>
49
- {onClearAll ? (
50
- <button
51
- type="button"
52
- onClick={onClearAll}
53
- className="hover:bg-muted text-muted-foreground hover:text-foreground focus-visible:ring-ring inline-flex shrink-0 items-center justify-center self-stretch aspect-square rounded-r-lg border-l border-border transition-colors focus-visible:ring-2 focus-visible:outline-none"
54
- aria-label="Clear all"
55
- >
56
- <X className="size-4" aria-hidden />
57
- </button>
58
- ) : null}
59
- </div>
60
- );
61
- }
@@ -1,57 +0,0 @@
1
- import { ArrowDown, ArrowUp, X } from "lucide-react";
2
- import type { ReactNode } from "react";
3
-
4
- import { cn } from "../../lib/utils";
5
-
6
- export interface FilterChipProps {
7
- label: ReactNode;
8
- onRemove: () => void;
9
- onClick?: () => void;
10
- className?: string;
11
- variant?: "default" | "sort";
12
- direction?: "asc" | "desc";
13
- role?: string;
14
- }
15
-
16
- export function FilterChip({
17
- label,
18
- onRemove,
19
- onClick,
20
- className,
21
- variant = "default",
22
- direction,
23
- role,
24
- }: FilterChipProps) {
25
- const isSort = variant === "sort";
26
- const DirIcon = direction === "asc" ? ArrowUp : direction === "desc" ? ArrowDown : null;
27
-
28
- return (
29
- <span
30
- role={role}
31
- aria-label={typeof label === "string" ? `${label} filter` : "filter"}
32
- className={cn(
33
- "border-border bg-secondary text-secondary-foreground inline-flex items-center rounded-full border text-xs font-medium",
34
- isSort && "border-dashed",
35
- className,
36
- )}
37
- >
38
- <button
39
- type="button"
40
- disabled={!onClick}
41
- className="focus-visible:ring-ring inline-flex items-center rounded-full px-3 transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-default"
42
- onClick={onClick}
43
- >
44
- {isSort && DirIcon ? <DirIcon className="mr-1 size-4" /> : null}
45
- {label}
46
- </button>
47
- <button
48
- type="button"
49
- className="text-muted-foreground hover:text-foreground focus-visible:ring-ring mr-0.5 flex size-6 items-center justify-center rounded-full transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none"
50
- onClick={onRemove}
51
- aria-label={`Remove filter: ${typeof label === "string" ? label : "selected filter"}`}
52
- >
53
- <X className="size-4" aria-hidden />
54
- </button>
55
- </span>
56
- );
57
- }
@@ -1,71 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
- import { expect, within } from "storybook/test";
3
-
4
- import { FilterSelect } from "./filter-select";
5
-
6
- const meta = {
7
- title: "Filters/FilterSelect",
8
- component: FilterSelect,
9
- tags: ["autodocs"],
10
- args: {
11
- options: [
12
- { value: "active", label: "Active" },
13
- { value: "interview", label: "Interview" },
14
- { value: "offer", label: "Offer" },
15
- { value: "rejected", label: "Rejected" },
16
- { value: "archived", label: "Archived" },
17
- ],
18
- placeholder: "All statuses",
19
- label: "Status",
20
- onChange: () => {},
21
- },
22
- } satisfies Meta<typeof FilterSelect>;
23
-
24
- export default meta;
25
- type Story = StoryObj<typeof meta>;
26
-
27
- export const Default: Story = {
28
- args: { value: null },
29
- play: async ({ canvasElement }) => {
30
- const canvas = within(canvasElement);
31
- const trigger = canvas.getByRole("combobox", { name: "Status" });
32
- await expect(trigger).toBeInTheDocument();
33
- await expect(trigger).toHaveTextContent("All statuses");
34
- },
35
- };
36
-
37
- export const WithValue: Story = {
38
- args: { value: "active" },
39
- play: async ({ canvasElement }) => {
40
- const canvas = within(canvasElement);
41
- const trigger = canvas.getByRole("combobox", { name: "Status" });
42
- await expect(trigger).toHaveTextContent("Active");
43
- },
44
- };
45
-
46
- export const Loading: Story = {
47
- args: { value: null, isLoading: true, options: [] },
48
- play: async ({ canvasElement }) => {
49
- const canvas = within(canvasElement);
50
- await expect(canvas.getByRole("combobox", { name: "Status" })).toBeInTheDocument();
51
- await expect(canvas.getByText("Status")).toBeInTheDocument();
52
- },
53
- };
54
-
55
- export const Empty: Story = {
56
- args: { value: null, options: [], emptyLabel: "No statuses found" },
57
- play: async ({ canvasElement }) => {
58
- const canvas = within(canvasElement);
59
- await expect(canvas.getByRole("combobox", { name: "Status" })).toBeInTheDocument();
60
- await expect(canvas.getByText("Status")).toBeInTheDocument();
61
- },
62
- };
63
-
64
- export const Disabled: Story = {
65
- args: { value: "active", disabled: true },
66
- play: async ({ canvasElement }) => {
67
- const canvas = within(canvasElement);
68
- const trigger = canvas.getByRole("combobox", { name: "Status" });
69
- await expect(trigger).toBeDisabled();
70
- },
71
- };
@@ -1,68 +0,0 @@
1
- import { useId, useMemo } from "react";
2
-
3
- import { cn } from "../../lib/utils";
4
- import { Spinner } from "../feedback/spinner";
5
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../forms/select";
6
-
7
- export interface FilterSelectOption {
8
- value: string;
9
- label: string;
10
- }
11
-
12
- export interface FilterSelectProps {
13
- value: string | null;
14
- onChange: (value: string | null) => void;
15
- options: FilterSelectOption[];
16
- label: string;
17
- placeholder?: string;
18
- disabled?: boolean;
19
- isLoading?: boolean;
20
- emptyLabel?: string;
21
- className?: string;
22
- id?: string;
23
- }
24
-
25
- export function FilterSelect({
26
- label,
27
- value,
28
- options,
29
- onChange,
30
- placeholder = "All",
31
- disabled = false,
32
- isLoading = false,
33
- emptyLabel = "No options available",
34
- className,
35
- id,
36
- }: FilterSelectProps) {
37
- const generatedId = useId();
38
- const selectId = id ?? generatedId;
39
- const items = useMemo(
40
- () => [{ value: null, label: placeholder }, ...options],
41
- [options, placeholder],
42
- );
43
-
44
- return (
45
- <div className={cn("space-y-2 min-w-36", className)}>
46
- <label htmlFor={selectId}>{label}</label>
47
- <Select value={value} onValueChange={onChange} items={items}>
48
- <SelectTrigger id={selectId} disabled={disabled} aria-label={label} className="w-full">
49
- <SelectValue placeholder={placeholder} />
50
- </SelectTrigger>
51
- <SelectContent className="bg-background max-h-[250px] min-w-36 overflow-y-auto">
52
- <SelectItem>{placeholder}</SelectItem>
53
- {isLoading ? (
54
- <Spinner presentation="centered" size="sm" />
55
- ) : options.length === 0 ? (
56
- <div className="text-muted-foreground px-2 py-1.5 text-sm">{emptyLabel}</div>
57
- ) : (
58
- options.map((option) => (
59
- <SelectItem key={option.value} value={option.value}>
60
- {option.label}
61
- </SelectItem>
62
- ))
63
- )}
64
- </SelectContent>
65
- </Select>
66
- </div>
67
- );
68
- }
@@ -1,35 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
-
3
- import { Checkbox } from "./checkbox";
4
- import { Label } from "./label";
5
-
6
- const meta = {
7
- title: "Forms/Checkbox",
8
- component: Checkbox,
9
- tags: ["autodocs"],
10
- } satisfies Meta<typeof Checkbox>;
11
-
12
- export default meta;
13
- type Story = StoryObj<typeof meta>;
14
-
15
- export const Default: Story = {
16
- args: { "aria-label": "Accept terms" },
17
- render: (args) => (
18
- <div className="flex items-center gap-2">
19
- <Checkbox {...args} id="terms" />
20
- <Label htmlFor="terms">Accept terms</Label>
21
- </div>
22
- ),
23
- };
24
-
25
- export const Checked: Story = {
26
- args: { "aria-label": "Checked", defaultChecked: true },
27
- };
28
-
29
- export const Indeterminate: Story = {
30
- args: { "aria-label": "Partially selected", indeterminate: true },
31
- };
32
-
33
- export const Disabled: Story = {
34
- args: { "aria-label": "Disabled", disabled: true },
35
- };
@@ -1,38 +0,0 @@
1
- "use client";
2
-
3
- import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox";
4
- import { CheckIcon, MinusIcon } from "lucide-react";
5
- import * as React from "react";
6
-
7
- import { cn } from "../../lib/utils";
8
-
9
- function Checkbox({ className, ...props }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
10
- return (
11
- <CheckboxPrimitive.Root
12
- data-slot="checkbox"
13
- className={cn(
14
- "border-input bg-background aria-checked:bg-primary aria-checked:text-primary-foreground aria-checked:border-primary focus-visible:outline-ring size-4 shrink-0 rounded-[4px] border transition-colors focus-visible:outline-2 focus-visible:outline-offset-2",
15
- "aria-[checked=mixed]:bg-primary aria-[checked=mixed]:text-primary-foreground aria-[checked=mixed]:border-primary",
16
- "data-disabled:pointer-events-none data-disabled:opacity-50",
17
- className,
18
- )}
19
- {...props}
20
- >
21
- <CheckboxPrimitive.Indicator
22
- data-slot="checkbox-indicator"
23
- className="flex items-center justify-center text-current transition-none"
24
- render={(props, state) => (
25
- <span {...props}>
26
- {state.indeterminate ? (
27
- <MinusIcon className="size-3" />
28
- ) : (
29
- <CheckIcon className="size-3" />
30
- )}
31
- </span>
32
- )}
33
- />
34
- </CheckboxPrimitive.Root>
35
- );
36
- }
37
-
38
- export { Checkbox };
@@ -1,26 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
- import { useState } from "react";
3
-
4
- import { DateMonthSelect, getCurrentMonthYear } from "./date-month-select";
5
-
6
- const meta = {
7
- title: "Forms/DateMonthSelect",
8
- component: DateMonthSelect,
9
- tags: ["autodocs"],
10
- } satisfies Meta<typeof DateMonthSelect>;
11
-
12
- export default meta;
13
- type Story = StoryObj<typeof meta>;
14
-
15
- function DateMonthSelectPreview() {
16
- const [value, setValue] = useState(getCurrentMonthYear());
17
- return <DateMonthSelect selectedMonthYear={value} onMonthChange={setValue} />;
18
- }
19
-
20
- export const Default: Story = {
21
- args: {
22
- selectedMonthYear: getCurrentMonthYear(),
23
- onMonthChange: () => {},
24
- },
25
- render: () => <DateMonthSelectPreview />,
26
- };
@@ -1,79 +0,0 @@
1
- import { Calendar } from "lucide-react";
2
- import { useMemo } from "react";
3
-
4
- import { cn } from "../../lib/utils";
5
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select";
6
-
7
- interface MonthOption {
8
- value: string;
9
- label: string;
10
- }
11
-
12
- export interface DateMonthSelectProps {
13
- selectedMonthYear: string;
14
- onMonthChange: (value: string) => void;
15
- monthsBack?: number;
16
- className?: string;
17
- placeholder?: string;
18
- }
19
-
20
- /** Select a `YYYY-MM` month from the last `monthsBack` months. */
21
- export function DateMonthSelect({
22
- selectedMonthYear,
23
- onMonthChange,
24
- monthsBack = 12,
25
- className = "w-[200px]",
26
- placeholder = "Select month",
27
- }: DateMonthSelectProps) {
28
- const monthOptions = useMemo<MonthOption[]>(() => {
29
- const options: MonthOption[] = [];
30
- const today = new Date();
31
- for (let i = 0; i < monthsBack; i++) {
32
- const date = new Date(today.getFullYear(), today.getMonth() - i, 1);
33
- const year = date.getFullYear();
34
- const month = (date.getMonth() + 1).toString().padStart(2, "0");
35
- options.push({
36
- value: `${year}-${month}`,
37
- label: date.toLocaleString("default", { month: "long", year: "numeric" }),
38
- });
39
- }
40
- return options;
41
- }, [monthsBack]);
42
-
43
- return (
44
- <div className="flex items-center">
45
- <div className="border-border bg-background text-foreground flex size-10 items-center justify-center rounded-l-xl border">
46
- <Calendar className="size-4" />
47
- </div>
48
- <Select
49
- value={selectedMonthYear}
50
- onValueChange={(v: string | null) => v != null && onMonthChange(v)}
51
- >
52
- <SelectTrigger
53
- className={cn(
54
- "border-border bg-background text-foreground min-h-10 max-h-10 rounded-l-none rounded-r-xl border-l-0",
55
- className,
56
- )}
57
- aria-label={placeholder}
58
- >
59
- <SelectValue placeholder={placeholder} />
60
- </SelectTrigger>
61
- <SelectContent>
62
- {monthOptions.map((option) => (
63
- <SelectItem key={option.value} value={option.value}>
64
- {option.label}
65
- </SelectItem>
66
- ))}
67
- </SelectContent>
68
- </Select>
69
- </div>
70
- );
71
- }
72
-
73
- /** Current month in `YYYY-MM` format. */
74
- export const getCurrentMonthYear = () => {
75
- const now = new Date();
76
- const year = now.getFullYear();
77
- const month = (now.getMonth() + 1).toString().padStart(2, "0");
78
- return `${year}-${month}`;
79
- };