@sovereignfs/ui 0.83.1 → 0.89.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.
- package/LICENSE +373 -661
- package/NOTICE +8 -0
- package/README.md +4 -6
- package/dist/AppsLauncher.module.css +6 -0
- package/dist/Button.module.css +5 -0
- package/dist/Card.module.css +6 -0
- package/dist/CardTile.module.css +6 -0
- package/dist/CheckableListRow.module.css +8 -0
- package/dist/Checkbox.module.css +8 -0
- package/dist/CodeTextarea.module.css +6 -0
- package/dist/ColorPicker.module.css +6 -0
- package/dist/Combobox.module.css +6 -0
- package/dist/Command.module.css +6 -0
- package/dist/ConfirmDialog.module.css +6 -0
- package/dist/DatePicker.module.css +6 -0
- package/dist/Dialog.module.css +1 -1
- package/dist/DragHandleRow.module.css +6 -0
- package/dist/FileDropzone.module.css +25 -1
- package/dist/Header.module.css +6 -0
- package/dist/IconPicker.module.css +14 -4
- package/dist/Input.module.css +6 -0
- package/dist/LegalLinks.module.css +9 -2
- package/dist/Markdown.module.css +5 -0
- package/dist/Message.module.css +9 -0
- package/dist/MobileAppsDrawer.module.css +9 -5
- package/dist/MobileFooter.module.css +6 -0
- package/dist/NavList.module.css +11 -2
- package/dist/NavTabs.module.css +7 -1
- package/dist/NotificationsPanel.module.css +10 -0
- package/dist/QrCode.module.css +17 -0
- package/dist/QuantityStepper.module.css +4 -2
- package/dist/RadioGroup.module.css +6 -0
- package/dist/SegmentedControl.module.css +6 -0
- package/dist/Select.module.css +6 -0
- package/dist/Sheet.module.css +16 -1
- package/dist/SplitPane.module.css +1 -1
- package/dist/StepStrip.module.css +5 -3
- package/dist/SuggestionInput.module.css +12 -1
- package/dist/SwipableMobileCarouselDots.module.css +6 -0
- package/dist/SystemBanner.module.css +6 -0
- package/dist/Tabs.module.css +6 -0
- package/dist/TagInput.module.css +6 -0
- package/dist/Textarea.module.css +6 -0
- package/dist/Toast.module.css +5 -0
- package/dist/Toggle.module.css +7 -0
- package/dist/Tooltip.module.css +6 -0
- package/dist/UserMenu.module.css +5 -0
- package/dist/fonts/OFL.txt +93 -0
- package/dist/fonts/hanken-grotesk-cyrillic-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-vietnamese-wght-normal.woff2 +0 -0
- package/dist/index.d.ts +121 -43
- package/dist/index.js +1410 -1152
- package/dist/tokens/fonts.css +53 -0
- package/dist/tokens/primitives.css +12 -12
- package/dist/tokens.css +2 -1
- package/package.json +24 -13
- package/src/__tests__/motion.test.tsx +0 -105
- package/src/__tests__/package-json.test.ts +0 -8
- package/src/components/Accordion/Accordion.module.css +0 -3
- package/src/components/Accordion/Accordion.stories.tsx +0 -110
- package/src/components/Accordion/Accordion.tsx +0 -53
- package/src/components/Accordion/__tests__/Accordion.test.tsx +0 -61
- package/src/components/Alert/Alert.module.css +0 -62
- package/src/components/Alert/Alert.stories.tsx +0 -96
- package/src/components/Alert/Alert.tsx +0 -58
- package/src/components/Alert/__tests__/Alert.test.tsx +0 -75
- package/src/components/AppsLauncher/AppsLauncher.module.css +0 -96
- package/src/components/AppsLauncher/AppsLauncher.stories.tsx +0 -58
- package/src/components/AppsLauncher/AppsLauncher.tsx +0 -117
- package/src/components/AppsLauncher/__tests__/AppsLauncher.test.tsx +0 -52
- package/src/components/AspectRatio/AspectRatio.module.css +0 -15
- package/src/components/AspectRatio/AspectRatio.stories.tsx +0 -78
- package/src/components/AspectRatio/AspectRatio.tsx +0 -21
- package/src/components/AspectRatio/__tests__/AspectRatio.test.tsx +0 -27
- package/src/components/Avatar/Avatar.module.css +0 -38
- package/src/components/Avatar/Avatar.tsx +0 -42
- package/src/components/Badge/Badge.module.css +0 -177
- package/src/components/Badge/Badge.stories.tsx +0 -238
- package/src/components/Badge/Badge.tsx +0 -84
- package/src/components/Badge/__tests__/Badge.test.tsx +0 -90
- package/src/components/BalanceChip/BalanceChip.module.css +0 -32
- package/src/components/BalanceChip/BalanceChip.stories.tsx +0 -45
- package/src/components/BalanceChip/BalanceChip.tsx +0 -33
- package/src/components/Breadcrumb/Breadcrumb.module.css +0 -49
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +0 -47
- package/src/components/Breadcrumb/Breadcrumb.tsx +0 -57
- package/src/components/Breadcrumb/__tests__/Breadcrumb.test.tsx +0 -52
- package/src/components/Button/Button.module.css +0 -169
- package/src/components/Button/Button.stories.tsx +0 -83
- package/src/components/Button/Button.tsx +0 -52
- package/src/components/Button/__tests__/Button.test.tsx +0 -66
- package/src/components/ButtonGroup/ButtonGroup.module.css +0 -29
- package/src/components/ButtonGroup/ButtonGroup.stories.tsx +0 -57
- package/src/components/ButtonGroup/ButtonGroup.tsx +0 -27
- package/src/components/ButtonGroup/__tests__/ButtonGroup.test.tsx +0 -29
- package/src/components/Calendar/Calendar.module.css +0 -126
- package/src/components/Calendar/Calendar.tsx +0 -213
- package/src/components/Calendar/__tests__/Calendar.test.tsx +0 -98
- package/src/components/Calendar/dateUtils.ts +0 -97
- package/src/components/Card/Card.module.css +0 -42
- package/src/components/Card/Card.tsx +0 -34
- package/src/components/CardTile/CardTile.module.css +0 -181
- package/src/components/CardTile/CardTile.stories.tsx +0 -86
- package/src/components/CardTile/CardTile.tsx +0 -128
- package/src/components/CardTile/__tests__/CardTile.test.tsx +0 -56
- package/src/components/CheckableListRow/CheckableListRow.module.css +0 -95
- package/src/components/CheckableListRow/CheckableListRow.stories.tsx +0 -99
- package/src/components/CheckableListRow/CheckableListRow.tsx +0 -89
- package/src/components/Checkbox/Checkbox.module.css +0 -111
- package/src/components/Checkbox/Checkbox.tsx +0 -78
- package/src/components/CodeTextarea/CodeTextarea.module.css +0 -53
- package/src/components/CodeTextarea/CodeTextarea.stories.tsx +0 -77
- package/src/components/CodeTextarea/CodeTextarea.tsx +0 -36
- package/src/components/Collapsible/Collapsible.module.css +0 -70
- package/src/components/Collapsible/Collapsible.stories.tsx +0 -62
- package/src/components/Collapsible/Collapsible.tsx +0 -66
- package/src/components/Collapsible/__tests__/Collapsible.test.tsx +0 -71
- package/src/components/ColorPicker/ColorPicker.module.css +0 -132
- package/src/components/ColorPicker/ColorPicker.stories.tsx +0 -80
- package/src/components/ColorPicker/ColorPicker.tsx +0 -157
- package/src/components/ColorPicker/__tests__/ColorPicker.test.tsx +0 -117
- package/src/components/Combobox/Combobox.module.css +0 -122
- package/src/components/Combobox/Combobox.stories.tsx +0 -79
- package/src/components/Combobox/Combobox.tsx +0 -186
- package/src/components/Combobox/__tests__/Combobox.test.tsx +0 -158
- package/src/components/Command/Command.module.css +0 -80
- package/src/components/Command/Command.stories.tsx +0 -104
- package/src/components/Command/Command.tsx +0 -168
- package/src/components/Command/__tests__/Command.test.tsx +0 -162
- package/src/components/ConfirmDialog/ConfirmDialog.module.css +0 -113
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +0 -150
- package/src/components/ConfirmDialog/__tests__/ConfirmDialog.test.tsx +0 -214
- package/src/components/ConsentPrompt/ConsentPrompt.module.css +0 -21
- package/src/components/ConsentPrompt/ConsentPrompt.stories.tsx +0 -31
- package/src/components/ConsentPrompt/ConsentPrompt.tsx +0 -66
- package/src/components/ContextMenu/ContextMenu.stories.tsx +0 -81
- package/src/components/ContextMenu/ContextMenu.tsx +0 -100
- package/src/components/ContextMenu/__tests__/ContextMenu.test.tsx +0 -87
- package/src/components/CurrencyInput/CurrencyInput.stories.tsx +0 -50
- package/src/components/CurrencyInput/CurrencyInput.tsx +0 -60
- package/src/components/DataTable/DataTable.module.css +0 -28
- package/src/components/DataTable/DataTable.stories.tsx +0 -74
- package/src/components/DataTable/DataTable.tsx +0 -154
- package/src/components/DataTable/__tests__/DataTable.test.tsx +0 -123
- package/src/components/DatePicker/DatePicker.module.css +0 -61
- package/src/components/DatePicker/DatePicker.tsx +0 -103
- package/src/components/DatePicker/__tests__/DatePicker.test.tsx +0 -89
- package/src/components/DeviceOnlyGate/DeviceOnlyGate.tsx +0 -58
- package/src/components/DeviceOnlyGate/__tests__/DeviceOnlyGate.test.tsx +0 -45
- package/src/components/DeviceStorageKeyGate/DeviceStorageKeyGate.tsx +0 -100
- package/src/components/DeviceStorageKeyGate/__tests__/DeviceStorageKeyGate.test.tsx +0 -119
- package/src/components/Dialog/Dialog.module.css +0 -335
- package/src/components/Dialog/Dialog.stories.tsx +0 -324
- package/src/components/Dialog/Dialog.tsx +0 -254
- package/src/components/Dialog/__tests__/Dialog.test.tsx +0 -318
- package/src/components/DragHandleRow/DragHandleRow.module.css +0 -57
- package/src/components/DragHandleRow/DragHandleRow.tsx +0 -42
- package/src/components/Drawer/Drawer.module.css +0 -121
- package/src/components/Drawer/Drawer.stories.tsx +0 -132
- package/src/components/Drawer/Drawer.tsx +0 -175
- package/src/components/Drawer/__tests__/Drawer.test.tsx +0 -226
- package/src/components/EmptyState/EmptyState.module.css +0 -27
- package/src/components/EmptyState/EmptyState.tsx +0 -27
- package/src/components/FileDropzone/FileDropzone.module.css +0 -63
- package/src/components/FileDropzone/FileDropzone.tsx +0 -109
- package/src/components/FileDropzone/__tests__/FileDropzone.test.tsx +0 -67
- package/src/components/FormField/FormField.module.css +0 -33
- package/src/components/FormField/FormField.tsx +0 -78
- package/src/components/FormField/__tests__/FormField.test.tsx +0 -83
- package/src/components/GripIcon/GripIcon.tsx +0 -27
- package/src/components/Header/Header.module.css +0 -62
- package/src/components/Header/Header.stories.tsx +0 -452
- package/src/components/Header/Header.tsx +0 -102
- package/src/components/Header/__tests__/Header.test.tsx +0 -104
- package/src/components/HeaderFooterLayout/HeaderFooterLayout.module.css +0 -21
- package/src/components/HeaderFooterLayout/HeaderFooterLayout.stories.tsx +0 -111
- package/src/components/HeaderFooterLayout/HeaderFooterLayout.tsx +0 -54
- package/src/components/HeaderFooterLayout/__tests__/HeaderFooterLayout.test.tsx +0 -58
- package/src/components/HoverCard/HoverCard.module.css +0 -3
- package/src/components/HoverCard/HoverCard.stories.tsx +0 -66
- package/src/components/HoverCard/HoverCard.tsx +0 -111
- package/src/components/HoverCard/__tests__/HoverCard.test.tsx +0 -99
- package/src/components/Icon/Icon.module.css +0 -27
- package/src/components/Icon/Icon.stories.tsx +0 -95
- package/src/components/Icon/Icon.tsx +0 -43
- package/src/components/Icon/__tests__/Icon.test.tsx +0 -40
- package/src/components/Icon/icons/activity.tsx +0 -20
- package/src/components/Icon/icons/alert-triangle.tsx +0 -22
- package/src/components/Icon/icons/apple.tsx +0 -21
- package/src/components/Icon/icons/arrow-left-right.tsx +0 -23
- package/src/components/Icon/icons/banana.tsx +0 -21
- package/src/components/Icon/icons/beef.tsx +0 -22
- package/src/components/Icon/icons/beer.tsx +0 -24
- package/src/components/Icon/icons/bell.tsx +0 -21
- package/src/components/Icon/icons/bold.tsx +0 -20
- package/src/components/Icon/icons/calendar.tsx +0 -23
- package/src/components/Icon/icons/candy.tsx +0 -24
- package/src/components/Icon/icons/carrot.tsx +0 -22
- package/src/components/Icon/icons/check.tsx +0 -20
- package/src/components/Icon/icons/chevron-down.tsx +0 -20
- package/src/components/Icon/icons/chevron-left.tsx +0 -20
- package/src/components/Icon/icons/chevron-right.tsx +0 -20
- package/src/components/Icon/icons/chevron-up.tsx +0 -20
- package/src/components/Icon/icons/circle-check.tsx +0 -21
- package/src/components/Icon/icons/circle-chevron-left.tsx +0 -21
- package/src/components/Icon/icons/circle-x.tsx +0 -22
- package/src/components/Icon/icons/code.tsx +0 -21
- package/src/components/Icon/icons/coffee.tsx +0 -23
- package/src/components/Icon/icons/cookie.tsx +0 -25
- package/src/components/Icon/icons/copy.tsx +0 -21
- package/src/components/Icon/icons/credit-card.tsx +0 -21
- package/src/components/Icon/icons/croissant.tsx +0 -24
- package/src/components/Icon/icons/cup-soda.tsx +0 -23
- package/src/components/Icon/icons/database.tsx +0 -22
- package/src/components/Icon/icons/download.tsx +0 -22
- package/src/components/Icon/icons/drumstick.tsx +0 -21
- package/src/components/Icon/icons/egg.tsx +0 -20
- package/src/components/Icon/icons/ellipsis-vertical.tsx +0 -22
- package/src/components/Icon/icons/external-link.tsx +0 -22
- package/src/components/Icon/icons/eye-off.tsx +0 -23
- package/src/components/Icon/icons/eye.tsx +0 -21
- package/src/components/Icon/icons/file-text.tsx +0 -24
- package/src/components/Icon/icons/file.tsx +0 -21
- package/src/components/Icon/icons/fish.tsx +0 -25
- package/src/components/Icon/icons/folder-closed.tsx +0 -21
- package/src/components/Icon/icons/folder-open.tsx +0 -20
- package/src/components/Icon/icons/folder.tsx +0 -20
- package/src/components/Icon/icons/folders.tsx +0 -21
- package/src/components/Icon/icons/grid-2x2.tsx +0 -22
- package/src/components/Icon/icons/hat-glasses.tsx +0 -24
- package/src/components/Icon/icons/history.tsx +0 -22
- package/src/components/Icon/icons/house.tsx +0 -21
- package/src/components/Icon/icons/inbox.tsx +0 -21
- package/src/components/Icon/icons/index.ts +0 -394
- package/src/components/Icon/icons/info.tsx +0 -22
- package/src/components/Icon/icons/italic.tsx +0 -22
- package/src/components/Icon/icons/layers.tsx +0 -22
- package/src/components/Icon/icons/layout-dashboard.tsx +0 -23
- package/src/components/Icon/icons/layout-grid.tsx +0 -23
- package/src/components/Icon/icons/link.tsx +0 -21
- package/src/components/Icon/icons/list-ordered.tsx +0 -25
- package/src/components/Icon/icons/list.tsx +0 -25
- package/src/components/Icon/icons/lock.tsx +0 -21
- package/src/components/Icon/icons/log-out.tsx +0 -22
- package/src/components/Icon/icons/luggage.tsx +0 -24
- package/src/components/Icon/icons/mail.tsx +0 -21
- package/src/components/Icon/icons/map-pin.tsx +0 -21
- package/src/components/Icon/icons/menu.tsx +0 -22
- package/src/components/Icon/icons/milk.tsx +0 -22
- package/src/components/Icon/icons/minus.tsx +0 -20
- package/src/components/Icon/icons/package.tsx +0 -23
- package/src/components/Icon/icons/paint-bucket.tsx +0 -23
- package/src/components/Icon/icons/panel-left.tsx +0 -21
- package/src/components/Icon/icons/pencil.tsx +0 -21
- package/src/components/Icon/icons/pin.tsx +0 -21
- package/src/components/Icon/icons/pizza.tsx +0 -24
- package/src/components/Icon/icons/plus.tsx +0 -21
- package/src/components/Icon/icons/rectangle-ellipsis.tsx +0 -23
- package/src/components/Icon/icons/refresh-cw.tsx +0 -23
- package/src/components/Icon/icons/rotate-ccw.tsx +0 -21
- package/src/components/Icon/icons/rotate-cw.tsx +0 -21
- package/src/components/Icon/icons/route.tsx +0 -22
- package/src/components/Icon/icons/salad.tsx +0 -24
- package/src/components/Icon/icons/search.tsx +0 -21
- package/src/components/Icon/icons/send.tsx +0 -21
- package/src/components/Icon/icons/settings.tsx +0 -21
- package/src/components/Icon/icons/share-2.tsx +0 -24
- package/src/components/Icon/icons/sheet.tsx +0 -24
- package/src/components/Icon/icons/shield.tsx +0 -20
- package/src/components/Icon/icons/shopping-basket.tsx +0 -26
- package/src/components/Icon/icons/sliders-horizontal.tsx +0 -28
- package/src/components/Icon/icons/smartphone.tsx +0 -21
- package/src/components/Icon/icons/snowflake.tsx +0 -31
- package/src/components/Icon/icons/spray-can.tsx +0 -29
- package/src/components/Icon/icons/square-kanban.tsx +0 -23
- package/src/components/Icon/icons/table.tsx +0 -23
- package/src/components/Icon/icons/trash-2.tsx +0 -24
- package/src/components/Icon/icons/upload.tsx +0 -22
- package/src/components/Icon/icons/user-round-plus.tsx +0 -23
- package/src/components/Icon/icons/user.tsx +0 -21
- package/src/components/Icon/icons/users.tsx +0 -23
- package/src/components/Icon/icons/wine.tsx +0 -23
- package/src/components/Icon/icons/x.tsx +0 -21
- package/src/components/IconPicker/IconPicker.module.css +0 -63
- package/src/components/IconPicker/IconPicker.stories.tsx +0 -84
- package/src/components/IconPicker/IconPicker.tsx +0 -90
- package/src/components/Input/Input.module.css +0 -52
- package/src/components/Input/Input.stories.tsx +0 -86
- package/src/components/Input/Input.tsx +0 -20
- package/src/components/Input/__tests__/Input.test.tsx +0 -22
- package/src/components/Item/Item.module.css +0 -70
- package/src/components/Item/Item.stories.tsx +0 -62
- package/src/components/Item/Item.tsx +0 -54
- package/src/components/Item/__tests__/Item.test.tsx +0 -39
- package/src/components/Kbd/Kbd.module.css +0 -14
- package/src/components/Kbd/Kbd.stories.tsx +0 -32
- package/src/components/Kbd/Kbd.tsx +0 -12
- package/src/components/Kbd/__tests__/Kbd.test.tsx +0 -14
- package/src/components/Label/Label.module.css +0 -11
- package/src/components/Label/Label.stories.tsx +0 -40
- package/src/components/Label/Label.tsx +0 -24
- package/src/components/Label/__tests__/Label.test.tsx +0 -32
- package/src/components/LegalLinks/LegalLinks.module.css +0 -24
- package/src/components/LegalLinks/LegalLinks.stories.tsx +0 -22
- package/src/components/LegalLinks/LegalLinks.tsx +0 -40
- package/src/components/Markdown/Markdown.module.css +0 -73
- package/src/components/Markdown/Markdown.stories.tsx +0 -40
- package/src/components/Markdown/Markdown.tsx +0 -305
- package/src/components/Markdown/__tests__/Markdown.test.tsx +0 -144
- package/src/components/Marker/Marker.module.css +0 -33
- package/src/components/Marker/Marker.stories.tsx +0 -39
- package/src/components/Marker/Marker.tsx +0 -34
- package/src/components/Marker/__tests__/Marker.test.tsx +0 -23
- package/src/components/MemberMultiSelect/MemberMultiSelect.module.css +0 -27
- package/src/components/MemberMultiSelect/MemberMultiSelect.stories.tsx +0 -74
- package/src/components/MemberMultiSelect/MemberMultiSelect.tsx +0 -67
- package/src/components/Menu/Menu.module.css +0 -90
- package/src/components/Menu/Menu.tsx +0 -209
- package/src/components/Menu/__tests__/Menu.test.tsx +0 -227
- package/src/components/Menubar/Menubar.stories.tsx +0 -52
- package/src/components/Menubar/Menubar.tsx +0 -42
- package/src/components/Menubar/__tests__/Menubar.test.tsx +0 -45
- package/src/components/Message/Message.module.css +0 -99
- package/src/components/Message/Message.stories.tsx +0 -79
- package/src/components/Message/Message.tsx +0 -41
- package/src/components/Message/__tests__/Message.test.tsx +0 -46
- package/src/components/MessageScroller/MessageScroller.module.css +0 -65
- package/src/components/MessageScroller/MessageScroller.stories.tsx +0 -84
- package/src/components/MessageScroller/MessageScroller.tsx +0 -84
- package/src/components/MessageScroller/__tests__/MessageScroller.test.tsx +0 -105
- package/src/components/MobileAppsDrawer/MobileAppsDrawer.module.css +0 -82
- package/src/components/MobileAppsDrawer/MobileAppsDrawer.tsx +0 -78
- package/src/components/MobileAppsDrawer/__tests__/MobileAppsDrawer.test.tsx +0 -80
- package/src/components/MobileFooter/MobileFooter.module.css +0 -73
- package/src/components/MobileFooter/MobileFooter.stories.tsx +0 -47
- package/src/components/MobileFooter/MobileFooter.tsx +0 -122
- package/src/components/MobileFooter/__tests__/MobileFooter.test.tsx +0 -129
- package/src/components/MobileHeader/MobileHeader.module.css +0 -76
- package/src/components/MobileHeader/MobileHeader.stories.tsx +0 -57
- package/src/components/MobileHeader/MobileHeader.tsx +0 -90
- package/src/components/MobileHeader/__tests__/MobileHeader.test.tsx +0 -113
- package/src/components/NavList/NavList.module.css +0 -130
- package/src/components/NavList/NavList.tsx +0 -136
- package/src/components/NavList/__tests__/NavList.test.tsx +0 -109
- package/src/components/NavTabs/NavTabs.module.css +0 -50
- package/src/components/NavTabs/NavTabs.tsx +0 -61
- package/src/components/NavigationMenu/NavigationMenu.module.css +0 -53
- package/src/components/NavigationMenu/NavigationMenu.stories.tsx +0 -58
- package/src/components/NavigationMenu/NavigationMenu.tsx +0 -125
- package/src/components/NavigationMenu/__tests__/NavigationMenu.test.tsx +0 -93
- package/src/components/NotificationsPanel/NotificationsPanel.module.css +0 -258
- package/src/components/NotificationsPanel/NotificationsPanel.stories.tsx +0 -61
- package/src/components/NotificationsPanel/NotificationsPanel.tsx +0 -187
- package/src/components/NotificationsPanel/__tests__/NotificationsPanel.test.tsx +0 -78
- package/src/components/OfflineGate/OfflineGate.tsx +0 -44
- package/src/components/OfflineGate/__tests__/OfflineGate.test.tsx +0 -66
- package/src/components/OverlayHeader/OverlayHeader.module.css +0 -98
- package/src/components/OverlayHeader/OverlayHeader.tsx +0 -77
- package/src/components/OverlayHeader/__tests__/OverlayHeader.test.tsx +0 -69
- package/src/components/PageContainer/PageContainer.module.css +0 -82
- package/src/components/PageContainer/PageContainer.tsx +0 -64
- package/src/components/PageContainer/__tests__/PageContainer.test.tsx +0 -70
- package/src/components/PageHeader/PageHeader.module.css +0 -86
- package/src/components/PageHeader/PageHeader.tsx +0 -176
- package/src/components/PageHeader/__tests__/PageHeader.test.tsx +0 -103
- package/src/components/PageLayout/PageLayout.module.css +0 -51
- package/src/components/PageLayout/PageLayout.stories.tsx +0 -73
- package/src/components/PageLayout/PageLayout.tsx +0 -51
- package/src/components/PageLayout/__tests__/PageLayout.test.tsx +0 -34
- package/src/components/Pagination/Pagination.module.css +0 -98
- package/src/components/Pagination/Pagination.stories.tsx +0 -49
- package/src/components/Pagination/Pagination.tsx +0 -87
- package/src/components/Pagination/__tests__/Pagination.test.tsx +0 -56
- package/src/components/Popover/Popover.module.css +0 -74
- package/src/components/Popover/Popover.stories.tsx +0 -357
- package/src/components/Popover/Popover.tsx +0 -290
- package/src/components/Popover/__tests__/Popover.test.tsx +0 -263
- package/src/components/Progress/Progress.module.css +0 -20
- package/src/components/Progress/Progress.stories.tsx +0 -40
- package/src/components/Progress/Progress.tsx +0 -35
- package/src/components/Progress/__tests__/Progress.test.tsx +0 -31
- package/src/components/QuantityStepper/QuantityStepper.module.css +0 -67
- package/src/components/QuantityStepper/QuantityStepper.stories.tsx +0 -79
- package/src/components/QuantityStepper/QuantityStepper.tsx +0 -107
- package/src/components/RadioGroup/RadioGroup.module.css +0 -88
- package/src/components/RadioGroup/RadioGroup.stories.tsx +0 -69
- package/src/components/RadioGroup/RadioGroup.tsx +0 -99
- package/src/components/RadioGroup/__tests__/RadioGroup.test.tsx +0 -59
- package/src/components/Resizable/Resizable.module.css +0 -70
- package/src/components/Resizable/Resizable.stories.tsx +0 -82
- package/src/components/Resizable/Resizable.tsx +0 -313
- package/src/components/Resizable/__tests__/Resizable.test.tsx +0 -128
- package/src/components/ResponsiveSurface/ResponsiveSurface.tsx +0 -24
- package/src/components/ResponsiveSurface/__tests__/ResponsiveSurface.test.tsx +0 -73
- package/src/components/RootLayout/RootLayout.module.css +0 -12
- package/src/components/RootLayout/RootLayout.stories.tsx +0 -125
- package/src/components/RootLayout/RootLayout.tsx +0 -185
- package/src/components/RootLayout/__tests__/RootLayout.test.tsx +0 -161
- package/src/components/ScrollArea/ScrollArea.module.css +0 -20
- package/src/components/ScrollArea/ScrollArea.stories.tsx +0 -57
- package/src/components/ScrollArea/ScrollArea.tsx +0 -27
- package/src/components/ScrollArea/__tests__/ScrollArea.test.tsx +0 -39
- package/src/components/SegmentedControl/SegmentedControl.module.css +0 -55
- package/src/components/SegmentedControl/SegmentedControl.stories.tsx +0 -118
- package/src/components/SegmentedControl/SegmentedControl.tsx +0 -56
- package/src/components/SegmentedControl/__tests__/SegmentedControl.test.tsx +0 -63
- package/src/components/Select/Select.module.css +0 -82
- package/src/components/Select/Select.stories.tsx +0 -115
- package/src/components/Select/Select.tsx +0 -48
- package/src/components/Select/__tests__/Select.test.tsx +0 -59
- package/src/components/Sheet/Sheet.module.css +0 -58
- package/src/components/Sheet/Sheet.tsx +0 -109
- package/src/components/Sheet/__tests__/Sheet.test.tsx +0 -99
- package/src/components/Slider/Slider.module.css +0 -95
- package/src/components/Slider/Slider.stories.tsx +0 -64
- package/src/components/Slider/Slider.tsx +0 -77
- package/src/components/Slider/__tests__/Slider.test.tsx +0 -45
- package/src/components/Spinner/Spinner.module.css +0 -39
- package/src/components/Spinner/Spinner.tsx +0 -23
- package/src/components/SplitMethodSelector/SplitMethodSelector.stories.tsx +0 -33
- package/src/components/SplitMethodSelector/SplitMethodSelector.tsx +0 -43
- package/src/components/SplitPane/SplitPane.module.css +0 -85
- package/src/components/SplitPane/SplitPane.stories.tsx +0 -160
- package/src/components/SplitPane/SplitPane.tsx +0 -149
- package/src/components/SplitPane/__tests__/SplitPane.test.tsx +0 -51
- package/src/components/StatusBadge/StatusBadge.module.css +0 -59
- package/src/components/StatusBadge/StatusBadge.stories.tsx +0 -74
- package/src/components/StatusBadge/StatusBadge.tsx +0 -65
- package/src/components/StepStrip/StepStrip.module.css +0 -53
- package/src/components/StepStrip/StepStrip.tsx +0 -74
- package/src/components/SuggestionInput/SuggestionInput.module.css +0 -103
- package/src/components/SuggestionInput/SuggestionInput.stories.tsx +0 -144
- package/src/components/SuggestionInput/SuggestionInput.tsx +0 -207
- package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.module.css +0 -64
- package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.tsx +0 -243
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlide.tsx +0 -35
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideBody.tsx +0 -42
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideFooter.tsx +0 -18
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideHeader.tsx +0 -21
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideParts.module.css +0 -29
- package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarousel.test.tsx +0 -214
- package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarouselDevGuard.test.tsx +0 -61
- package/src/components/SwipableMobileCarousel/context.ts +0 -13
- package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.module.css +0 -64
- package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.tsx +0 -73
- package/src/components/SwipableMobileCarouselDots/__tests__/SwipableMobileCarouselDots.test.tsx +0 -63
- package/src/components/SwipeStack/SwipeStack.module.css +0 -101
- package/src/components/SwipeStack/SwipeStack.tsx +0 -199
- package/src/components/SwipeStack/SwipeStackCard.tsx +0 -22
- package/src/components/SwipeStack/__tests__/SwipeStack.test.tsx +0 -169
- package/src/components/SystemBanner/SystemBanner.module.css +0 -79
- package/src/components/SystemBanner/SystemBanner.stories.tsx +0 -86
- package/src/components/SystemBanner/SystemBanner.tsx +0 -50
- package/src/components/SystemBanner/__tests__/SystemBanner.test.tsx +0 -50
- package/src/components/Table/Table.module.css +0 -45
- package/src/components/Table/Table.stories.tsx +0 -125
- package/src/components/Table/Table.tsx +0 -51
- package/src/components/Table/__tests__/Table.test.tsx +0 -50
- package/src/components/Tabs/Tabs.module.css +0 -56
- package/src/components/Tabs/Tabs.stories.tsx +0 -100
- package/src/components/Tabs/Tabs.tsx +0 -46
- package/src/components/Tabs/__tests__/Tabs.test.tsx +0 -45
- package/src/components/TagInput/TagInput.module.css +0 -141
- package/src/components/TagInput/TagInput.stories.tsx +0 -109
- package/src/components/TagInput/TagInput.tsx +0 -174
- package/src/components/TagInput/__tests__/TagInput.test.tsx +0 -69
- package/src/components/Textarea/Textarea.module.css +0 -47
- package/src/components/Textarea/Textarea.tsx +0 -38
- package/src/components/Textarea/__tests__/Textarea.test.tsx +0 -51
- package/src/components/ThreeColumnLayout/ThreeColumnLayout.module.css +0 -35
- package/src/components/ThreeColumnLayout/ThreeColumnLayout.stories.tsx +0 -152
- package/src/components/ThreeColumnLayout/ThreeColumnLayout.tsx +0 -87
- package/src/components/ThreeColumnLayout/__tests__/ThreeColumnLayout.test.tsx +0 -78
- package/src/components/Toast/Toast.module.css +0 -191
- package/src/components/Toast/Toast.stories.tsx +0 -191
- package/src/components/Toast/Toast.tsx +0 -139
- package/src/components/Toggle/Toggle.module.css +0 -57
- package/src/components/Toggle/Toggle.stories.tsx +0 -95
- package/src/components/Toggle/Toggle.tsx +0 -39
- package/src/components/Toggle/__tests__/Toggle.test.tsx +0 -47
- package/src/components/Tooltip/Tooltip.module.css +0 -56
- package/src/components/Tooltip/Tooltip.tsx +0 -30
- package/src/components/Typography/Typography.module.css +0 -47
- package/src/components/Typography/Typography.stories.tsx +0 -62
- package/src/components/Typography/Typography.tsx +0 -30
- package/src/components/Typography/__tests__/Typography.test.tsx +0 -34
- package/src/components/UserMenu/UserMenu.module.css +0 -90
- package/src/components/UserMenu/UserMenu.stories.tsx +0 -49
- package/src/components/UserMenu/UserMenu.tsx +0 -132
- package/src/components/UserMenu/__tests__/UserMenu.test.tsx +0 -77
- package/src/css-modules.d.ts +0 -4
- package/src/hooks/__tests__/useCarouselRouteSync.test.tsx +0 -126
- package/src/hooks/__tests__/useDoubleTap.test.tsx +0 -117
- package/src/hooks/__tests__/useIsMobile.test.tsx +0 -80
- package/src/hooks/__tests__/useIsOffline.test.tsx +0 -53
- package/src/hooks/__tests__/useLongPress.test.tsx +0 -158
- package/src/hooks/__tests__/useOfflineTileState.test.tsx +0 -69
- package/src/hooks/__tests__/usePublishShellChromeHeight.test.tsx +0 -118
- package/src/hooks/__tests__/useReorderSensors.test.ts +0 -26
- package/src/hooks/__tests__/useResponsiveLayout.test.tsx +0 -87
- package/src/hooks/__tests__/useSnapCarousel.test.tsx +0 -175
- package/src/hooks/__tests__/useSwipeReveal.test.tsx +0 -110
- package/src/hooks/__tests__/useSwipeStack.test.tsx +0 -155
- package/src/hooks/index.ts +0 -33
- package/src/hooks/useCarouselRouteSync.ts +0 -92
- package/src/hooks/useCommitOnEnterOrBlur.ts +0 -39
- package/src/hooks/useDoubleTap.ts +0 -91
- package/src/hooks/useIsMobile.ts +0 -33
- package/src/hooks/useIsOffline.ts +0 -41
- package/src/hooks/useLongPress.ts +0 -185
- package/src/hooks/useOfflineTileState.ts +0 -45
- package/src/hooks/usePublishShellChromeHeight.ts +0 -96
- package/src/hooks/useReorderSensors.ts +0 -88
- package/src/hooks/useResponsiveLayout.ts +0 -36
- package/src/hooks/useSnapCarousel.ts +0 -284
- package/src/hooks/useSwipeReveal.ts +0 -107
- package/src/hooks/useSwipeStack.ts +0 -235
- package/src/index.ts +0 -311
- package/src/motion.ts +0 -78
- package/src/overlay-shell.ts +0 -159
- package/src/scroll-lock.ts +0 -22
- package/src/stories/Avatar.stories.tsx +0 -44
- package/src/stories/Card.stories.tsx +0 -35
- package/src/stories/Checkbox.stories.tsx +0 -45
- package/src/stories/DesignSystemOverview.stories.tsx +0 -3793
- package/src/stories/DeviceOnlyGate.stories.tsx +0 -35
- package/src/stories/DeviceStorageKeyGate.stories.tsx +0 -57
- package/src/stories/DragHandleRow.stories.tsx +0 -41
- package/src/stories/EmptyState.stories.tsx +0 -33
- package/src/stories/FileDropzone.stories.tsx +0 -69
- package/src/stories/FormField.stories.tsx +0 -47
- package/src/stories/GripIcon.stories.tsx +0 -29
- package/src/stories/InteractionHooks.stories.tsx +0 -925
- package/src/stories/LayoutSystem.stories.tsx +0 -639
- package/src/stories/MenuEntries.stories.tsx +0 -86
- package/src/stories/MobilePatterns.stories.tsx +0 -1536
- package/src/stories/NavList.stories.tsx +0 -147
- package/src/stories/NavTabs.stories.tsx +0 -53
- package/src/stories/OfflineGate.stories.tsx +0 -42
- package/src/stories/PageContainer.stories.tsx +0 -82
- package/src/stories/PageHeader.stories.tsx +0 -95
- package/src/stories/ResponsiveSurface.stories.tsx +0 -47
- package/src/stories/Spinner.stories.tsx +0 -25
- package/src/stories/StepStrip.stories.tsx +0 -76
- package/src/stories/SwipableMobileCarousel.stories.tsx +0 -362
- package/src/stories/SwipeStack.stories.tsx +0 -200
- package/src/stories/Textarea.stories.tsx +0 -63
- package/src/stories/TokenGallery.stories.tsx +0 -652
- package/src/stories/Tooltip.stories.tsx +0 -59
- package/src/tokens/primitives.css +0 -168
- package/src/tokens/semantic.css +0 -162
- package/src/tokens/theme-presets.css +0 -33
- package/src/tokens/theme-presets.ts +0 -72
- package/src/tokens.css +0 -5
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
-
import { NotificationsPanel } from './NotificationsPanel';
|
|
3
|
-
import { Icon } from '../Icon/Icon';
|
|
4
|
-
|
|
5
|
-
const items = [
|
|
6
|
-
{
|
|
7
|
-
id: '1',
|
|
8
|
-
icon: <Icon name="layers" size="sm" aria-hidden />,
|
|
9
|
-
title: 'Added to a project',
|
|
10
|
-
timeLabel: '2d ago',
|
|
11
|
-
read: false,
|
|
12
|
-
onDismiss: () => {},
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
id: '2',
|
|
16
|
-
icon: <Icon name="user-round-plus" size="sm" aria-hidden />,
|
|
17
|
-
title: 'Jamie invited you to Notes',
|
|
18
|
-
timeLabel: '3d ago',
|
|
19
|
-
read: true,
|
|
20
|
-
href: '/notes',
|
|
21
|
-
onDismiss: () => {},
|
|
22
|
-
},
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
const meta = {
|
|
26
|
-
title: 'Components/NotificationsPanel',
|
|
27
|
-
component: NotificationsPanel,
|
|
28
|
-
parameters: {
|
|
29
|
-
layout: 'centered',
|
|
30
|
-
docs: {
|
|
31
|
-
description: {
|
|
32
|
-
component:
|
|
33
|
-
"The bell trigger + dropdown, generalized from the runtime shell's own NotificationBell. Pure presentation — no fetch/SSE/mark-read logic; the consumer supplies already-resolved items and wires onOpen/onDismiss/onMarkAllRead/onClearAll.",
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
args: {
|
|
38
|
-
items,
|
|
39
|
-
unreadCount: 1,
|
|
40
|
-
onMarkAllRead: () => {},
|
|
41
|
-
onClearAll: () => {},
|
|
42
|
-
},
|
|
43
|
-
} satisfies Meta<typeof NotificationsPanel>;
|
|
44
|
-
|
|
45
|
-
export default meta;
|
|
46
|
-
type Story = StoryObj<typeof meta>;
|
|
47
|
-
|
|
48
|
-
export const Default: Story = {};
|
|
49
|
-
|
|
50
|
-
export const Empty: Story = {
|
|
51
|
-
args: {
|
|
52
|
-
items: [],
|
|
53
|
-
unreadCount: 0,
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export const WithViewAll: Story = {
|
|
58
|
-
args: {
|
|
59
|
-
viewAllHref: '/inbox',
|
|
60
|
-
},
|
|
61
|
-
};
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useState, type ReactNode } from 'react';
|
|
4
|
-
import { Icon } from '../Icon/Icon';
|
|
5
|
-
import { Popover } from '../Popover/Popover';
|
|
6
|
-
import styles from './NotificationsPanel.module.css';
|
|
7
|
-
|
|
8
|
-
export interface NotificationsPanelItem {
|
|
9
|
-
id: string;
|
|
10
|
-
/** Category icon, already resolved (sized + colored) by the consumer —
|
|
11
|
-
* this component doesn't own category → color mapping. */
|
|
12
|
-
icon: ReactNode;
|
|
13
|
-
title: string;
|
|
14
|
-
/** Pre-formatted relative time (e.g. "2d ago") — formatting stays
|
|
15
|
-
* consumer-side so this component needs no Date/clock dependency. */
|
|
16
|
-
timeLabel: string;
|
|
17
|
-
read: boolean;
|
|
18
|
-
/** Renders the title as a link when set. */
|
|
19
|
-
href?: string;
|
|
20
|
-
/** Called when the title is clicked/tapped — typically "mark as read". */
|
|
21
|
-
onOpen?: () => void;
|
|
22
|
-
onDismiss: () => void;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface NotificationsPanelProps {
|
|
26
|
-
items: NotificationsPanelItem[];
|
|
27
|
-
unreadCount: number;
|
|
28
|
-
onMarkAllRead?: () => void;
|
|
29
|
-
onClearAll?: () => void;
|
|
30
|
-
emptyMessage?: string;
|
|
31
|
-
/** Trigger button's accessible label base — the unread count is appended
|
|
32
|
-
* automatically, matching the real NotificationBell. Defaults to
|
|
33
|
-
* "Notifications". */
|
|
34
|
-
'aria-label'?: string;
|
|
35
|
-
align?: 'left' | 'right';
|
|
36
|
-
/** Optional footer link (e.g. to the consumer's own full notification
|
|
37
|
-
* list/detail view — RFC 0048) — mirrors the real `NotificationBell`'s
|
|
38
|
-
* "View all" link. Omit to render no footer, matching this component's
|
|
39
|
-
* behavior before this prop existed. */
|
|
40
|
-
viewAllHref?: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function NotificationRow({ item }: { item: NotificationsPanelItem }) {
|
|
44
|
-
const titleContent = item.href ? (
|
|
45
|
-
<a href={item.href} className={styles.itemTitle} onClick={item.onOpen}>
|
|
46
|
-
{item.title}
|
|
47
|
-
</a>
|
|
48
|
-
) : !item.read ? (
|
|
49
|
-
<button
|
|
50
|
-
type="button"
|
|
51
|
-
className={styles.itemTitle}
|
|
52
|
-
aria-label={`Mark as read: ${item.title}`}
|
|
53
|
-
onClick={item.onOpen}
|
|
54
|
-
>
|
|
55
|
-
{item.title}
|
|
56
|
-
</button>
|
|
57
|
-
) : (
|
|
58
|
-
<span className={styles.itemTitle}>{item.title}</span>
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<li className={[styles.item, item.read ? styles.itemRead : ''].filter(Boolean).join(' ')}>
|
|
63
|
-
<div className={styles.categoryIcon}>{item.icon}</div>
|
|
64
|
-
<div className={styles.itemBody}>
|
|
65
|
-
{titleContent}
|
|
66
|
-
<span className={styles.itemTime}>{item.timeLabel}</span>
|
|
67
|
-
</div>
|
|
68
|
-
<div className={styles.itemEnd}>
|
|
69
|
-
{!item.read && <span className={styles.unreadDot} aria-label="Unread" />}
|
|
70
|
-
<button
|
|
71
|
-
type="button"
|
|
72
|
-
className={styles.dismissBtn}
|
|
73
|
-
aria-label={`Dismiss: ${item.title}`}
|
|
74
|
-
onClick={item.onDismiss}
|
|
75
|
-
>
|
|
76
|
-
<Icon name="x" size="sm" aria-hidden />
|
|
77
|
-
</button>
|
|
78
|
-
</div>
|
|
79
|
-
</li>
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* NotificationsPanel — the bell trigger + dropdown, generalized from the
|
|
85
|
-
* runtime shell's own `NotificationBell.tsx` visual structure and copy
|
|
86
|
-
* (title / Mark all read / Clear all / close, then a list of category-icon +
|
|
87
|
-
* title + time + unread-dot + dismiss rows, or an empty state).
|
|
88
|
-
*
|
|
89
|
-
* Deliberately **presentational only** — no fetch/SSE/polling, no
|
|
90
|
-
* category → color mapping, no mark-read/dismiss networking. The real
|
|
91
|
-
* `NotificationBell` owns real platform data (`/api/account/notifications`,
|
|
92
|
-
* a shared cross-instance store); this component can't and shouldn't know
|
|
93
|
-
* about that (SDK boundary). The consumer supplies `items` already resolved
|
|
94
|
-
* and wires `onOpen`/`onDismiss`/`onMarkAllRead`/`onClearAll` to whatever
|
|
95
|
-
* its own data layer needs, against a shared panel instead of a
|
|
96
|
-
* `shell: minimal` plugin hand-rebuilding one locally. Built on the shared
|
|
97
|
-
* `Popover` primitive (self-contained open state) rather than the
|
|
98
|
-
* original's hand-rolled outside-click/Escape listener.
|
|
99
|
-
*/
|
|
100
|
-
export function NotificationsPanel({
|
|
101
|
-
items,
|
|
102
|
-
unreadCount,
|
|
103
|
-
onMarkAllRead,
|
|
104
|
-
onClearAll,
|
|
105
|
-
emptyMessage = 'No notifications.',
|
|
106
|
-
'aria-label': ariaLabel = 'Notifications',
|
|
107
|
-
align = 'right',
|
|
108
|
-
viewAllHref,
|
|
109
|
-
}: NotificationsPanelProps) {
|
|
110
|
-
const [open, setOpen] = useState(false);
|
|
111
|
-
|
|
112
|
-
return (
|
|
113
|
-
<Popover
|
|
114
|
-
align={align}
|
|
115
|
-
width={340}
|
|
116
|
-
aria-label={ariaLabel}
|
|
117
|
-
open={open}
|
|
118
|
-
onClose={() => setOpen(false)}
|
|
119
|
-
trigger={
|
|
120
|
-
<button
|
|
121
|
-
type="button"
|
|
122
|
-
aria-label={unreadCount > 0 ? `${ariaLabel} (${unreadCount} unread)` : ariaLabel}
|
|
123
|
-
aria-haspopup="true"
|
|
124
|
-
aria-expanded={open}
|
|
125
|
-
onClick={() => setOpen((v) => !v)}
|
|
126
|
-
className={styles.trigger}
|
|
127
|
-
>
|
|
128
|
-
{/* size="lg" (24px), not "md" (20px) — matches the real
|
|
129
|
-
NotificationBell's own inline SVG, which is literally
|
|
130
|
-
width="24" height="24", not the DS Icon scale's "md" step. */}
|
|
131
|
-
<Icon name="bell" size="lg" aria-hidden />
|
|
132
|
-
{unreadCount > 0 && (
|
|
133
|
-
<span className={styles.badge} aria-hidden="true">
|
|
134
|
-
{unreadCount > 99 ? '99+' : unreadCount}
|
|
135
|
-
</span>
|
|
136
|
-
)}
|
|
137
|
-
</button>
|
|
138
|
-
}
|
|
139
|
-
>
|
|
140
|
-
<div className={styles.header}>
|
|
141
|
-
<span className={styles.headerTitle}>{ariaLabel}</span>
|
|
142
|
-
<div className={styles.headerActions}>
|
|
143
|
-
{items.length > 0 && (
|
|
144
|
-
<>
|
|
145
|
-
{unreadCount > 0 && onMarkAllRead && (
|
|
146
|
-
<button type="button" className={styles.actionBtn} onClick={onMarkAllRead}>
|
|
147
|
-
Mark all read
|
|
148
|
-
</button>
|
|
149
|
-
)}
|
|
150
|
-
{onClearAll && (
|
|
151
|
-
<button type="button" className={styles.actionBtn} onClick={onClearAll}>
|
|
152
|
-
Clear all
|
|
153
|
-
</button>
|
|
154
|
-
)}
|
|
155
|
-
</>
|
|
156
|
-
)}
|
|
157
|
-
<button
|
|
158
|
-
type="button"
|
|
159
|
-
className={styles.closeBtn}
|
|
160
|
-
aria-label={`Close ${ariaLabel.toLowerCase()}`}
|
|
161
|
-
onClick={() => setOpen(false)}
|
|
162
|
-
>
|
|
163
|
-
<Icon name="x" size="sm" aria-hidden />
|
|
164
|
-
</button>
|
|
165
|
-
</div>
|
|
166
|
-
</div>
|
|
167
|
-
|
|
168
|
-
<ul className={styles.list} aria-label={`${ariaLabel} list`}>
|
|
169
|
-
{items.length === 0 ? (
|
|
170
|
-
<li className={styles.empty}>
|
|
171
|
-
<span className={styles.emptyIcon} aria-hidden="true">
|
|
172
|
-
<Icon name="bell" size="lg" aria-hidden />
|
|
173
|
-
</span>
|
|
174
|
-
<span className={styles.emptyText}>{emptyMessage}</span>
|
|
175
|
-
</li>
|
|
176
|
-
) : (
|
|
177
|
-
items.map((item) => <NotificationRow key={item.id} item={item} />)
|
|
178
|
-
)}
|
|
179
|
-
</ul>
|
|
180
|
-
{viewAllHref && (
|
|
181
|
-
<a href={viewAllHref} className={styles.viewAll} onClick={() => setOpen(false)}>
|
|
182
|
-
View all
|
|
183
|
-
</a>
|
|
184
|
-
)}
|
|
185
|
-
</Popover>
|
|
186
|
-
);
|
|
187
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
|
|
4
|
-
import { NotificationsPanel } from '../NotificationsPanel';
|
|
5
|
-
|
|
6
|
-
afterEach(() => {
|
|
7
|
-
cleanup();
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
const items = [
|
|
11
|
-
{
|
|
12
|
-
id: '1',
|
|
13
|
-
icon: <span>icon</span>,
|
|
14
|
-
title: 'Added to a project',
|
|
15
|
-
timeLabel: '2d ago',
|
|
16
|
-
read: false,
|
|
17
|
-
onOpen: vi.fn(),
|
|
18
|
-
onDismiss: vi.fn(),
|
|
19
|
-
},
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
describe('NotificationsPanel', () => {
|
|
23
|
-
it('renders a closed trigger with an unread badge', () => {
|
|
24
|
-
render(<NotificationsPanel items={items} unreadCount={1} />);
|
|
25
|
-
expect(screen.getByRole('button', { name: /1 unread/ })).toBeTruthy();
|
|
26
|
-
expect(screen.queryByText('Added to a project')).toBeNull();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('renders no badge when there are no unread notifications', () => {
|
|
30
|
-
render(<NotificationsPanel items={[]} unreadCount={0} />);
|
|
31
|
-
expect(screen.getByRole('button', { name: 'Notifications' })).toBeTruthy();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('opens the panel on trigger click, showing items', () => {
|
|
35
|
-
render(<NotificationsPanel items={items} unreadCount={1} />);
|
|
36
|
-
fireEvent.click(screen.getByRole('button', { name: /unread/ }));
|
|
37
|
-
expect(screen.getByText('Added to a project')).toBeTruthy();
|
|
38
|
-
expect(screen.getByText('2d ago')).toBeTruthy();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('shows an empty state when there are no items', () => {
|
|
42
|
-
render(<NotificationsPanel items={[]} unreadCount={0} />);
|
|
43
|
-
fireEvent.click(screen.getByRole('button', { name: 'Notifications' }));
|
|
44
|
-
expect(screen.getByText('No notifications.')).toBeTruthy();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('calls onOpen when an unread title is clicked', () => {
|
|
48
|
-
render(<NotificationsPanel items={items} unreadCount={1} />);
|
|
49
|
-
fireEvent.click(screen.getByRole('button', { name: /unread/ }));
|
|
50
|
-
fireEvent.click(screen.getByRole('button', { name: 'Mark as read: Added to a project' }));
|
|
51
|
-
expect(items[0]?.onOpen).toHaveBeenCalledOnce();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('calls onDismiss when the dismiss button is clicked', () => {
|
|
55
|
-
render(<NotificationsPanel items={items} unreadCount={1} />);
|
|
56
|
-
fireEvent.click(screen.getByRole('button', { name: /unread/ }));
|
|
57
|
-
fireEvent.click(screen.getByRole('button', { name: 'Dismiss: Added to a project' }));
|
|
58
|
-
expect(items[0]?.onDismiss).toHaveBeenCalledOnce();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('calls onMarkAllRead and onClearAll from the header actions', () => {
|
|
62
|
-
const onMarkAllRead = vi.fn();
|
|
63
|
-
const onClearAll = vi.fn();
|
|
64
|
-
render(
|
|
65
|
-
<NotificationsPanel
|
|
66
|
-
items={items}
|
|
67
|
-
unreadCount={1}
|
|
68
|
-
onMarkAllRead={onMarkAllRead}
|
|
69
|
-
onClearAll={onClearAll}
|
|
70
|
-
/>,
|
|
71
|
-
);
|
|
72
|
-
fireEvent.click(screen.getByRole('button', { name: /unread/ }));
|
|
73
|
-
fireEvent.click(screen.getByRole('button', { name: 'Mark all read' }));
|
|
74
|
-
fireEvent.click(screen.getByRole('button', { name: 'Clear all' }));
|
|
75
|
-
expect(onMarkAllRead).toHaveBeenCalledOnce();
|
|
76
|
-
expect(onClearAll).toHaveBeenCalledOnce();
|
|
77
|
-
});
|
|
78
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import type { ReactNode } from 'react';
|
|
4
|
-
import { useIsOffline } from '../../hooks/useIsOffline';
|
|
5
|
-
import { EmptyState } from '../EmptyState/EmptyState';
|
|
6
|
-
|
|
7
|
-
export interface OfflineGateProps {
|
|
8
|
-
children: ReactNode;
|
|
9
|
-
/** Defaults to a generic "this section" phrasing — pass the surface's own name. */
|
|
10
|
-
surfaceName?: string;
|
|
11
|
-
className?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* OfflineGate — blocks administrative/settings surfaces from rendering
|
|
16
|
-
* (possibly stale) cached content while the device has no network, showing an
|
|
17
|
-
* explanatory empty state instead (research 0012, epic task 2.32).
|
|
18
|
-
*
|
|
19
|
-
* Distinct from `OfflineBanner`: the banner is an informational overlay that
|
|
20
|
-
* coexists with normal content everywhere in the shell. This is a hard block
|
|
21
|
-
* for surfaces where operating against stale data is actively wrong — e.g. a
|
|
22
|
-
* Console user list or an Account billing page reflects a point-in-time
|
|
23
|
-
* snapshot the moment it's server-rendered, and any cached copy replayed
|
|
24
|
-
* later has no way to signal that it may already be stale. `OfflineGate`
|
|
25
|
-
* doesn't try to tell staleness apart from freshness; it removes the
|
|
26
|
-
* question by not rendering the content at all while offline.
|
|
27
|
-
*
|
|
28
|
-
* `children` is rendered unconditionally while online, so this adds no cost
|
|
29
|
-
* to the common case — it only decides what to show, never how the wrapped
|
|
30
|
-
* content itself is fetched or cached.
|
|
31
|
-
*/
|
|
32
|
-
export function OfflineGate({ children, surfaceName, className }: OfflineGateProps) {
|
|
33
|
-
const isOffline = useIsOffline();
|
|
34
|
-
if (!isOffline) return <>{children}</>;
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
<EmptyState
|
|
38
|
-
icon="alert-triangle"
|
|
39
|
-
heading="You're offline"
|
|
40
|
-
description={`${surfaceName ?? 'This section'} needs a connection — reconnect to continue.`}
|
|
41
|
-
className={className}
|
|
42
|
-
/>
|
|
43
|
-
);
|
|
44
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import { act, cleanup, render, screen } from '@testing-library/react';
|
|
4
|
-
import { OfflineGate } from '../OfflineGate';
|
|
5
|
-
|
|
6
|
-
describe('OfflineGate', () => {
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
vi.stubGlobal('navigator', { ...window.navigator, onLine: true });
|
|
9
|
-
});
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
vi.unstubAllGlobals();
|
|
12
|
-
cleanup();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('renders children unchanged while online', () => {
|
|
16
|
-
render(
|
|
17
|
-
<OfflineGate>
|
|
18
|
-
<p>Live content</p>
|
|
19
|
-
</OfflineGate>,
|
|
20
|
-
);
|
|
21
|
-
expect(screen.getByText('Live content')).toBeDefined();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('replaces children with an explanatory empty state while offline', () => {
|
|
25
|
-
vi.stubGlobal('navigator', { ...window.navigator, onLine: false });
|
|
26
|
-
render(
|
|
27
|
-
<OfflineGate>
|
|
28
|
-
<p>Live content</p>
|
|
29
|
-
</OfflineGate>,
|
|
30
|
-
);
|
|
31
|
-
expect(screen.queryByText('Live content')).toBeNull();
|
|
32
|
-
expect(screen.getByText("You're offline")).toBeDefined();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('names the surface in the offline message when provided', () => {
|
|
36
|
-
vi.stubGlobal('navigator', { ...window.navigator, onLine: false });
|
|
37
|
-
render(
|
|
38
|
-
<OfflineGate surfaceName="Console">
|
|
39
|
-
<p>Live content</p>
|
|
40
|
-
</OfflineGate>,
|
|
41
|
-
);
|
|
42
|
-
expect(screen.getByText(/Console needs a connection/)).toBeDefined();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('falls back to generic phrasing with no surface name', () => {
|
|
46
|
-
vi.stubGlobal('navigator', { ...window.navigator, onLine: false });
|
|
47
|
-
render(
|
|
48
|
-
<OfflineGate>
|
|
49
|
-
<p>Live content</p>
|
|
50
|
-
</OfflineGate>,
|
|
51
|
-
);
|
|
52
|
-
expect(screen.getByText(/This section needs a connection/)).toBeDefined();
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('swaps back to children on reconnect', () => {
|
|
56
|
-
vi.stubGlobal('navigator', { ...window.navigator, onLine: false });
|
|
57
|
-
render(
|
|
58
|
-
<OfflineGate>
|
|
59
|
-
<p>Live content</p>
|
|
60
|
-
</OfflineGate>,
|
|
61
|
-
);
|
|
62
|
-
expect(screen.getByText("You're offline")).toBeDefined();
|
|
63
|
-
act(() => window.dispatchEvent(new Event('online')));
|
|
64
|
-
expect(screen.getByText('Live content')).toBeDefined();
|
|
65
|
-
});
|
|
66
|
-
});
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/* OverlayHeader — shared fixed secondary header for Dialog's mobile mode,
|
|
2
|
-
* Sheet, and Drawer. Not itself pinned via position — the consumer renders it
|
|
3
|
-
* as a flex-shrink: 0 sibling before its own scrolling content region (see
|
|
4
|
-
* the component's own doc comment). */
|
|
5
|
-
|
|
6
|
-
.header {
|
|
7
|
-
flex-shrink: 0;
|
|
8
|
-
background: var(--sv-color-surface);
|
|
9
|
-
border-bottom: var(--sv-border-width-hairline) solid var(--sv-color-border);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
.row {
|
|
13
|
-
display: flex;
|
|
14
|
-
align-items: center;
|
|
15
|
-
gap: var(--sv-space-2);
|
|
16
|
-
padding: var(--sv-space-3) var(--sv-space-4);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.backButton {
|
|
20
|
-
display: inline-flex;
|
|
21
|
-
align-items: center;
|
|
22
|
-
justify-content: center;
|
|
23
|
-
width: var(--sv-touch-target-min, 44px);
|
|
24
|
-
height: var(--sv-touch-target-min, 44px);
|
|
25
|
-
flex-shrink: 0;
|
|
26
|
-
/* Pull the touch-target padding back so the chevron glyph still sits close
|
|
27
|
-
to the title, matching the visual (not hit-area) spacing of .row's own
|
|
28
|
-
gap — otherwise a 44px box reads as a large empty gap before the title. */
|
|
29
|
-
margin-left: calc(-1 * (var(--sv-touch-target-min, 44px) - var(--sv-icon-size-md)) / 2);
|
|
30
|
-
margin-right: calc(
|
|
31
|
-
-1 * (var(--sv-touch-target-min, 44px) - var(--sv-icon-size-md)) / 2 + var(--sv-space-1)
|
|
32
|
-
);
|
|
33
|
-
border: none;
|
|
34
|
-
border-radius: var(--sv-radius-md);
|
|
35
|
-
background: transparent;
|
|
36
|
-
color: var(--sv-color-text-muted);
|
|
37
|
-
cursor: pointer;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
@media (hover: hover) {
|
|
41
|
-
.backButton:hover {
|
|
42
|
-
background: var(--sv-color-surface-sunken);
|
|
43
|
-
color: var(--sv-color-text-primary);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.backButton:focus-visible {
|
|
48
|
-
outline: 2px solid var(--sv-color-focus-ring);
|
|
49
|
-
outline-offset: -2px;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.title {
|
|
53
|
-
flex: 1;
|
|
54
|
-
min-width: 0;
|
|
55
|
-
overflow: hidden;
|
|
56
|
-
text-overflow: ellipsis;
|
|
57
|
-
white-space: nowrap;
|
|
58
|
-
font-size: var(--sv-font-size-xl);
|
|
59
|
-
font-weight: var(--sv-font-weight-semibold);
|
|
60
|
-
color: var(--sv-color-text-primary);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.action {
|
|
64
|
-
flex-shrink: 0;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.closeButton {
|
|
68
|
-
display: inline-flex;
|
|
69
|
-
align-items: center;
|
|
70
|
-
justify-content: center;
|
|
71
|
-
width: var(--sv-touch-target-min, 44px);
|
|
72
|
-
height: var(--sv-touch-target-min, 44px);
|
|
73
|
-
flex-shrink: 0;
|
|
74
|
-
/* Pull the touch-target padding back so the × sits flush with the row's own
|
|
75
|
-
edge padding, same reasoning as .backButton above. */
|
|
76
|
-
margin-right: calc(-1 * (var(--sv-touch-target-min, 44px) - var(--sv-icon-size-sm)) / 2);
|
|
77
|
-
border: none;
|
|
78
|
-
border-radius: var(--sv-radius-md);
|
|
79
|
-
background: transparent;
|
|
80
|
-
color: var(--sv-color-text-muted);
|
|
81
|
-
cursor: pointer;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
@media (hover: hover) {
|
|
85
|
-
.closeButton:hover {
|
|
86
|
-
background: var(--sv-color-surface-sunken);
|
|
87
|
-
color: var(--sv-color-text-primary);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.closeButton:focus-visible {
|
|
92
|
-
outline: 2px solid var(--sv-color-focus-ring);
|
|
93
|
-
outline-offset: -2px;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.secondRow {
|
|
97
|
-
padding: 0 var(--sv-space-2) var(--sv-space-2);
|
|
98
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
import { Icon } from '../Icon/Icon';
|
|
3
|
-
import styles from './OverlayHeader.module.css';
|
|
4
|
-
|
|
5
|
-
export interface OverlayHeaderProps {
|
|
6
|
-
/** Overlay title, shown between the optional back button and the trailing
|
|
7
|
-
* slots. Optional — some consumers (e.g. `Dialog`'s mobile bar) need the
|
|
8
|
-
* row's close button present even when no title applies. Accepts any
|
|
9
|
-
* `ReactNode`, not just a string — e.g. `Dialog`'s `header` prop passes
|
|
10
|
-
* arbitrary content through here (an icon next to text, etc.); the
|
|
11
|
-
* row's title slot still assumes single-line content sized like text
|
|
12
|
-
* (see the component's own doc comment for the line this doesn't cross). */
|
|
13
|
-
title?: ReactNode;
|
|
14
|
-
/** Always renders a close button; called on click. */
|
|
15
|
-
onClose: () => void;
|
|
16
|
-
/** When provided, renders a back-chevron button before the title (for
|
|
17
|
-
* hierarchical navigation within a single overlay — going back without
|
|
18
|
-
* fully closing it). Omit for a flat overlay that only ever closes. */
|
|
19
|
-
onBack?: () => void;
|
|
20
|
-
/** Trailing slot next to the close button — e.g. a "Save" action. */
|
|
21
|
-
action?: ReactNode;
|
|
22
|
-
/** A second row below the title row — e.g. a tab strip. */
|
|
23
|
-
secondRow?: ReactNode;
|
|
24
|
-
className?: string;
|
|
25
|
-
/** Override the title row's own className (e.g. tighter padding to match
|
|
26
|
-
* a consumer's own compact header elsewhere in the same surface) — the
|
|
27
|
-
* row keeps its standard title/back/action/close layout, just restyled.
|
|
28
|
-
* For a genuinely different shape, don't use `OverlayHeader` at all (see
|
|
29
|
-
* this component's own doc comment). */
|
|
30
|
-
rowClassName?: string;
|
|
31
|
-
/** Override the title text's own className (e.g. a smaller font size to
|
|
32
|
-
* match a consumer's own compact header elsewhere in the same surface). */
|
|
33
|
-
titleClassName?: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* OverlayHeader — the shared fixed secondary header for `Dialog`'s mobile
|
|
38
|
-
* mode, `Sheet`, and `Drawer`: title + close (+ optional back button /
|
|
39
|
-
* trailing action / tab-strip row). Not itself `position: sticky` or
|
|
40
|
-
* `position: fixed` — it stays visually pinned because the consuming overlay
|
|
41
|
-
* renders it as a non-scrolling flex sibling before its own scrollable
|
|
42
|
-
* content region, the same technique `Dialog`'s `.mobileBar` already used
|
|
43
|
-
* before this component existed to generalize it.
|
|
44
|
-
*
|
|
45
|
-
* Content that needs a fully custom header (e.g. an editable title, or
|
|
46
|
-
* controls that aren't a simple title/back/action shape) should not use this
|
|
47
|
-
* component — render its own header row instead; `Sheet`'s `title` prop is
|
|
48
|
-
* optional specifically to leave that escape hatch open.
|
|
49
|
-
*/
|
|
50
|
-
export function OverlayHeader({
|
|
51
|
-
title,
|
|
52
|
-
onClose,
|
|
53
|
-
onBack,
|
|
54
|
-
action,
|
|
55
|
-
secondRow,
|
|
56
|
-
className,
|
|
57
|
-
rowClassName,
|
|
58
|
-
titleClassName,
|
|
59
|
-
}: OverlayHeaderProps) {
|
|
60
|
-
return (
|
|
61
|
-
<div className={[styles.header, className].filter(Boolean).join(' ')}>
|
|
62
|
-
<div className={[styles.row, rowClassName].filter(Boolean).join(' ')}>
|
|
63
|
-
{onBack && (
|
|
64
|
-
<button type="button" className={styles.backButton} aria-label="Back" onClick={onBack}>
|
|
65
|
-
<Icon name="chevron-left" size="md" aria-hidden />
|
|
66
|
-
</button>
|
|
67
|
-
)}
|
|
68
|
-
<span className={[styles.title, titleClassName].filter(Boolean).join(' ')}>{title}</span>
|
|
69
|
-
{action && <div className={styles.action}>{action}</div>}
|
|
70
|
-
<button type="button" className={styles.closeButton} aria-label="Close" onClick={onClose}>
|
|
71
|
-
<Icon name="x" size="sm" aria-hidden />
|
|
72
|
-
</button>
|
|
73
|
-
</div>
|
|
74
|
-
{secondRow && <div className={styles.secondRow}>{secondRow}</div>}
|
|
75
|
-
</div>
|
|
76
|
-
);
|
|
77
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
|
|
4
|
-
import { OverlayHeader } from '../OverlayHeader';
|
|
5
|
-
|
|
6
|
-
describe('OverlayHeader', () => {
|
|
7
|
-
afterEach(cleanup);
|
|
8
|
-
|
|
9
|
-
it('renders the title and calls onClose on the close button', () => {
|
|
10
|
-
const onClose = vi.fn();
|
|
11
|
-
render(<OverlayHeader title="Account" onClose={onClose} />);
|
|
12
|
-
expect(screen.getByText('Account')).toBeTruthy();
|
|
13
|
-
fireEvent.click(screen.getByRole('button', { name: 'Close' }));
|
|
14
|
-
expect(onClose).toHaveBeenCalledOnce();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('does not render a back button when onBack is omitted', () => {
|
|
18
|
-
render(<OverlayHeader title="Account" onClose={() => {}} />);
|
|
19
|
-
expect(screen.queryByRole('button', { name: 'Back' })).toBeNull();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('renders and wires a back button when onBack is provided', () => {
|
|
23
|
-
const onBack = vi.fn();
|
|
24
|
-
render(<OverlayHeader title="Account" onClose={() => {}} onBack={onBack} />);
|
|
25
|
-
fireEvent.click(screen.getByRole('button', { name: 'Back' }));
|
|
26
|
-
expect(onBack).toHaveBeenCalledOnce();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('renders the trailing action slot', () => {
|
|
30
|
-
render(
|
|
31
|
-
<OverlayHeader
|
|
32
|
-
title="Edit list"
|
|
33
|
-
onClose={() => {}}
|
|
34
|
-
action={<button type="button">Save</button>}
|
|
35
|
-
/>,
|
|
36
|
-
);
|
|
37
|
-
expect(screen.getByRole('button', { name: 'Save' })).toBeTruthy();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('renders just the close button when title is omitted', () => {
|
|
41
|
-
const onClose = vi.fn();
|
|
42
|
-
render(<OverlayHeader onClose={onClose} />);
|
|
43
|
-
fireEvent.click(screen.getByRole('button', { name: 'Close' }));
|
|
44
|
-
expect(onClose).toHaveBeenCalledOnce();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('renders the second row', () => {
|
|
48
|
-
render(<OverlayHeader title="Account" onClose={() => {}} secondRow={<nav>Tab strip</nav>} />);
|
|
49
|
-
expect(screen.getByText('Tab strip')).toBeTruthy();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('accepts arbitrary ReactNode content for title, not just a string', () => {
|
|
53
|
-
// Locks in the type widening (title?: string -> title?: ReactNode) that
|
|
54
|
-
// Dialog's `header` prop relies on (Dialog.tsx passes header through
|
|
55
|
-
// here as `title`) — a plain string title must keep working unchanged.
|
|
56
|
-
render(
|
|
57
|
-
<OverlayHeader
|
|
58
|
-
title={
|
|
59
|
-
<>
|
|
60
|
-
<strong>Card</strong> detail
|
|
61
|
-
</>
|
|
62
|
-
}
|
|
63
|
-
onClose={() => {}}
|
|
64
|
-
/>,
|
|
65
|
-
);
|
|
66
|
-
expect(screen.getByText('Card').tagName).toBe('STRONG');
|
|
67
|
-
expect(screen.getByText('detail')).toBeTruthy();
|
|
68
|
-
});
|
|
69
|
-
});
|