@elabs-ai/components-ui 4.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.
- package/LICENSE +21 -0
- package/README.md +78 -0
- package/dist/index.d.ts +4497 -0
- package/dist/index.js +15192 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cn.d.ts +10 -0
- package/dist/lib/cn.js +20 -0
- package/dist/lib/cn.js.map +1 -0
- package/package.json +106 -0
- package/src/blocks/sidebar-02/app-sidebar.tsx +160 -0
- package/src/blocks/sidebar-02/logo.tsx +18 -0
- package/src/blocks/sidebar-02/nav-main.tsx +13 -0
- package/src/blocks/sidebar-02/nav-notifications.tsx +7 -0
- package/src/blocks/sidebar-02/sidebar-02.stories.tsx +23 -0
- package/src/blocks/sidebar-02/team-switcher.tsx +7 -0
- package/src/blocks/sidebar-04/app-sidebar.tsx +236 -0
- package/src/blocks/sidebar-04/mail-context.tsx +29 -0
- package/src/blocks/sidebar-04/nav-user.tsx +6 -0
- package/src/blocks/sidebar-04/sidebar-04.stories.tsx +150 -0
- package/src/blocks/sidebar-05/app-sidebar.tsx +375 -0
- package/src/blocks/sidebar-05/sidebar-05.stories.tsx +18 -0
- package/src/blocks/sidebar-05/team-switcher.tsx +6 -0
- package/src/blocks-comparison-table.stories.tsx +165 -0
- package/src/components/accordion/accordion.stories.tsx +71 -0
- package/src/components/accordion/accordion.test.tsx +18 -0
- package/src/components/accordion/accordion.tsx +49 -0
- package/src/components/accordion/index.ts +1 -0
- package/src/components/advanced-group/advanced-group.stories.tsx +143 -0
- package/src/components/advanced-group/advanced-group.test.tsx +81 -0
- package/src/components/advanced-group/advanced-group.tsx +111 -0
- package/src/components/advanced-group/index.ts +2 -0
- package/src/components/alert/alert.stories.tsx +156 -0
- package/src/components/alert/alert.test.tsx +57 -0
- package/src/components/alert/alert.tsx +91 -0
- package/src/components/alert/index.ts +8 -0
- package/src/components/alert-dialog/alert-dialog.stories.tsx +73 -0
- package/src/components/alert-dialog/alert-dialog.tsx +104 -0
- package/src/components/alert-dialog/index.ts +12 -0
- package/src/components/app-shell/app-shell.stories.tsx +61 -0
- package/src/components/app-shell/app-shell.tsx +31 -0
- package/src/components/app-shell/index.ts +1 -0
- package/src/components/app-sidebar/app-sidebar.stories.tsx +121 -0
- package/src/components/app-sidebar/app-sidebar.test.tsx +55 -0
- package/src/components/app-sidebar/app-sidebar.tsx +53 -0
- package/src/components/app-sidebar/index.ts +1 -0
- package/src/components/aspect-ratio/aspect-ratio.stories.tsx +46 -0
- package/src/components/aspect-ratio/aspect-ratio.tsx +3 -0
- package/src/components/aspect-ratio/index.ts +1 -0
- package/src/components/attribution-panel/attribution-panel.stories.tsx +83 -0
- package/src/components/attribution-panel/attribution-panel.test.tsx +123 -0
- package/src/components/attribution-panel/attribution-panel.tsx +191 -0
- package/src/components/attribution-panel/attribution-types.ts +44 -0
- package/src/components/attribution-panel/attributions.generated.ts +1422 -0
- package/src/components/attribution-panel/index.ts +3 -0
- package/src/components/avatar/avatar.stories.tsx +45 -0
- package/src/components/avatar/avatar.test.tsx +13 -0
- package/src/components/avatar/avatar.tsx +46 -0
- package/src/components/avatar/index.ts +1 -0
- package/src/components/badge/badge.stories.tsx +44 -0
- package/src/components/badge/badge.test.tsx +10 -0
- package/src/components/badge/badge.tsx +28 -0
- package/src/components/badge/index.ts +1 -0
- package/src/components/bento-grid/bento-grid.stories.tsx +165 -0
- package/src/components/bento-grid/bento-grid.test.tsx +119 -0
- package/src/components/bento-grid/bento-grid.tsx +339 -0
- package/src/components/bento-grid/index.ts +9 -0
- package/src/components/bounded-number/bounded-number.stories.tsx +118 -0
- package/src/components/bounded-number/bounded-number.test.tsx +73 -0
- package/src/components/bounded-number/bounded-number.tsx +110 -0
- package/src/components/bounded-number/index.ts +1 -0
- package/src/components/breadcrumb/breadcrumb.stories.tsx +47 -0
- package/src/components/breadcrumb/breadcrumb.test.tsx +17 -0
- package/src/components/breadcrumb/breadcrumb.tsx +86 -0
- package/src/components/breadcrumb/index.ts +9 -0
- package/src/components/button/button.stories.tsx +70 -0
- package/src/components/button/button.test.tsx +50 -0
- package/src/components/button/button.tsx +63 -0
- package/src/components/button/index.ts +1 -0
- package/src/components/button-group/button-group.stories.tsx +45 -0
- package/src/components/button-group/button-group.tsx +70 -0
- package/src/components/button-group/index.ts +6 -0
- package/src/components/calendar/calendar.stories.tsx +95 -0
- package/src/components/calendar/calendar.test.tsx +140 -0
- package/src/components/calendar/calendar.tsx +70 -0
- package/src/components/calendar/index.ts +1 -0
- package/src/components/card/card.stories.tsx +438 -0
- package/src/components/card/card.test.tsx +153 -0
- package/src/components/card/card.tsx +332 -0
- package/src/components/card/index.ts +13 -0
- package/src/components/carousel/carousel.stories.tsx +102 -0
- package/src/components/carousel/carousel.tsx +225 -0
- package/src/components/carousel/index.ts +8 -0
- package/src/components/change-review/change-review.stories.tsx +288 -0
- package/src/components/change-review/change-review.test.tsx +261 -0
- package/src/components/change-review/change-review.tsx +637 -0
- package/src/components/change-review/index.ts +23 -0
- package/src/components/checkbox/checkbox.stories.tsx +119 -0
- package/src/components/checkbox/checkbox.test.tsx +40 -0
- package/src/components/checkbox/checkbox.tsx +34 -0
- package/src/components/checkbox/index.ts +1 -0
- package/src/components/collapsible/collapsible.stories.tsx +62 -0
- package/src/components/collapsible/collapsible.tsx +22 -0
- package/src/components/collapsible/index.ts +1 -0
- package/src/components/collapsible-panel/index.ts +8 -0
- package/src/components/collapsible-panel/use-collapsible-panel.test.tsx +93 -0
- package/src/components/collapsible-panel/use-collapsible-panel.ts +159 -0
- package/src/components/color-picker/color-picker.stories.tsx +93 -0
- package/src/components/color-picker/color-picker.test.tsx +79 -0
- package/src/components/color-picker/color-picker.tsx +341 -0
- package/src/components/color-picker/index.ts +1 -0
- package/src/components/combobox/combobox.stories.tsx +154 -0
- package/src/components/combobox/combobox.test.tsx +246 -0
- package/src/components/combobox/combobox.tsx +210 -0
- package/src/components/combobox/index.ts +1 -0
- package/src/components/command/command.stories.tsx +371 -0
- package/src/components/command/command.test.tsx +280 -0
- package/src/components/command/command.tsx +301 -0
- package/src/components/command/index.ts +13 -0
- package/src/components/confirm-dialog/confirm-dialog.stories.tsx +183 -0
- package/src/components/confirm-dialog/confirm-dialog.test.tsx +147 -0
- package/src/components/confirm-dialog/confirm-dialog.tsx +146 -0
- package/src/components/confirm-dialog/index.ts +2 -0
- package/src/components/context-menu/context-menu.stories.tsx +70 -0
- package/src/components/context-menu/context-menu.tsx +172 -0
- package/src/components/context-menu/index.ts +16 -0
- package/src/components/copyable-value/copyable-value.stories.tsx +80 -0
- package/src/components/copyable-value/copyable-value.test.tsx +128 -0
- package/src/components/copyable-value/copyable-value.tsx +101 -0
- package/src/components/copyable-value/index.ts +1 -0
- package/src/components/date-picker/date-picker.stories.tsx +42 -0
- package/src/components/date-picker/date-picker.tsx +49 -0
- package/src/components/date-picker/index.ts +1 -0
- package/src/components/date-range-picker/date-range-picker.stories.tsx +151 -0
- package/src/components/date-range-picker/date-range-picker.test.tsx +62 -0
- package/src/components/date-range-picker/date-range-picker.tsx +126 -0
- package/src/components/date-range-picker/index.ts +6 -0
- package/src/components/descriptions/descriptions.stories.tsx +43 -0
- package/src/components/descriptions/descriptions.test.tsx +41 -0
- package/src/components/descriptions/descriptions.tsx +95 -0
- package/src/components/descriptions/index.ts +6 -0
- package/src/components/dialog/dialog.stories.tsx +728 -0
- package/src/components/dialog/dialog.test.tsx +285 -0
- package/src/components/dialog/dialog.tsx +340 -0
- package/src/components/dialog/index.ts +18 -0
- package/src/components/dialog/use-dialog-dismiss-guard.test.tsx +80 -0
- package/src/components/dialog/use-dialog-dismiss-guard.ts +109 -0
- package/src/components/drawer/drawer.stories.tsx +76 -0
- package/src/components/drawer/drawer.tsx +85 -0
- package/src/components/drawer/index.ts +11 -0
- package/src/components/dropdown-menu/dropdown-menu.stories.tsx +176 -0
- package/src/components/dropdown-menu/dropdown-menu.test.tsx +109 -0
- package/src/components/dropdown-menu/dropdown-menu.tsx +179 -0
- package/src/components/dropdown-menu/index.ts +17 -0
- package/src/components/empty-state/empty-state.stories.tsx +54 -0
- package/src/components/empty-state/empty-state.test.tsx +11 -0
- package/src/components/empty-state/empty-state.tsx +30 -0
- package/src/components/empty-state/index.ts +1 -0
- package/src/components/error-state/error-state.stories.tsx +51 -0
- package/src/components/error-state/error-state.tsx +27 -0
- package/src/components/error-state/index.ts +1 -0
- package/src/components/expand-dialog/expand-dialog.stories.tsx +246 -0
- package/src/components/expand-dialog/expand-dialog.test.tsx +185 -0
- package/src/components/expand-dialog/expand-dialog.tsx +387 -0
- package/src/components/expand-dialog/index.ts +11 -0
- package/src/components/field-row/field-row.stories.tsx +114 -0
- package/src/components/field-row/field-row.test.tsx +188 -0
- package/src/components/field-row/field-row.tsx +115 -0
- package/src/components/field-row/index.ts +1 -0
- package/src/components/file-upload/file-upload.stories.tsx +183 -0
- package/src/components/file-upload/file-upload.test.tsx +169 -0
- package/src/components/file-upload/file-upload.tsx +459 -0
- package/src/components/file-upload/index.ts +13 -0
- package/src/components/form/form.stories.tsx +71 -0
- package/src/components/form/form.tsx +141 -0
- package/src/components/form/index.ts +10 -0
- package/src/components/hover-card/hover-card.stories.tsx +57 -0
- package/src/components/hover-card/hover-card.tsx +25 -0
- package/src/components/hover-card/index.ts +1 -0
- package/src/components/icon-button/icon-button.stories.tsx +108 -0
- package/src/components/icon-button/icon-button.test.tsx +93 -0
- package/src/components/icon-button/icon-button.tsx +139 -0
- package/src/components/icon-button/index.ts +1 -0
- package/src/components/input/index.ts +1 -0
- package/src/components/input/input.stories.tsx +44 -0
- package/src/components/input/input.test.tsx +19 -0
- package/src/components/input/input.tsx +26 -0
- package/src/components/input-group/index.ts +11 -0
- package/src/components/input-group/input-group.stories.tsx +87 -0
- package/src/components/input-group/input-group.test.tsx +48 -0
- package/src/components/input-group/input-group.tsx +170 -0
- package/src/components/input-otp/index.ts +1 -0
- package/src/components/input-otp/input-otp.stories.tsx +66 -0
- package/src/components/input-otp/input-otp.tsx +68 -0
- package/src/components/kbd/index.ts +1 -0
- package/src/components/kbd/kbd.stories.tsx +45 -0
- package/src/components/kbd/kbd.tsx +20 -0
- package/src/components/key-value-editor/index.ts +1 -0
- package/src/components/key-value-editor/key-value-editor.stories.tsx +130 -0
- package/src/components/key-value-editor/key-value-editor.test.tsx +231 -0
- package/src/components/key-value-editor/key-value-editor.tsx +263 -0
- package/src/components/label/index.ts +1 -0
- package/src/components/label/label.stories.tsx +35 -0
- package/src/components/label/label.tsx +19 -0
- package/src/components/link-preview/index.ts +2 -0
- package/src/components/link-preview/link-preview.stories.tsx +178 -0
- package/src/components/link-preview/link-preview.test.tsx +123 -0
- package/src/components/link-preview/link-preview.tsx +194 -0
- package/src/components/list-editor/index.ts +1 -0
- package/src/components/list-editor/list-editor.stories.tsx +119 -0
- package/src/components/list-editor/list-editor.test.tsx +96 -0
- package/src/components/list-editor/list-editor.tsx +192 -0
- package/src/components/loading-state/index.ts +1 -0
- package/src/components/loading-state/loading-state.stories.tsx +38 -0
- package/src/components/loading-state/loading-state.tsx +26 -0
- package/src/components/locale-provider/index.ts +8 -0
- package/src/components/locale-provider/locale-provider.stories.tsx +83 -0
- package/src/components/locale-provider/locale-provider.test.tsx +238 -0
- package/src/components/locale-provider/locale-provider.tsx +187 -0
- package/src/components/locale-provider/messages.ts +300 -0
- package/src/components/match-highlight/index.ts +7 -0
- package/src/components/match-highlight/match-highlight.stories.tsx +146 -0
- package/src/components/match-highlight/match-highlight.test.tsx +202 -0
- package/src/components/match-highlight/match-highlight.tsx +145 -0
- package/src/components/mention-input/index.ts +28 -0
- package/src/components/mention-input/mention-input-mirror.stories.tsx +171 -0
- package/src/components/mention-input/mention-input.stories.tsx +463 -0
- package/src/components/mention-input/mention-input.test.tsx +617 -0
- package/src/components/mention-input/mention-input.tsx +1102 -0
- package/src/components/mention-input/mention-value.test.ts +253 -0
- package/src/components/mention-input/mention-value.ts +193 -0
- package/src/components/menubar/index.ts +17 -0
- package/src/components/menubar/menubar.stories.tsx +67 -0
- package/src/components/menubar/menubar.tsx +198 -0
- package/src/components/metric-card/index.ts +1 -0
- package/src/components/metric-card/metric-card.stories.tsx +224 -0
- package/src/components/metric-card/metric-card.test.tsx +198 -0
- package/src/components/metric-card/metric-card.tsx +229 -0
- package/src/components/model-picker/index.ts +11 -0
- package/src/components/model-picker/model-picker-state.ts +85 -0
- package/src/components/model-picker/model-picker.stories.tsx +128 -0
- package/src/components/model-picker/model-picker.test.tsx +241 -0
- package/src/components/model-picker/model-picker.tsx +270 -0
- package/src/components/nav-main/index.ts +1 -0
- package/src/components/nav-main/nav-main.tsx +147 -0
- package/src/components/nav-notifications/index.ts +5 -0
- package/src/components/nav-notifications/nav-notifications.test.tsx +15 -0
- package/src/components/nav-notifications/nav-notifications.tsx +70 -0
- package/src/components/nav-user/index.ts +1 -0
- package/src/components/nav-user/nav-user.test.tsx +29 -0
- package/src/components/nav-user/nav-user.tsx +114 -0
- package/src/components/navigation-menu/index.ts +10 -0
- package/src/components/navigation-menu/navigation-menu.stories.tsx +92 -0
- package/src/components/navigation-menu/navigation-menu.tsx +99 -0
- package/src/components/number-input/index.ts +1 -0
- package/src/components/number-input/number-input.stories.tsx +120 -0
- package/src/components/number-input/number-input.test.tsx +96 -0
- package/src/components/number-input/number-input.tsx +219 -0
- package/src/components/page-shell/index.ts +1 -0
- package/src/components/page-shell/page-shell.stories.tsx +105 -0
- package/src/components/page-shell/page-shell.test.tsx +57 -0
- package/src/components/page-shell/page-shell.tsx +62 -0
- package/src/components/pagination/index.ts +9 -0
- package/src/components/pagination/pagination.stories.tsx +57 -0
- package/src/components/pagination/pagination.tsx +77 -0
- package/src/components/popover/index.ts +1 -0
- package/src/components/popover/popover.stories.tsx +28 -0
- package/src/components/popover/popover.test.tsx +16 -0
- package/src/components/popover/popover.tsx +29 -0
- package/src/components/progress/index.ts +1 -0
- package/src/components/progress/progress.stories.tsx +80 -0
- package/src/components/progress/progress.test.tsx +35 -0
- package/src/components/progress/progress.tsx +50 -0
- package/src/components/radio-group/index.ts +1 -0
- package/src/components/radio-group/radio-group.stories.tsx +75 -0
- package/src/components/radio-group/radio-group.test.tsx +16 -0
- package/src/components/radio-group/radio-group.tsx +33 -0
- package/src/components/rating/index.ts +1 -0
- package/src/components/rating/rating.stories.tsx +111 -0
- package/src/components/rating/rating.test.tsx +41 -0
- package/src/components/rating/rating.tsx +226 -0
- package/src/components/resizable/index.ts +1 -0
- package/src/components/resizable/resizable.stories.tsx +25 -0
- package/src/components/resizable/resizable.tsx +40 -0
- package/src/components/reveal/index.ts +7 -0
- package/src/components/reveal/reveal.stories.tsx +90 -0
- package/src/components/reveal/reveal.test.tsx +43 -0
- package/src/components/reveal/reveal.tsx +95 -0
- package/src/components/revision-timeline/index.ts +10 -0
- package/src/components/revision-timeline/revision-timeline.stories.tsx +785 -0
- package/src/components/revision-timeline/revision-timeline.test.tsx +509 -0
- package/src/components/revision-timeline/revision-timeline.tsx +1107 -0
- package/src/components/scroll-area/index.ts +1 -0
- package/src/components/scroll-area/scroll-area.stories.tsx +45 -0
- package/src/components/scroll-area/scroll-area.tsx +43 -0
- package/src/components/section-header/index.ts +1 -0
- package/src/components/section-header/section-header.stories.tsx +20 -0
- package/src/components/section-header/section-header.tsx +36 -0
- package/src/components/segmented-field/index.ts +5 -0
- package/src/components/segmented-field/segmented-field.stories.tsx +121 -0
- package/src/components/segmented-field/segmented-field.test.tsx +163 -0
- package/src/components/segmented-field/segmented-field.tsx +235 -0
- package/src/components/select/index.ts +10 -0
- package/src/components/select/select.stories.tsx +106 -0
- package/src/components/select/select.test.tsx +98 -0
- package/src/components/select/select.tsx +230 -0
- package/src/components/separator/index.ts +2 -0
- package/src/components/separator/separator.stories.tsx +87 -0
- package/src/components/separator/separator.tsx +51 -0
- package/src/components/sheet/index.ts +12 -0
- package/src/components/sheet/sheet.stories.tsx +65 -0
- package/src/components/sheet/sheet.tsx +109 -0
- package/src/components/sidebar/index.ts +27 -0
- package/src/components/sidebar/sidebar.stories.tsx +69 -0
- package/src/components/sidebar/sidebar.test.tsx +43 -0
- package/src/components/sidebar/sidebar.tsx +688 -0
- package/src/components/skeleton/index.ts +1 -0
- package/src/components/skeleton/skeleton.stories.tsx +28 -0
- package/src/components/skeleton/skeleton.tsx +15 -0
- package/src/components/slider/index.ts +1 -0
- package/src/components/slider/slider.stories.tsx +211 -0
- package/src/components/slider/slider.test.tsx +168 -0
- package/src/components/slider/slider.tsx +136 -0
- package/src/components/slider-number/index.ts +1 -0
- package/src/components/slider-number/slider-number.stories.tsx +123 -0
- package/src/components/slider-number/slider-number.test.tsx +165 -0
- package/src/components/slider-number/slider-number.tsx +191 -0
- package/src/components/sonner/index.ts +1 -0
- package/src/components/sonner/sonner.stories.tsx +124 -0
- package/src/components/sonner/sonner.test.tsx +76 -0
- package/src/components/sonner/sonner.tsx +101 -0
- package/src/components/spinner/index.ts +1 -0
- package/src/components/spinner/spinner.stories.tsx +27 -0
- package/src/components/spinner/spinner.tsx +19 -0
- package/src/components/split-panel/index.ts +1 -0
- package/src/components/split-panel/split-panel.stories.tsx +60 -0
- package/src/components/split-panel/split-panel.test.tsx +38 -0
- package/src/components/split-panel/split-panel.tsx +95 -0
- package/src/components/state-panel/index.ts +1 -0
- package/src/components/state-panel/state-panel.stories.tsx +80 -0
- package/src/components/state-panel/state-panel.test.tsx +64 -0
- package/src/components/state-panel/state-panel.tsx +167 -0
- package/src/components/status-badge/index.ts +18 -0
- package/src/components/status-badge/status-badge.stories.tsx +132 -0
- package/src/components/status-badge/status-badge.test.tsx +189 -0
- package/src/components/status-badge/status-badge.tsx +330 -0
- package/src/components/switch/index.ts +1 -0
- package/src/components/switch/switch.stories.tsx +58 -0
- package/src/components/switch/switch.test.tsx +12 -0
- package/src/components/switch/switch.tsx +29 -0
- package/src/components/table/index.ts +10 -0
- package/src/components/table/table.stories.tsx +53 -0
- package/src/components/table/table.test.tsx +17 -0
- package/src/components/table/table.tsx +106 -0
- package/src/components/tabs/index.ts +1 -0
- package/src/components/tabs/tabs.stories.tsx +267 -0
- package/src/components/tabs/tabs.test.tsx +94 -0
- package/src/components/tabs/tabs.tsx +170 -0
- package/src/components/tag-input/index.ts +1 -0
- package/src/components/tag-input/tag-input.stories.tsx +120 -0
- package/src/components/tag-input/tag-input.test.tsx +97 -0
- package/src/components/tag-input/tag-input.tsx +253 -0
- package/src/components/team-switcher/index.ts +1 -0
- package/src/components/team-switcher/team-switcher.test.tsx +39 -0
- package/src/components/team-switcher/team-switcher.tsx +100 -0
- package/src/components/textarea/index.ts +1 -0
- package/src/components/textarea/textarea.stories.tsx +42 -0
- package/src/components/textarea/textarea.tsx +24 -0
- package/src/components/theme-switcher/index.ts +2 -0
- package/src/components/theme-switcher/theme-switcher.stories.tsx +146 -0
- package/src/components/theme-switcher/theme-switcher.test.tsx +253 -0
- package/src/components/theme-switcher/theme-switcher.tsx +280 -0
- package/src/components/theme-switcher/use-theme-transition.ts +62 -0
- package/src/components/timeline/index.ts +13 -0
- package/src/components/timeline/timeline.stories.tsx +99 -0
- package/src/components/timeline/timeline.test.tsx +196 -0
- package/src/components/timeline/timeline.tsx +188 -0
- package/src/components/toggle/index.ts +1 -0
- package/src/components/toggle/toggle.stories.tsx +116 -0
- package/src/components/toggle/toggle.test.tsx +119 -0
- package/src/components/toggle/toggle.tsx +64 -0
- package/src/components/toggle-group/index.ts +1 -0
- package/src/components/toggle-group/toggle-group.stories.tsx +110 -0
- package/src/components/toggle-group/toggle-group.tsx +59 -0
- package/src/components/toolbar/index.ts +14 -0
- package/src/components/toolbar/toolbar.stories.tsx +126 -0
- package/src/components/toolbar/toolbar.test.tsx +73 -0
- package/src/components/toolbar/toolbar.tsx +177 -0
- package/src/components/tooltip/index.ts +1 -0
- package/src/components/tooltip/tooltip.stories.tsx +67 -0
- package/src/components/tooltip/tooltip.tsx +27 -0
- package/src/components/top-nav/index.ts +1 -0
- package/src/components/top-nav/top-nav.stories.tsx +18 -0
- package/src/components/top-nav/top-nav.tsx +34 -0
- package/src/components/transfer/index.ts +2 -0
- package/src/components/transfer/transfer.stories.tsx +162 -0
- package/src/components/transfer/transfer.test.tsx +177 -0
- package/src/components/transfer/transfer.tsx +481 -0
- package/src/components/tree/index.ts +6 -0
- package/src/components/tree/tree.stories.tsx +618 -0
- package/src/components/tree/tree.test.tsx +699 -0
- package/src/components/tree/tree.tsx +1149 -0
- package/src/components/tree/use-tree-keyboard.ts +314 -0
- package/src/components/tree-select/index.ts +1 -0
- package/src/components/tree-select/tree-select.stories.tsx +127 -0
- package/src/components/tree-select/tree-select.test.tsx +40 -0
- package/src/components/tree-select/tree-select.tsx +137 -0
- package/src/components/typography/index.ts +31 -0
- package/src/components/typography/prose.test.tsx +48 -0
- package/src/components/typography/prose.tsx +194 -0
- package/src/components/typography/typography.stories.tsx +244 -0
- package/src/components/typography/typography.test.tsx +140 -0
- package/src/components/typography/typography.tsx +126 -0
- package/src/components/view-toolbar/index.ts +10 -0
- package/src/components/view-toolbar/view-toolbar.stories.tsx +342 -0
- package/src/components/view-toolbar/view-toolbar.test.tsx +262 -0
- package/src/components/view-toolbar/view-toolbar.tsx +320 -0
- package/src/components/virtual-select/index.ts +2 -0
- package/src/components/virtual-select/use-virtual-listbox.ts +132 -0
- package/src/components/virtual-select/virtual-select.stories.tsx +203 -0
- package/src/components/virtual-select/virtual-select.test.tsx +106 -0
- package/src/components/virtual-select/virtual-select.tsx +361 -0
- package/src/components/wizard/index.ts +12 -0
- package/src/components/wizard/wizard.stories.tsx +148 -0
- package/src/components/wizard/wizard.test.tsx +67 -0
- package/src/components/wizard/wizard.tsx +354 -0
- package/src/decoration.stories.tsx +236 -0
- package/src/index.ts +174 -0
- package/src/lib/cn.test.ts +39 -0
- package/src/lib/cn.ts +31 -0
- package/src/lib/compact-number.ts +61 -0
- package/src/lib/document-address.test.ts +75 -0
- package/src/lib/document-address.ts +265 -0
- package/src/lib/download.ts +93 -0
- package/src/lib/file-icon.test.ts +45 -0
- package/src/lib/file-icon.ts +58 -0
- package/src/lib/file-kind.test.ts +98 -0
- package/src/lib/file-kind.ts +303 -0
- package/src/lib/file-source.test.ts +217 -0
- package/src/lib/file-source.ts +269 -0
- package/src/lib/merge-refs.test.ts +37 -0
- package/src/lib/merge-refs.ts +17 -0
- package/src/lib/streamdown-translations.ts +99 -0
- package/src/lib/use-copy-to-clipboard.ts +75 -0
- package/src/lib/use-mobile.ts +16 -0
- package/src/motion.stories.tsx +266 -0
- package/src/taste-profile.test.tsx +252 -0
- package/src/templates-object-detail-hub.stories.tsx +466 -0
- package/src/templates-screen-states.stories.tsx +306 -0
- package/src/templates-settings.stories.tsx +245 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typography primitives — the semantic type-role API (#187/#188, research 07 §B.3).
|
|
3
|
+
*
|
|
4
|
+
* `Text` and `Heading` map intent → a type ROLE. Each `text-<role>` utility is
|
|
5
|
+
* backed by the `--text-<role>` tokens in @elabs-ai/components-tokens and bundles all four
|
|
6
|
+
* dimensions (size + leading + weight + tracking) while staying composable —
|
|
7
|
+
* `leading-*` / `font-*` / `tracking-*` overrides still win. Reach for these
|
|
8
|
+
* primitives (or a `text-<role>` utility) — never a raw size utility (sm/xl/…).
|
|
9
|
+
*/
|
|
10
|
+
import { forwardRef, type HTMLAttributes } from "react";
|
|
11
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
12
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
13
|
+
import { cn } from "../../lib/cn";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The role font sizes in rem — a TS mirror of the `--text-<role>` tokens
|
|
17
|
+
* (packages/tokens/src/themes.css) for consumers that need the numbers at
|
|
18
|
+
* runtime (e.g. @elabs-ai/components-editor's markdown-scale CSS-var seam). Edit the tokens
|
|
19
|
+
* first; typography.test.tsx pins this mirror to themes.css.
|
|
20
|
+
*/
|
|
21
|
+
export const TEXT_ROLE_REM = {
|
|
22
|
+
display: 1.875,
|
|
23
|
+
title: 1.25,
|
|
24
|
+
subtitle: 1,
|
|
25
|
+
body: 0.875,
|
|
26
|
+
caption: 0.8125,
|
|
27
|
+
meta: 0.75,
|
|
28
|
+
kpi: 2,
|
|
29
|
+
code: 0.8125,
|
|
30
|
+
} as const;
|
|
31
|
+
|
|
32
|
+
export type TextRole = keyof typeof TEXT_ROLE_REM;
|
|
33
|
+
|
|
34
|
+
export const textVariants = cva("", {
|
|
35
|
+
variants: {
|
|
36
|
+
variant: {
|
|
37
|
+
/** A lead paragraph — the subtitle rung at body weight (research 07 §B.3). */
|
|
38
|
+
lead: "text-subtitle font-normal",
|
|
39
|
+
body: "text-body",
|
|
40
|
+
caption: "text-caption",
|
|
41
|
+
meta: "text-meta",
|
|
42
|
+
/** KPI values pair with tabular figures (interaction-guidelines). */
|
|
43
|
+
kpi: "text-kpi tabular-nums",
|
|
44
|
+
/** Inline/block code pairs with the `--font-mono` seam. */
|
|
45
|
+
code: "text-code font-mono",
|
|
46
|
+
},
|
|
47
|
+
tone: {
|
|
48
|
+
default: "text-foreground",
|
|
49
|
+
muted: "text-muted-foreground",
|
|
50
|
+
/** #399 — the on-surface TEXT rung, not the `--primary` FILL. */
|
|
51
|
+
primary: "text-primary-text",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
defaultVariants: { variant: "body", tone: "default" },
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
export type TextVariant = NonNullable<VariantProps<typeof textVariants>["variant"]>;
|
|
58
|
+
export type TextTone = NonNullable<VariantProps<typeof textVariants>["tone"]>;
|
|
59
|
+
|
|
60
|
+
export interface TextProps
|
|
61
|
+
extends HTMLAttributes<HTMLParagraphElement>, VariantProps<typeof textVariants> {
|
|
62
|
+
/** Render as a different element. Defaults to "p". */
|
|
63
|
+
as?: "p" | "span" | "div";
|
|
64
|
+
/** Render the child element as the text root (Radix Slot) instead. */
|
|
65
|
+
asChild?: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const Text = forwardRef<HTMLParagraphElement, TextProps>(function Text(
|
|
69
|
+
{ className, variant, tone, as: Tag = "p", asChild = false, ...props },
|
|
70
|
+
ref,
|
|
71
|
+
) {
|
|
72
|
+
const Comp = asChild ? Slot : Tag;
|
|
73
|
+
return <Comp ref={ref} className={cn(textVariants({ variant, tone }), className)} {...props} />;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
77
|
+
|
|
78
|
+
export const headingVariants = cva("text-balance text-foreground", {
|
|
79
|
+
variants: {
|
|
80
|
+
size: {
|
|
81
|
+
/** display + title carry the `--font-display` brand seam (research 07 §E.5). */
|
|
82
|
+
display: "font-display text-display",
|
|
83
|
+
title: "font-display text-title",
|
|
84
|
+
subtitle: "text-subtitle",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
defaultVariants: { size: "title" },
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
export type HeadingSize = NonNullable<VariantProps<typeof headingVariants>["size"]>;
|
|
91
|
+
|
|
92
|
+
/** Default visual rung per heading level: 1 → display, 2–3 → title, 4–6 → subtitle. */
|
|
93
|
+
const LEVEL_SIZE: Record<HeadingLevel, HeadingSize> = {
|
|
94
|
+
1: "display",
|
|
95
|
+
2: "title",
|
|
96
|
+
3: "title",
|
|
97
|
+
4: "subtitle",
|
|
98
|
+
5: "subtitle",
|
|
99
|
+
6: "subtitle",
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export interface HeadingProps
|
|
103
|
+
extends HTMLAttributes<HTMLHeadingElement>, VariantProps<typeof headingVariants> {
|
|
104
|
+
/** Semantic heading level — drives the rendered tag AND the default size. */
|
|
105
|
+
level?: HeadingLevel;
|
|
106
|
+
/** Render the child element as the heading root (Radix Slot) instead. */
|
|
107
|
+
asChild?: boolean;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* `level` keeps the a11y heading order; `size` overrides the visual rung
|
|
112
|
+
* independently, so a visually-small `<h2>` stays an `<h2>`.
|
|
113
|
+
*/
|
|
114
|
+
export const Heading = forwardRef<HTMLHeadingElement, HeadingProps>(function Heading(
|
|
115
|
+
{ className, level = 2, size, asChild = false, ...props },
|
|
116
|
+
ref,
|
|
117
|
+
) {
|
|
118
|
+
const Comp = asChild ? Slot : (`h${level}` as const);
|
|
119
|
+
return (
|
|
120
|
+
<Comp
|
|
121
|
+
ref={ref}
|
|
122
|
+
className={cn(headingVariants({ size: size ?? LEVEL_SIZE[level] }), className)}
|
|
123
|
+
{...props}
|
|
124
|
+
/>
|
|
125
|
+
);
|
|
126
|
+
});
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
3
|
+
import { Download, Plus, SlidersHorizontal } from "lucide-react";
|
|
4
|
+
import { expect, userEvent, waitFor } from "storybook/test";
|
|
5
|
+
import { FilterChip, ResultCount, ViewToolbar, ViewToolbarFilters } from "./view-toolbar";
|
|
6
|
+
import { Button } from "../button";
|
|
7
|
+
import { StatePanel } from "../state-panel";
|
|
8
|
+
import { StatusBadge } from "../status-badge";
|
|
9
|
+
|
|
10
|
+
const meta = {
|
|
11
|
+
title: "Layout/ViewToolbar",
|
|
12
|
+
component: ViewToolbar,
|
|
13
|
+
tags: ["autodocs"],
|
|
14
|
+
parameters: { layout: "padded" },
|
|
15
|
+
argTypes: {
|
|
16
|
+
info: {
|
|
17
|
+
description: "What this view is — shown in a tooltip on the ⓘ trigger.",
|
|
18
|
+
control: "text",
|
|
19
|
+
table: { category: "Content" },
|
|
20
|
+
},
|
|
21
|
+
children: {
|
|
22
|
+
description: "Left cluster — status/context, ViewToolbarFilters, ResultCount.",
|
|
23
|
+
control: false,
|
|
24
|
+
table: { category: "Content" },
|
|
25
|
+
},
|
|
26
|
+
actions: {
|
|
27
|
+
description: "Right cluster — the view's actions. Never clipped at narrow widths.",
|
|
28
|
+
control: false,
|
|
29
|
+
table: { category: "Content" },
|
|
30
|
+
},
|
|
31
|
+
className: {
|
|
32
|
+
description: "Extra Tailwind classes merged via cn() — e.g. the sticky-bar recipe.",
|
|
33
|
+
control: "text",
|
|
34
|
+
table: { category: "Appearance" },
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
} satisfies Meta<typeof ViewToolbar>;
|
|
38
|
+
export default meta;
|
|
39
|
+
type Story = StoryObj<typeof meta>;
|
|
40
|
+
|
|
41
|
+
const VIEW_INFO =
|
|
42
|
+
"Every pipeline run in this workspace from the last 30 days, newest first. Runs are removed after 90 days.";
|
|
43
|
+
|
|
44
|
+
function ViewActions() {
|
|
45
|
+
return (
|
|
46
|
+
<>
|
|
47
|
+
<Button variant="outline" size="sm">
|
|
48
|
+
<SlidersHorizontal aria-hidden="true" />
|
|
49
|
+
Filters
|
|
50
|
+
</Button>
|
|
51
|
+
<Button variant="outline" size="sm">
|
|
52
|
+
<Download aria-hidden="true" />
|
|
53
|
+
Export
|
|
54
|
+
</Button>
|
|
55
|
+
<Button size="sm">
|
|
56
|
+
<Plus aria-hidden="true" />
|
|
57
|
+
New run
|
|
58
|
+
</Button>
|
|
59
|
+
</>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Two chips, statically rendered — used by the read-only theme stories. */
|
|
64
|
+
function StaticFilters() {
|
|
65
|
+
return (
|
|
66
|
+
<ViewToolbarFilters onClearAll={() => undefined}>
|
|
67
|
+
<FilterChip label="Status: Failed" onRemove={() => undefined} />
|
|
68
|
+
<FilterChip label="Owner: Data platform" onRemove={() => undefined} />
|
|
69
|
+
</ViewToolbarFilters>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The whole grammar in one row: ⓘ info · status · active filters · result count
|
|
75
|
+
* on the left, actions on the right. The count reads "24 of 128" because a
|
|
76
|
+
* filter is active — a bare "24" would misrepresent the view.
|
|
77
|
+
*/
|
|
78
|
+
export const Default: Story = {
|
|
79
|
+
render: function DefaultToolbar() {
|
|
80
|
+
const [filters, setFilters] = useState(["Status: Failed", "Owner: Data platform"]);
|
|
81
|
+
const filtered = filters.length > 0;
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<ViewToolbar info={VIEW_INFO} actions={<ViewActions />} className="w-full max-w-4xl">
|
|
85
|
+
<StatusBadge status="running" size="sm" />
|
|
86
|
+
{filtered ? (
|
|
87
|
+
<ViewToolbarFilters onClearAll={() => setFilters([])}>
|
|
88
|
+
{filters.map((filter) => (
|
|
89
|
+
<FilterChip
|
|
90
|
+
key={filter}
|
|
91
|
+
label={filter}
|
|
92
|
+
onRemove={() => setFilters((current) => current.filter((f) => f !== filter))}
|
|
93
|
+
/>
|
|
94
|
+
))}
|
|
95
|
+
</ViewToolbarFilters>
|
|
96
|
+
) : null}
|
|
97
|
+
<ResultCount count={filtered ? 24 : 128} total={filtered ? 128 : undefined}>
|
|
98
|
+
runs
|
|
99
|
+
</ResultCount>
|
|
100
|
+
</ViewToolbar>
|
|
101
|
+
);
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The minimum viable row: actions only. No info, no filters, no count — the row
|
|
107
|
+
* still holds its `min-h-9` floor so it lines up with a populated sibling view.
|
|
108
|
+
*/
|
|
109
|
+
export const ActionsOnly: Story = {
|
|
110
|
+
render: () => <ViewToolbar actions={<ViewActions />} className="w-full max-w-4xl" />,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* **No filters applied** — the filters region is ABSENT, not an empty box, and
|
|
115
|
+
* the count is a bare total ("128 runs"), because "128 of 128" is noise.
|
|
116
|
+
*/
|
|
117
|
+
export const NoFiltersApplied: Story = {
|
|
118
|
+
render: () => (
|
|
119
|
+
<ViewToolbar info={VIEW_INFO} actions={<ViewActions />} className="w-full max-w-4xl">
|
|
120
|
+
<StatusBadge status="complete" size="sm" />
|
|
121
|
+
<ResultCount count={128}>runs</ResultCount>
|
|
122
|
+
</ViewToolbar>
|
|
123
|
+
),
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* **Empty results.** The count still tells the truth ("0 of 128") and the chips
|
|
128
|
+
* STAY, so the filter that emptied the view is the thing the user can undo. The
|
|
129
|
+
* toolbar does not own the empty state itself — that is a `StatePanel` below it.
|
|
130
|
+
*/
|
|
131
|
+
export const EmptyResults: Story = {
|
|
132
|
+
render: function EmptyResultsToolbar() {
|
|
133
|
+
const [filters, setFilters] = useState(["Status: Denied", "Owner: Platform SRE"]);
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<div className="flex w-full max-w-4xl flex-col gap-4">
|
|
137
|
+
<ViewToolbar info={VIEW_INFO} actions={<ViewActions />}>
|
|
138
|
+
{filters.length > 0 ? (
|
|
139
|
+
<ViewToolbarFilters onClearAll={() => setFilters([])}>
|
|
140
|
+
{filters.map((filter) => (
|
|
141
|
+
<FilterChip
|
|
142
|
+
key={filter}
|
|
143
|
+
label={filter}
|
|
144
|
+
onRemove={() => setFilters((current) => current.filter((f) => f !== filter))}
|
|
145
|
+
/>
|
|
146
|
+
))}
|
|
147
|
+
</ViewToolbarFilters>
|
|
148
|
+
) : null}
|
|
149
|
+
<ResultCount count={0} total={128}>
|
|
150
|
+
runs
|
|
151
|
+
</ResultCount>
|
|
152
|
+
</ViewToolbar>
|
|
153
|
+
<StatePanel
|
|
154
|
+
kind="empty"
|
|
155
|
+
title="No runs match these filters"
|
|
156
|
+
description="Remove a filter above to widen the search."
|
|
157
|
+
/>
|
|
158
|
+
</div>
|
|
159
|
+
);
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* **Not ready.** `ResultCount loading` renders a layout-shaped placeholder
|
|
165
|
+
* instead of a "0" that lies while the first page is in flight, and announces
|
|
166
|
+
* the wait once at the region rather than per skeleton box.
|
|
167
|
+
*/
|
|
168
|
+
export const ResultCountLoading: Story = {
|
|
169
|
+
render: () => (
|
|
170
|
+
<ViewToolbar info={VIEW_INFO} actions={<ViewActions />} className="w-full max-w-4xl">
|
|
171
|
+
<StatusBadge status="running" size="sm" />
|
|
172
|
+
<ResultCount count={0} total={128} loading />
|
|
173
|
+
</ViewToolbar>
|
|
174
|
+
),
|
|
175
|
+
play: async ({ canvas }) => {
|
|
176
|
+
await expect(canvas.getByRole("status")).toBeInTheDocument();
|
|
177
|
+
await expect(canvas.queryByText("0 of 128")).toBeNull();
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* **Overflowing content at a narrow width.** Long chip labels truncate, the chip
|
|
183
|
+
* run wraps inside the filters group, and the two clusters stack instead of the
|
|
184
|
+
* left one being squeezed to nothing. The play function asserts the AC directly:
|
|
185
|
+
* the last action is still inside the toolbar's box, and nothing in the row
|
|
186
|
+
* clips it.
|
|
187
|
+
*/
|
|
188
|
+
export const Overflowing: Story = {
|
|
189
|
+
render: function OverflowingToolbar() {
|
|
190
|
+
const [filters, setFilters] = useState([
|
|
191
|
+
"Status: Failed",
|
|
192
|
+
"Owner: Data platform engineering",
|
|
193
|
+
"Environment: Production (eu-west-1)",
|
|
194
|
+
"Trigger: Scheduled — nightly full refresh",
|
|
195
|
+
"Tag: needs-review",
|
|
196
|
+
]);
|
|
197
|
+
|
|
198
|
+
return (
|
|
199
|
+
<div className="w-full max-w-sm rounded-lg border border-border p-3">
|
|
200
|
+
<ViewToolbar info={VIEW_INFO} actions={<ViewActions />}>
|
|
201
|
+
{filters.length > 0 ? (
|
|
202
|
+
<ViewToolbarFilters onClearAll={() => setFilters([])}>
|
|
203
|
+
{filters.map((filter) => (
|
|
204
|
+
<FilterChip
|
|
205
|
+
key={filter}
|
|
206
|
+
label={filter}
|
|
207
|
+
onRemove={() => setFilters((current) => current.filter((f) => f !== filter))}
|
|
208
|
+
/>
|
|
209
|
+
))}
|
|
210
|
+
</ViewToolbarFilters>
|
|
211
|
+
) : null}
|
|
212
|
+
<ResultCount count={3} total={128}>
|
|
213
|
+
runs
|
|
214
|
+
</ResultCount>
|
|
215
|
+
</ViewToolbar>
|
|
216
|
+
</div>
|
|
217
|
+
);
|
|
218
|
+
},
|
|
219
|
+
play: async ({ canvas }) => {
|
|
220
|
+
const action = canvas.getByRole("button", { name: "New run" });
|
|
221
|
+
const toolbar = action.closest("[data-slot='view-toolbar']") as HTMLElement | null;
|
|
222
|
+
|
|
223
|
+
await expect(action).toBeVisible();
|
|
224
|
+
await expect(toolbar).not.toBeNull();
|
|
225
|
+
|
|
226
|
+
/** The AC in geometric terms: the last action is inside the row's own box. */
|
|
227
|
+
const assertActionInsideRow = () => {
|
|
228
|
+
const toolbarBox = toolbar!.getBoundingClientRect();
|
|
229
|
+
const actionBox = action.getBoundingClientRect();
|
|
230
|
+
expect(actionBox.width).toBeGreaterThan(0);
|
|
231
|
+
expect(actionBox.right).toBeLessThanOrEqual(Math.ceil(toolbarBox.right) + 1);
|
|
232
|
+
expect(actionBox.left).toBeGreaterThanOrEqual(Math.floor(toolbarBox.left) - 1);
|
|
233
|
+
expect(actionBox.bottom).toBeLessThanOrEqual(Math.ceil(toolbarBox.bottom) + 1);
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
assertActionInsideRow();
|
|
237
|
+
|
|
238
|
+
// Nothing between the action and the row may clip it away.
|
|
239
|
+
for (
|
|
240
|
+
let node: HTMLElement | null = action;
|
|
241
|
+
node && node !== toolbar!.parentElement;
|
|
242
|
+
node = node.parentElement
|
|
243
|
+
) {
|
|
244
|
+
await expect(getComputedStyle(node).overflow).not.toBe("hidden");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/*
|
|
248
|
+
* Squeeze the row to a phone width, INSIDE the test, because the runner's
|
|
249
|
+
* viewport is wide enough to hide the bug this locks: with a `shrink-0`
|
|
250
|
+
* action cluster whose buttons could not wrap, the cluster overflowed the
|
|
251
|
+
* row's right edge by ~8px at a 320px viewport — the action was "reachable"
|
|
252
|
+
* only because nothing happened to clip it. It now wraps instead.
|
|
253
|
+
*/
|
|
254
|
+
const frame = toolbar!.parentElement as HTMLElement;
|
|
255
|
+
const restore = frame.style.maxWidth;
|
|
256
|
+
frame.style.maxWidth = "260px";
|
|
257
|
+
await waitFor(() => expect(toolbar!.getBoundingClientRect().width).toBeLessThan(260));
|
|
258
|
+
assertActionInsideRow();
|
|
259
|
+
frame.style.maxWidth = restore;
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* **Keyboard removal (AC).** The whole chip is one `<button>`, so Enter and
|
|
265
|
+
* Space both remove it and there is exactly one tab stop per active filter.
|
|
266
|
+
*/
|
|
267
|
+
export const KeyboardRemoval: Story = {
|
|
268
|
+
render: function KeyboardToolbar() {
|
|
269
|
+
const [filters, setFilters] = useState(["Status: Failed", "Owner: Data platform"]);
|
|
270
|
+
const filtered = filters.length > 0;
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<ViewToolbar actions={<ViewActions />} className="w-full max-w-4xl">
|
|
274
|
+
{filtered ? (
|
|
275
|
+
<ViewToolbarFilters onClearAll={() => setFilters([])}>
|
|
276
|
+
{filters.map((filter) => (
|
|
277
|
+
<FilterChip
|
|
278
|
+
key={filter}
|
|
279
|
+
label={filter}
|
|
280
|
+
onRemove={() => setFilters((current) => current.filter((f) => f !== filter))}
|
|
281
|
+
/>
|
|
282
|
+
))}
|
|
283
|
+
</ViewToolbarFilters>
|
|
284
|
+
) : null}
|
|
285
|
+
<ResultCount count={filtered ? 24 : 128} total={filtered ? 128 : undefined}>
|
|
286
|
+
runs
|
|
287
|
+
</ResultCount>
|
|
288
|
+
</ViewToolbar>
|
|
289
|
+
);
|
|
290
|
+
},
|
|
291
|
+
play: async ({ canvas }) => {
|
|
292
|
+
const first = canvas.getByRole("button", { name: "Remove filter: Status: Failed" });
|
|
293
|
+
await expect(first.tagName).toBe("BUTTON");
|
|
294
|
+
first.focus();
|
|
295
|
+
await expect(first).toHaveFocus();
|
|
296
|
+
await userEvent.keyboard("{Enter}");
|
|
297
|
+
await waitFor(() =>
|
|
298
|
+
expect(canvas.queryByRole("button", { name: "Remove filter: Status: Failed" })).toBeNull(),
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
const second = canvas.getByRole("button", { name: "Remove filter: Owner: Data platform" });
|
|
302
|
+
second.focus();
|
|
303
|
+
await userEvent.keyboard(" ");
|
|
304
|
+
await waitFor(() =>
|
|
305
|
+
expect(
|
|
306
|
+
canvas.queryByRole("button", { name: "Remove filter: Owner: Data platform" }),
|
|
307
|
+
).toBeNull(),
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
// With no filters left the region disappears and the count drops "of M".
|
|
311
|
+
await expect(canvas.queryByRole("group", { name: "Active filters" })).toBeNull();
|
|
312
|
+
await expect(canvas.getByText("128 runs")).toBeInTheDocument();
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
/** The same row on `dark`. */
|
|
317
|
+
export const DarkTheme: Story = {
|
|
318
|
+
globals: { theme: "dark" },
|
|
319
|
+
render: () => (
|
|
320
|
+
<ViewToolbar info={VIEW_INFO} actions={<ViewActions />} className="w-full max-w-4xl">
|
|
321
|
+
<StatusBadge status="complete" size="sm" />
|
|
322
|
+
<StaticFilters />
|
|
323
|
+
<ResultCount count={24} total={128}>
|
|
324
|
+
runs
|
|
325
|
+
</ResultCount>
|
|
326
|
+
</ViewToolbar>
|
|
327
|
+
),
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
/** The same row at decoration 10 — drawn, not filled. */
|
|
331
|
+
export const HighDecoration: Story = {
|
|
332
|
+
globals: { decoration: "10" },
|
|
333
|
+
render: () => (
|
|
334
|
+
<ViewToolbar info={VIEW_INFO} actions={<ViewActions />} className="w-full max-w-4xl">
|
|
335
|
+
<StatusBadge status="complete" size="sm" />
|
|
336
|
+
<StaticFilters />
|
|
337
|
+
<ResultCount count={24} total={128}>
|
|
338
|
+
runs
|
|
339
|
+
</ResultCount>
|
|
340
|
+
</ViewToolbar>
|
|
341
|
+
),
|
|
342
|
+
};
|