@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,90 +0,0 @@
|
|
|
1
|
-
.trigger {
|
|
2
|
-
display: inline-flex;
|
|
3
|
-
align-items: center;
|
|
4
|
-
justify-content: center;
|
|
5
|
-
flex-shrink: 0;
|
|
6
|
-
width: 36px;
|
|
7
|
-
height: 36px;
|
|
8
|
-
padding: 0;
|
|
9
|
-
border: var(--sv-border-width-hairline) solid var(--sv-color-accent);
|
|
10
|
-
border-radius: var(--sv-radius-full);
|
|
11
|
-
background-color: var(--sv-color-accent);
|
|
12
|
-
color: var(--sv-color-text-on-accent);
|
|
13
|
-
font-size: var(--sv-font-size-xs);
|
|
14
|
-
font-weight: var(--sv-font-weight-semibold);
|
|
15
|
-
overflow: hidden;
|
|
16
|
-
cursor: pointer;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/* The compact `Header` top-bar context's own avatar size. */
|
|
20
|
-
.triggerSm {
|
|
21
|
-
width: 32px;
|
|
22
|
-
height: 32px;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.triggerImg {
|
|
26
|
-
width: 100%;
|
|
27
|
-
height: 100%;
|
|
28
|
-
object-fit: cover;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.panelHeader {
|
|
32
|
-
padding: var(--sv-space-1) var(--sv-space-1) 0;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.userHeader {
|
|
36
|
-
display: flex;
|
|
37
|
-
align-items: center;
|
|
38
|
-
gap: var(--sv-space-3);
|
|
39
|
-
padding: var(--sv-space-2) var(--sv-space-3);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.menuAvatar {
|
|
43
|
-
display: flex;
|
|
44
|
-
align-items: center;
|
|
45
|
-
justify-content: center;
|
|
46
|
-
flex-shrink: 0;
|
|
47
|
-
width: 38px;
|
|
48
|
-
height: 38px;
|
|
49
|
-
border-radius: var(--sv-radius-full);
|
|
50
|
-
background-color: var(--sv-color-accent);
|
|
51
|
-
color: var(--sv-color-text-on-accent);
|
|
52
|
-
font-size: var(--sv-font-size-sm);
|
|
53
|
-
font-weight: var(--sv-font-weight-semibold);
|
|
54
|
-
overflow: hidden;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.menuAvatarImg {
|
|
58
|
-
width: 100%;
|
|
59
|
-
height: 100%;
|
|
60
|
-
object-fit: cover;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.userInfo {
|
|
64
|
-
display: flex;
|
|
65
|
-
flex-direction: column;
|
|
66
|
-
min-width: 0;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.userName {
|
|
70
|
-
font-size: var(--sv-font-size-sm);
|
|
71
|
-
font-weight: var(--sv-font-weight-semibold);
|
|
72
|
-
color: var(--sv-color-text-primary);
|
|
73
|
-
white-space: nowrap;
|
|
74
|
-
overflow: hidden;
|
|
75
|
-
text-overflow: ellipsis;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.userEmail {
|
|
79
|
-
font-size: var(--sv-font-size-caption);
|
|
80
|
-
color: var(--sv-color-text-muted);
|
|
81
|
-
white-space: nowrap;
|
|
82
|
-
overflow: hidden;
|
|
83
|
-
text-overflow: ellipsis;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
.divider {
|
|
87
|
-
margin: var(--sv-space-1) 0 0;
|
|
88
|
-
border: none;
|
|
89
|
-
border-top: var(--sv-border-width-hairline) solid var(--sv-color-border);
|
|
90
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
-
import { UserMenu } from './UserMenu';
|
|
3
|
-
|
|
4
|
-
const meta = {
|
|
5
|
-
title: 'Components/UserMenu',
|
|
6
|
-
component: UserMenu,
|
|
7
|
-
parameters: {
|
|
8
|
-
layout: 'centered',
|
|
9
|
-
docs: {
|
|
10
|
-
description: {
|
|
11
|
-
component:
|
|
12
|
-
'The account avatar trigger + dropdown, generalized from the runtime shell\'s own AccountMenu — accent-filled avatar, name/email header block, then a plain item list. Presentational only: items carry their own onSelect and/or href. size="md" (36px, default) matches the platform sidebar\'s own avatar; size="sm" (32px) suits the more compact Header top-bar context instead.',
|
|
13
|
-
},
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
args: {
|
|
17
|
-
name: 'Kasun Benthara',
|
|
18
|
-
email: 'kasun@openfs.io',
|
|
19
|
-
items: [
|
|
20
|
-
{ label: 'Account', icon: 'user', href: '/account' },
|
|
21
|
-
{ label: 'Preferences', icon: 'sliders-horizontal', href: '/account/preferences' },
|
|
22
|
-
{ label: 'Sign out', icon: 'log-out', destructive: true, onSelect: () => {} },
|
|
23
|
-
],
|
|
24
|
-
},
|
|
25
|
-
} satisfies Meta<typeof UserMenu>;
|
|
26
|
-
|
|
27
|
-
export default meta;
|
|
28
|
-
type Story = StoryObj<typeof meta>;
|
|
29
|
-
|
|
30
|
-
export const Default: Story = {};
|
|
31
|
-
|
|
32
|
-
export const WithAvatarImage: Story = {
|
|
33
|
-
args: {
|
|
34
|
-
avatarUrl: 'https://i.pravatar.cc/80',
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export const NoUserInfo: Story = {
|
|
39
|
-
args: {
|
|
40
|
-
name: undefined,
|
|
41
|
-
email: undefined,
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export const CompactSize: Story = {
|
|
46
|
-
args: {
|
|
47
|
-
size: 'sm',
|
|
48
|
-
},
|
|
49
|
-
};
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
import type { IconName } from '../Icon/Icon';
|
|
5
|
-
import { MenuEntries, type MenuItem } from '../Menu/Menu';
|
|
6
|
-
import { Popover } from '../Popover/Popover';
|
|
7
|
-
import styles from './UserMenu.module.css';
|
|
8
|
-
|
|
9
|
-
export interface UserMenuItem {
|
|
10
|
-
label: string;
|
|
11
|
-
icon: IconName;
|
|
12
|
-
/** Optional when `href` is provided (a pure navigation entry needs no
|
|
13
|
-
* extra callback). Required otherwise (e.g. "Sign out"). */
|
|
14
|
-
onSelect?: () => void;
|
|
15
|
-
/** Renders the entry as a link (`<a href>`) instead of a button — same
|
|
16
|
-
* href-vs-onClick convention as `MobileAppsDrawer`'s items. */
|
|
17
|
-
href?: string;
|
|
18
|
-
destructive?: boolean;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface UserMenuProps {
|
|
22
|
-
name?: string;
|
|
23
|
-
email?: string;
|
|
24
|
-
avatarUrl?: string;
|
|
25
|
-
items: UserMenuItem[];
|
|
26
|
-
/** Trigger button's accessible label. Defaults to "Account". */
|
|
27
|
-
'aria-label'?: string;
|
|
28
|
-
align?: 'left' | 'right';
|
|
29
|
-
/** Trigger avatar size: `'md'` (36px) matches the platform sidebar's own
|
|
30
|
-
* `.avatar` exactly — the default, for that context. `'sm'` (32px) suits
|
|
31
|
-
* the more compact `Header` top-bar context instead. The panel's own
|
|
32
|
-
* user-info avatar (38px) is unaffected either way — only the trigger
|
|
33
|
-
* scales. */
|
|
34
|
-
size?: 'sm' | 'md';
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function initials(value: string): string {
|
|
38
|
-
const trimmed = value.trim();
|
|
39
|
-
if (!trimmed) return '?';
|
|
40
|
-
const [first = '', second = ''] = trimmed.split(/\s+/);
|
|
41
|
-
return (second ? first.charAt(0) + second.charAt(0) : first.slice(0, 2)).toUpperCase();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* UserMenu — the account avatar trigger + dropdown, generalized from the
|
|
46
|
-
* runtime shell's own `AccountMenu.tsx` (the real, currently-shipped
|
|
47
|
-
* component that every `shell: minimal` plugin would otherwise have to
|
|
48
|
-
* hand-rebuild locally instead of importing). Same visual structure and
|
|
49
|
-
* copy: an accent-filled avatar trigger (36px, matching the sidebar's own
|
|
50
|
-
* `.avatar`), a name+email header block (38px avatar) above a divider, then
|
|
51
|
-
* a plain item list.
|
|
52
|
-
*
|
|
53
|
-
* Presentational only — no auth/session/routing logic of its own. The
|
|
54
|
-
* consumer supplies `items`, each with its own `onSelect` and/or `href`,
|
|
55
|
-
* matching `MobileHeader`'s "consumer wires it up" boundary; a real
|
|
56
|
-
* "Sign out" is just an item whose `onSelect` does whatever the consuming
|
|
57
|
-
* app's sign-out actually requires. Built on the shared `Popover` primitive
|
|
58
|
-
* (self-contained open state, matching the original's own architecture)
|
|
59
|
-
* rather than a hand-rolled outside-click/Escape listener — the original
|
|
60
|
-
* `AccountMenu` and every plugin-local rebuild of it independently re-derive
|
|
61
|
-
* that same logic; one primitive here means one implementation to get
|
|
62
|
-
* right. Always a small anchored dropdown, never a full-width sheet —
|
|
63
|
-
* this is deliberately not built on the adaptive `Menu` component (which
|
|
64
|
-
* becomes a bottom `Drawer` on mobile): the real account menu is a compact
|
|
65
|
-
* floating panel on every surface, mobile included.
|
|
66
|
-
*/
|
|
67
|
-
export function UserMenu({
|
|
68
|
-
name,
|
|
69
|
-
email,
|
|
70
|
-
avatarUrl,
|
|
71
|
-
items,
|
|
72
|
-
'aria-label': ariaLabel = 'Account',
|
|
73
|
-
align = 'right',
|
|
74
|
-
size = 'md',
|
|
75
|
-
}: UserMenuProps) {
|
|
76
|
-
const [open, setOpen] = useState(false);
|
|
77
|
-
const displayName = name || email || '';
|
|
78
|
-
|
|
79
|
-
return (
|
|
80
|
-
<Popover
|
|
81
|
-
align={align}
|
|
82
|
-
width={240}
|
|
83
|
-
aria-label={ariaLabel}
|
|
84
|
-
open={open}
|
|
85
|
-
onClose={() => setOpen(false)}
|
|
86
|
-
trigger={
|
|
87
|
-
<button
|
|
88
|
-
type="button"
|
|
89
|
-
aria-label={ariaLabel}
|
|
90
|
-
aria-haspopup="menu"
|
|
91
|
-
aria-expanded={open}
|
|
92
|
-
onClick={() => setOpen((v) => !v)}
|
|
93
|
-
className={[styles.trigger, size === 'sm' ? styles.triggerSm : '']
|
|
94
|
-
.filter(Boolean)
|
|
95
|
-
.join(' ')}
|
|
96
|
-
>
|
|
97
|
-
{avatarUrl ? (
|
|
98
|
-
<img src={avatarUrl} alt="" className={styles.triggerImg} />
|
|
99
|
-
) : (
|
|
100
|
-
initials(displayName)
|
|
101
|
-
)}
|
|
102
|
-
</button>
|
|
103
|
-
}
|
|
104
|
-
>
|
|
105
|
-
{displayName && (
|
|
106
|
-
<div className={styles.panelHeader}>
|
|
107
|
-
<div className={styles.userHeader}>
|
|
108
|
-
<span className={styles.menuAvatar} aria-hidden="true">
|
|
109
|
-
{avatarUrl ? (
|
|
110
|
-
<img src={avatarUrl} alt="" className={styles.menuAvatarImg} />
|
|
111
|
-
) : (
|
|
112
|
-
initials(displayName)
|
|
113
|
-
)}
|
|
114
|
-
</span>
|
|
115
|
-
<span className={styles.userInfo}>
|
|
116
|
-
{name && <span className={styles.userName}>{name}</span>}
|
|
117
|
-
{email && <span className={styles.userEmail}>{email}</span>}
|
|
118
|
-
</span>
|
|
119
|
-
</div>
|
|
120
|
-
<hr className={styles.divider} />
|
|
121
|
-
</div>
|
|
122
|
-
)}
|
|
123
|
-
<MenuEntries
|
|
124
|
-
items={items}
|
|
125
|
-
onSelect={(entry: MenuItem) => {
|
|
126
|
-
setOpen(false);
|
|
127
|
-
entry.onSelect?.();
|
|
128
|
-
}}
|
|
129
|
-
/>
|
|
130
|
-
</Popover>
|
|
131
|
-
);
|
|
132
|
-
}
|
|
@@ -1,77 +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 { UserMenu } from '../UserMenu';
|
|
5
|
-
|
|
6
|
-
afterEach(() => {
|
|
7
|
-
cleanup();
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
const items = [
|
|
11
|
-
{ label: 'Account', icon: 'user' as const, href: '/account' },
|
|
12
|
-
{ label: 'Preferences', icon: 'sliders-horizontal' as const, href: '/account/preferences' },
|
|
13
|
-
{ label: 'Sign out', icon: 'log-out' as const, destructive: true, onSelect: vi.fn() },
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
describe('UserMenu', () => {
|
|
17
|
-
it('renders a closed trigger by default, showing initials from name', () => {
|
|
18
|
-
render(<UserMenu name="Jamie Doe" email="jamie@example.com" items={items} />);
|
|
19
|
-
expect(screen.getByRole('button', { name: 'Account' })).toBeTruthy();
|
|
20
|
-
expect(screen.getByText('JD')).toBeTruthy();
|
|
21
|
-
expect(screen.queryByRole('menuitem')).toBeNull();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('opens the panel on trigger click, showing name, email, and items', () => {
|
|
25
|
-
render(<UserMenu name="Jamie Doe" email="jamie@example.com" items={items} />);
|
|
26
|
-
fireEvent.click(screen.getByRole('button', { name: 'Account' }));
|
|
27
|
-
|
|
28
|
-
expect(screen.getByText('Jamie Doe')).toBeTruthy();
|
|
29
|
-
expect(screen.getByText('jamie@example.com')).toBeTruthy();
|
|
30
|
-
expect(screen.getByRole('menuitem', { name: /Account/ })).toBeTruthy();
|
|
31
|
-
expect(screen.getByRole('menuitem', { name: /Preferences/ })).toBeTruthy();
|
|
32
|
-
expect(screen.getByRole('menuitem', { name: /Sign out/ })).toBeTruthy();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('renders href items as links', () => {
|
|
36
|
-
render(<UserMenu name="Jamie Doe" items={items} />);
|
|
37
|
-
fireEvent.click(screen.getByRole('button', { name: 'Account' }));
|
|
38
|
-
const accountLink = screen.getByRole('menuitem', { name: /Account/ });
|
|
39
|
-
expect(accountLink.tagName).toBe('A');
|
|
40
|
-
expect(accountLink.getAttribute('href')).toBe('/account');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('closes and calls onSelect when a plain item is clicked', () => {
|
|
44
|
-
const onSelect = vi.fn();
|
|
45
|
-
render(
|
|
46
|
-
<UserMenu name="Jamie Doe" items={[{ label: 'Sign out', icon: 'log-out', onSelect }]} />,
|
|
47
|
-
);
|
|
48
|
-
fireEvent.click(screen.getByRole('button', { name: 'Account' }));
|
|
49
|
-
fireEvent.click(screen.getByRole('menuitem', { name: /Sign out/ }));
|
|
50
|
-
expect(onSelect).toHaveBeenCalledOnce();
|
|
51
|
-
expect(screen.queryByRole('menuitem')).toBeNull();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('renders no user header block when neither name nor email is provided', () => {
|
|
55
|
-
render(<UserMenu items={items} />);
|
|
56
|
-
fireEvent.click(screen.getByRole('button', { name: 'Account' }));
|
|
57
|
-
expect(screen.queryByText('Jamie Doe')).toBeNull();
|
|
58
|
-
expect(screen.getByRole('menuitem', { name: /Account/ })).toBeTruthy();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('falls back to email initials when name is omitted', () => {
|
|
62
|
-
render(<UserMenu email="jamie@example.com" items={items} />);
|
|
63
|
-
expect(screen.getByText('JA')).toBeTruthy();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('defaults to the md (36px sidebar) trigger size', () => {
|
|
67
|
-
render(<UserMenu name="Jamie Doe" items={items} />);
|
|
68
|
-
const trigger = screen.getByRole('button', { name: 'Account' });
|
|
69
|
-
expect(trigger.className).not.toMatch(/triggerSm/);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('applies the sm (32px, Header top-bar) trigger size when requested', () => {
|
|
73
|
-
render(<UserMenu name="Jamie Doe" items={items} size="sm" />);
|
|
74
|
-
const trigger = screen.getByRole('button', { name: 'Account' });
|
|
75
|
-
expect(trigger.className).toMatch(/triggerSm/);
|
|
76
|
-
});
|
|
77
|
-
});
|
package/src/css-modules.d.ts
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import { act, cleanup, render } from '@testing-library/react';
|
|
4
|
-
import { useCarouselRouteSync } from '../useCarouselRouteSync';
|
|
5
|
-
|
|
6
|
-
const SLIDES = ['/a', '/b', '/c'];
|
|
7
|
-
function indexForPathname(pathname: string) {
|
|
8
|
-
const i = SLIDES.indexOf(pathname);
|
|
9
|
-
return i === -1 ? 0 : i;
|
|
10
|
-
}
|
|
11
|
-
function pathForIndex(index: number) {
|
|
12
|
-
return SLIDES[index] ?? '/a';
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function Harness({
|
|
16
|
-
pathname,
|
|
17
|
-
onNavigate,
|
|
18
|
-
onResult,
|
|
19
|
-
}: {
|
|
20
|
-
pathname: string;
|
|
21
|
-
onNavigate: (path: string) => void;
|
|
22
|
-
onResult: (result: { activeIndex: number; onSettle: (i: number) => void }) => void;
|
|
23
|
-
}) {
|
|
24
|
-
const result = useCarouselRouteSync({ indexForPathname, pathForIndex, pathname, onNavigate });
|
|
25
|
-
onResult(result);
|
|
26
|
-
return <span data-testid="active">{result.activeIndex}</span>;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
afterEach(cleanup);
|
|
30
|
-
|
|
31
|
-
describe('useCarouselRouteSync', () => {
|
|
32
|
-
it('initializes activeIndex from the current pathname', () => {
|
|
33
|
-
const onNavigate = vi.fn();
|
|
34
|
-
const { getByTestId } = render(
|
|
35
|
-
<Harness pathname="/b" onNavigate={onNavigate} onResult={() => {}} />,
|
|
36
|
-
);
|
|
37
|
-
expect(getByTestId('active').textContent).toBe('1');
|
|
38
|
-
expect(onNavigate).not.toHaveBeenCalled();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('an external pathname change (tapped Link, browser back/forward) updates activeIndex without calling onNavigate', () => {
|
|
42
|
-
const onNavigate = vi.fn();
|
|
43
|
-
const { getByTestId, rerender } = render(
|
|
44
|
-
<Harness pathname="/a" onNavigate={onNavigate} onResult={() => {}} />,
|
|
45
|
-
);
|
|
46
|
-
expect(getByTestId('active').textContent).toBe('0');
|
|
47
|
-
|
|
48
|
-
rerender(<Harness pathname="/c" onNavigate={onNavigate} onResult={() => {}} />);
|
|
49
|
-
expect(getByTestId('active').textContent).toBe('2');
|
|
50
|
-
expect(onNavigate).not.toHaveBeenCalled();
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("onSettle calls onNavigate with pathForIndex's output and updates activeIndex synchronously", () => {
|
|
54
|
-
const onNavigate = vi.fn();
|
|
55
|
-
let latest: { activeIndex: number; onSettle: (i: number) => void } = {
|
|
56
|
-
activeIndex: -1,
|
|
57
|
-
onSettle: () => {},
|
|
58
|
-
};
|
|
59
|
-
const { getByTestId } = render(
|
|
60
|
-
<Harness
|
|
61
|
-
pathname="/a"
|
|
62
|
-
onNavigate={onNavigate}
|
|
63
|
-
onResult={(r) => {
|
|
64
|
-
latest = r;
|
|
65
|
-
}}
|
|
66
|
-
/>,
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
act(() => void latest.onSettle(2));
|
|
70
|
-
|
|
71
|
-
expect(getByTestId('active').textContent).toBe('2');
|
|
72
|
-
expect(onNavigate).toHaveBeenCalledWith('/c');
|
|
73
|
-
expect(onNavigate).toHaveBeenCalledTimes(1);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('does NOT re-trigger the external-nav branch on the pathname-effect run that follows its own onSettle-driven navigation', () => {
|
|
77
|
-
// This is the isInternalNav dance: onSettle navigates via onNavigate; the
|
|
78
|
-
// caller's router then re-renders this hook with the new pathname — that
|
|
79
|
-
// pathname change must be recognized as "our own settle, already
|
|
80
|
-
// applied" and NOT reprocessed as an external nav. Both existing plugins'
|
|
81
|
-
// code comments describe a real double-flicker bug from getting this
|
|
82
|
-
// wrong, so this is the highest-value assertion in this file.
|
|
83
|
-
const onNavigate = vi.fn();
|
|
84
|
-
let latest: { activeIndex: number; onSettle: (i: number) => void } = {
|
|
85
|
-
activeIndex: -1,
|
|
86
|
-
onSettle: () => {},
|
|
87
|
-
};
|
|
88
|
-
const { getByTestId, rerender } = render(
|
|
89
|
-
<Harness
|
|
90
|
-
pathname="/a"
|
|
91
|
-
onNavigate={onNavigate}
|
|
92
|
-
onResult={(r) => {
|
|
93
|
-
latest = r;
|
|
94
|
-
}}
|
|
95
|
-
/>,
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
// Settle on /b (index 1) — this is the carousel's own gesture-driven navigation.
|
|
99
|
-
act(() => void latest.onSettle(1));
|
|
100
|
-
expect(getByTestId('active').textContent).toBe('1');
|
|
101
|
-
expect(onNavigate).toHaveBeenCalledWith('/b');
|
|
102
|
-
|
|
103
|
-
// The caller's router "commits" the navigation by re-rendering with the
|
|
104
|
-
// new pathname (exactly what onNavigate's real router.replace would
|
|
105
|
-
// eventually cause). If this were misidentified as an external nav, the
|
|
106
|
-
// effect would recompute indexForPathname('/b') anyway — same value, so
|
|
107
|
-
// a bug here wouldn't show up as a *wrong* index, only as an extra,
|
|
108
|
-
// redundant setState (the actual symptom in the original bug was a
|
|
109
|
-
// visible re-snap/flicker, not a wrong end state) — so this test also
|
|
110
|
-
// asserts onNavigate is never called a second time as a side effect of
|
|
111
|
-
// that re-render, which would happen if the effect treated it as new
|
|
112
|
-
// settle-worthy input rather than a no-op resync.
|
|
113
|
-
rerender(<Harness pathname="/b" onNavigate={onNavigate} onResult={(r) => (latest = r)} />);
|
|
114
|
-
|
|
115
|
-
expect(getByTestId('active').textContent).toBe('1');
|
|
116
|
-
expect(onNavigate).toHaveBeenCalledTimes(1);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('an unrecognized pathname falls back to index 0', () => {
|
|
120
|
-
const onNavigate = vi.fn();
|
|
121
|
-
const { getByTestId } = render(
|
|
122
|
-
<Harness pathname="/unknown" onNavigate={onNavigate} onResult={() => {}} />,
|
|
123
|
-
);
|
|
124
|
-
expect(getByTestId('active').textContent).toBe('0');
|
|
125
|
-
});
|
|
126
|
-
});
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import { act, cleanup, fireEvent, render } from '@testing-library/react';
|
|
4
|
-
import { useDoubleTapHandler, useSingleOrDoubleTap } from '../useDoubleTap';
|
|
5
|
-
|
|
6
|
-
// A minimal harness: the hook returns a handler bound to onClick. Real
|
|
7
|
-
// double-clicks report detail===2; two separate taps report detail===1 each.
|
|
8
|
-
function DoubleTapHarness({ onDouble }: { onDouble: () => void }) {
|
|
9
|
-
const handle = useDoubleTapHandler<React.MouseEvent>(() => onDouble());
|
|
10
|
-
return (
|
|
11
|
-
<button data-testid="btn" onClick={handle}>
|
|
12
|
-
tap
|
|
13
|
-
</button>
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function SingleOrDoubleHarness({
|
|
18
|
-
onSingle,
|
|
19
|
-
onDouble,
|
|
20
|
-
}: {
|
|
21
|
-
onSingle: () => void;
|
|
22
|
-
onDouble: () => void;
|
|
23
|
-
}) {
|
|
24
|
-
const handle = useSingleOrDoubleTap<React.MouseEvent>(
|
|
25
|
-
() => onSingle(),
|
|
26
|
-
() => onDouble(),
|
|
27
|
-
);
|
|
28
|
-
return (
|
|
29
|
-
<button data-testid="btn" onClick={handle}>
|
|
30
|
-
tap
|
|
31
|
-
</button>
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
describe('useDoubleTapHandler', () => {
|
|
36
|
-
beforeEach(() => vi.useFakeTimers());
|
|
37
|
-
afterEach(() => {
|
|
38
|
-
vi.useRealTimers();
|
|
39
|
-
cleanup();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('fires immediately on a native double-click (detail === 2)', () => {
|
|
43
|
-
const onDouble = vi.fn();
|
|
44
|
-
const { getByTestId } = render(<DoubleTapHarness onDouble={onDouble} />);
|
|
45
|
-
fireEvent.click(getByTestId('btn'), { detail: 2 });
|
|
46
|
-
expect(onDouble).toHaveBeenCalledOnce();
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('fires on two touch taps within the window', () => {
|
|
50
|
-
const onDouble = vi.fn();
|
|
51
|
-
const { getByTestId } = render(<DoubleTapHarness onDouble={onDouble} />);
|
|
52
|
-
const btn = getByTestId('btn');
|
|
53
|
-
fireEvent.click(btn, { detail: 1 });
|
|
54
|
-
act(() => void vi.advanceTimersByTime(100));
|
|
55
|
-
fireEvent.click(btn, { detail: 1 });
|
|
56
|
-
expect(onDouble).toHaveBeenCalledOnce();
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('does not fire on a single tap', () => {
|
|
60
|
-
const onDouble = vi.fn();
|
|
61
|
-
const { getByTestId } = render(<DoubleTapHarness onDouble={onDouble} />);
|
|
62
|
-
fireEvent.click(getByTestId('btn'), { detail: 1 });
|
|
63
|
-
act(() => void vi.advanceTimersByTime(1000));
|
|
64
|
-
expect(onDouble).not.toHaveBeenCalled();
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
describe('useSingleOrDoubleTap', () => {
|
|
69
|
-
beforeEach(() => vi.useFakeTimers());
|
|
70
|
-
afterEach(() => {
|
|
71
|
-
vi.useRealTimers();
|
|
72
|
-
cleanup();
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('defers the single action until the double-tap window closes', () => {
|
|
76
|
-
const onSingle = vi.fn();
|
|
77
|
-
const onDouble = vi.fn();
|
|
78
|
-
const { getByTestId } = render(
|
|
79
|
-
<SingleOrDoubleHarness onSingle={onSingle} onDouble={onDouble} />,
|
|
80
|
-
);
|
|
81
|
-
fireEvent.click(getByTestId('btn'), { detail: 1 });
|
|
82
|
-
// Single must NOT have fired yet — this deferral is the whole point.
|
|
83
|
-
expect(onSingle).not.toHaveBeenCalled();
|
|
84
|
-
act(() => void vi.advanceTimersByTime(350));
|
|
85
|
-
expect(onSingle).toHaveBeenCalledOnce();
|
|
86
|
-
expect(onDouble).not.toHaveBeenCalled();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('fires double (and never single) when a second tap arrives in time', () => {
|
|
90
|
-
const onSingle = vi.fn();
|
|
91
|
-
const onDouble = vi.fn();
|
|
92
|
-
const { getByTestId } = render(
|
|
93
|
-
<SingleOrDoubleHarness onSingle={onSingle} onDouble={onDouble} />,
|
|
94
|
-
);
|
|
95
|
-
const btn = getByTestId('btn');
|
|
96
|
-
fireEvent.click(btn, { detail: 1 });
|
|
97
|
-
act(() => void vi.advanceTimersByTime(100));
|
|
98
|
-
fireEvent.click(btn, { detail: 1 });
|
|
99
|
-
act(() => void vi.advanceTimersByTime(500));
|
|
100
|
-
expect(onDouble).toHaveBeenCalledOnce();
|
|
101
|
-
expect(onSingle).not.toHaveBeenCalled();
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('fires double on a native double-click and cancels the pending single', () => {
|
|
105
|
-
const onSingle = vi.fn();
|
|
106
|
-
const onDouble = vi.fn();
|
|
107
|
-
const { getByTestId } = render(
|
|
108
|
-
<SingleOrDoubleHarness onSingle={onSingle} onDouble={onDouble} />,
|
|
109
|
-
);
|
|
110
|
-
const btn = getByTestId('btn');
|
|
111
|
-
fireEvent.click(btn, { detail: 1 });
|
|
112
|
-
fireEvent.click(btn, { detail: 2 });
|
|
113
|
-
act(() => void vi.advanceTimersByTime(500));
|
|
114
|
-
expect(onDouble).toHaveBeenCalledOnce();
|
|
115
|
-
expect(onSingle).not.toHaveBeenCalled();
|
|
116
|
-
});
|
|
117
|
-
});
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import { act, cleanup, render } from '@testing-library/react';
|
|
4
|
-
import { MOBILE_BREAKPOINT_PX, useIsMobile } from '../useIsMobile';
|
|
5
|
-
|
|
6
|
-
// Capture the registered change listener so a test can drive a viewport change.
|
|
7
|
-
let changeHandler: ((e: MediaQueryListEvent) => void) | null = null;
|
|
8
|
-
let matches = false;
|
|
9
|
-
let lastQuery = '';
|
|
10
|
-
|
|
11
|
-
function installMatchMedia() {
|
|
12
|
-
changeHandler = null;
|
|
13
|
-
vi.stubGlobal(
|
|
14
|
-
'matchMedia',
|
|
15
|
-
vi.fn().mockImplementation((query: string) => {
|
|
16
|
-
lastQuery = query;
|
|
17
|
-
return {
|
|
18
|
-
matches,
|
|
19
|
-
media: query,
|
|
20
|
-
addEventListener: (_: string, cb: (e: MediaQueryListEvent) => void) => {
|
|
21
|
-
changeHandler = cb;
|
|
22
|
-
},
|
|
23
|
-
removeEventListener: vi.fn(),
|
|
24
|
-
addListener: vi.fn(),
|
|
25
|
-
removeListener: vi.fn(),
|
|
26
|
-
dispatchEvent: vi.fn(),
|
|
27
|
-
};
|
|
28
|
-
}),
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function Harness({ breakpoint }: { breakpoint?: number }) {
|
|
33
|
-
const isMobile = useIsMobile(breakpoint);
|
|
34
|
-
return <span data-testid="out">{String(isMobile)}</span>;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
describe('useIsMobile', () => {
|
|
38
|
-
beforeEach(() => {
|
|
39
|
-
matches = false;
|
|
40
|
-
installMatchMedia();
|
|
41
|
-
});
|
|
42
|
-
afterEach(() => {
|
|
43
|
-
vi.unstubAllGlobals();
|
|
44
|
-
cleanup();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('exports the canonical 768px breakpoint', () => {
|
|
48
|
-
expect(MOBILE_BREAKPOINT_PX).toBe(768);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('defaults to the canonical breakpoint in its query', () => {
|
|
52
|
-
matches = true;
|
|
53
|
-
installMatchMedia();
|
|
54
|
-
render(<Harness />);
|
|
55
|
-
expect(lastQuery).toBe('(max-width: 768px)');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('reflects the initial matchMedia result after mount', () => {
|
|
59
|
-
matches = true;
|
|
60
|
-
installMatchMedia();
|
|
61
|
-
const { getByTestId } = render(<Harness />);
|
|
62
|
-
expect(getByTestId('out').textContent).toBe('true');
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('updates when the viewport crosses the breakpoint', () => {
|
|
66
|
-
matches = false;
|
|
67
|
-
installMatchMedia();
|
|
68
|
-
const { getByTestId } = render(<Harness />);
|
|
69
|
-
expect(getByTestId('out').textContent).toBe('false');
|
|
70
|
-
act(() => changeHandler?.({ matches: true } as MediaQueryListEvent));
|
|
71
|
-
expect(getByTestId('out').textContent).toBe('true');
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('honours a custom breakpoint override', () => {
|
|
75
|
-
matches = false;
|
|
76
|
-
installMatchMedia();
|
|
77
|
-
render(<Harness breakpoint={640} />);
|
|
78
|
-
expect(lastQuery).toBe('(max-width: 640px)');
|
|
79
|
-
});
|
|
80
|
-
});
|