@takaro/lib-components 0.0.0-next.0da151e
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/.babelrc +7 -0
- package/@types/files.d.ts +23 -0
- package/@types/react-table-config.d.ts +121 -0
- package/Dockerfile.dev +21 -0
- package/package.json +15 -0
- package/public/images/7-days-to-die/logo.png +0 -0
- package/public/images/csmm-icon.png +0 -0
- package/public/images/milk.png +0 -0
- package/public/images/placeholder-01.jpeg +0 -0
- package/public/images/placeholder-02.jpeg +0 -0
- package/public/images/placeholder-03.jpeg +0 -0
- package/public/images/rust/logo.png +0 -0
- package/readme.md +10 -0
- package/src/components/actions/Button/Button.stories.tsx +149 -0
- package/src/components/actions/Button/Button.test.tsx +8 -0
- package/src/components/actions/Button/Button.test.tsx.snap +89 -0
- package/src/components/actions/Button/__snapshots__/Button.test.tsx.snap +18 -0
- package/src/components/actions/Button/index.tsx +89 -0
- package/src/components/actions/Button/style.ts +142 -0
- package/src/components/actions/ContextMenu/ContextMenu.stories.tsx +41 -0
- package/src/components/actions/ContextMenu/Group.tsx +34 -0
- package/src/components/actions/ContextMenu/MenuItem.tsx +69 -0
- package/src/components/actions/ContextMenu/index.tsx +216 -0
- package/src/components/actions/Dropdown/Dropdown.stories.tsx +88 -0
- package/src/components/actions/Dropdown/DropdownContext.tsx +21 -0
- package/src/components/actions/Dropdown/DropdownMenu.tsx +70 -0
- package/src/components/actions/Dropdown/DropdownMenuGroup.tsx +37 -0
- package/src/components/actions/Dropdown/DropdownMenuItem.tsx +128 -0
- package/src/components/actions/Dropdown/DropdownTrigger.tsx +87 -0
- package/src/components/actions/Dropdown/index.tsx +22 -0
- package/src/components/actions/Dropdown/useDropdown.tsx +89 -0
- package/src/components/actions/DropdownButton/DropdownButton.stories.tsx +88 -0
- package/src/components/actions/DropdownButton/index.tsx +128 -0
- package/src/components/actions/IconButton/IconButton.stories.tsx +54 -0
- package/src/components/actions/IconButton/IconButton.test.tsx +8 -0
- package/src/components/actions/IconButton/IconButton.test.tsx.snap +46 -0
- package/src/components/actions/IconButton/__snapshots__/IconButton.test.tsx.snap +18 -0
- package/src/components/actions/IconButton/getIconSize.ts +16 -0
- package/src/components/actions/IconButton/index.tsx +28 -0
- package/src/components/actions/IconButton/style.ts +51 -0
- package/src/components/actions/ToggleButton/ToggleButton.stories.tsx +85 -0
- package/src/components/actions/ToggleButton/ToggleButton.tsx +48 -0
- package/src/components/actions/ToggleButton/ToggleButtonGroup.tsx +107 -0
- package/src/components/actions/ToggleButton/index.tsx +2 -0
- package/src/components/actions/ToggleButton/style.ts +75 -0
- package/src/components/actions/index.ts +16 -0
- package/src/components/charts/AreaChart/AreaChart.stories.tsx +103 -0
- package/src/components/charts/AreaChart/index.tsx +428 -0
- package/src/components/charts/BarChart/BarChart.stories.tsx +55 -0
- package/src/components/charts/BarChart/index.tsx +420 -0
- package/src/components/charts/BrushHandle.tsx +21 -0
- package/src/components/charts/EmptyChart.tsx +45 -0
- package/src/components/charts/GeoMercator/GeoMercator.stories.tsx +77 -0
- package/src/components/charts/GeoMercator/index.tsx +228 -0
- package/src/components/charts/GeoMercator/iso3166-alpha2-to-alpha3.ts +250 -0
- package/src/components/charts/GeoMercator/world.d.ts +1 -0
- package/src/components/charts/GeoMercator/world.json +99818 -0
- package/src/components/charts/Heatmap/Heatmap.stories.tsx +189 -0
- package/src/components/charts/Heatmap/index.tsx +461 -0
- package/src/components/charts/LineChart/LineChart.stories.tsx +111 -0
- package/src/components/charts/LineChart/index.tsx +353 -0
- package/src/components/charts/PieChart/PieChart.stories.tsx +151 -0
- package/src/components/charts/PieChart/index.tsx +457 -0
- package/src/components/charts/PointHighlight.tsx +49 -0
- package/src/components/charts/RadarChart/RadarChart.stories.tsx +47 -0
- package/src/components/charts/RadarChart/generators.ts +30 -0
- package/src/components/charts/RadarChart/index.tsx +224 -0
- package/src/components/charts/RadialBarChart/RadialBarChart.stories.tsx +59 -0
- package/src/components/charts/RadialBarChart/index.tsx +231 -0
- package/src/components/charts/RadialLineChart/RadialLineChart.stories.tsx +43 -0
- package/src/components/charts/RadialLineChart/index.tsx +231 -0
- package/src/components/charts/ZoomControls.tsx +49 -0
- package/src/components/charts/index.tsx +26 -0
- package/src/components/charts/useGradients.tsx +37 -0
- package/src/components/charts/util.ts +78 -0
- package/src/components/data/Avatar/Avatar.stories.tsx +93 -0
- package/src/components/data/Avatar/context.tsx +20 -0
- package/src/components/data/Avatar/index.tsx +136 -0
- package/src/components/data/Avatar/style.ts +124 -0
- package/src/components/data/Avatar/useImageLoadingStatus.tsx +32 -0
- package/src/components/data/Chip/Chip.stories.tsx +67 -0
- package/src/components/data/Chip/Chip.test.tsx +8 -0
- package/src/components/data/Chip/Chip.test.tsx.snap +50 -0
- package/src/components/data/Chip/__snapshots__/Chip.test.tsx.snap +38 -0
- package/src/components/data/Chip/index.tsx +67 -0
- package/src/components/data/Chip/style.ts +49 -0
- package/src/components/data/Console/Console.stories.tsx +51 -0
- package/src/components/data/Console/Console.tsx +125 -0
- package/src/components/data/Console/ConsoleInput/index.tsx +87 -0
- package/src/components/data/Console/ConsoleInput/style.ts +41 -0
- package/src/components/data/Console/ConsoleLine/index.tsx +127 -0
- package/src/components/data/Console/ConsoleLine/style.ts +76 -0
- package/src/components/data/Console/MessageModel.ts +9 -0
- package/src/components/data/Console/constants.ts +6 -0
- package/src/components/data/Console/index.tsx +4 -0
- package/src/components/data/Console/style.ts +31 -0
- package/src/components/data/Console/useConsoleLiveMode.ts +42 -0
- package/src/components/data/CopyId/CopyId.stories.tsx +36 -0
- package/src/components/data/CopyId/index.tsx +107 -0
- package/src/components/data/CountryList/index.tsx +146 -0
- package/src/components/data/DateFormatter/DateFormatter.stories.tsx +26 -0
- package/src/components/data/DateFormatter/index.tsx +15 -0
- package/src/components/data/Drawer/Drawer.stories.tsx +155 -0
- package/src/components/data/Drawer/DrawerBody.tsx +22 -0
- package/src/components/data/Drawer/DrawerContent.tsx +169 -0
- package/src/components/data/Drawer/DrawerContext.tsx +21 -0
- package/src/components/data/Drawer/DrawerFooter.tsx +19 -0
- package/src/components/data/Drawer/DrawerHeading.tsx +50 -0
- package/src/components/data/Drawer/DrawerSkeleton.tsx +29 -0
- package/src/components/data/Drawer/index.tsx +25 -0
- package/src/components/data/Drawer/useDrawer.tsx +66 -0
- package/src/components/data/InfiniteScroll/InfiniteScroll.stories.tsx +33 -0
- package/src/components/data/InfiniteScroll/index.tsx +54 -0
- package/src/components/data/Stats/Sparkline.tsx +48 -0
- package/src/components/data/Stats/Stat.tsx +192 -0
- package/src/components/data/Stats/Stats.stories.tsx +196 -0
- package/src/components/data/Stats/context.tsx +18 -0
- package/src/components/data/Stats/index.tsx +62 -0
- package/src/components/data/Table/Table.stories.tsx +146 -0
- package/src/components/data/Table/index.tsx +492 -0
- package/src/components/data/Table/react-table.d.ts +14 -0
- package/src/components/data/Table/style.ts +97 -0
- package/src/components/data/Table/subcomponents/ColumnHeader/ColumnSettings.tsx +164 -0
- package/src/components/data/Table/subcomponents/ColumnHeader/index.tsx +207 -0
- package/src/components/data/Table/subcomponents/ColumnHeader/style.ts +90 -0
- package/src/components/data/Table/subcomponents/ColumnVisibility/index.tsx +70 -0
- package/src/components/data/Table/subcomponents/Filter/field.tsx +127 -0
- package/src/components/data/Table/subcomponents/Filter/index.tsx +228 -0
- package/src/components/data/Table/subcomponents/Filter/style.ts +26 -0
- package/src/components/data/Table/subcomponents/Filter/types.ts +10 -0
- package/src/components/data/Table/subcomponents/Pagination/PagePicker.tsx +109 -0
- package/src/components/data/Table/subcomponents/Pagination/PageSizeSelect.tsx +37 -0
- package/src/components/data/Table/subcomponents/Pagination/style.ts +49 -0
- package/src/components/data/index.ts +28 -0
- package/src/components/dialogs/Dialog/Dialog.stories.tsx +77 -0
- package/src/components/dialogs/Dialog/DialogBody.tsx +64 -0
- package/src/components/dialogs/Dialog/DialogContent.tsx +44 -0
- package/src/components/dialogs/Dialog/DialogContext.tsx +21 -0
- package/src/components/dialogs/Dialog/DialogHeading.tsx +56 -0
- package/src/components/dialogs/Dialog/index.tsx +20 -0
- package/src/components/dialogs/Dialog/useDialog.tsx +50 -0
- package/src/components/feedback/Alert/Alert.stories.tsx +75 -0
- package/src/components/feedback/Alert/Alert.test.tsx +8 -0
- package/src/components/feedback/Alert/Alert.test.tsx.snap +146 -0
- package/src/components/feedback/Alert/__snapshots__/Alert.test.tsx.snap +44 -0
- package/src/components/feedback/Alert/index.tsx +120 -0
- package/src/components/feedback/Alert/style.ts +97 -0
- package/src/components/feedback/Badge/Badge.stories.tsx +23 -0
- package/src/components/feedback/Badge/index.tsx +47 -0
- package/src/components/feedback/ErrorFallback/ErrorFallback.stories.tsx +10 -0
- package/src/components/feedback/ErrorFallback/index.tsx +49 -0
- package/src/components/feedback/ErrorPage/ErrorPage.stories.tsx +31 -0
- package/src/components/feedback/ErrorPage/index.tsx +193 -0
- package/src/components/feedback/FormError/FormError.stories.tsx +25 -0
- package/src/components/feedback/FormError/index.tsx +54 -0
- package/src/components/feedback/IconTooltip/IconTooltip.stories.tsx +22 -0
- package/src/components/feedback/IconTooltip/index.tsx +51 -0
- package/src/components/feedback/Loaders/Loaders.stories.tsx +28 -0
- package/src/components/feedback/Loaders/Loading.test.tsx +8 -0
- package/src/components/feedback/Loaders/Loading.test.tsx.snap +141 -0
- package/src/components/feedback/Loaders/Loading.tsx +107 -0
- package/src/components/feedback/Loaders/Spinner.test.tsx +8 -0
- package/src/components/feedback/Loaders/Spinner.test.tsx.snap +18 -0
- package/src/components/feedback/Loaders/Spinner.tsx +74 -0
- package/src/components/feedback/Loaders/__snapshots__/Loading.test.tsx.snap +141 -0
- package/src/components/feedback/Loaders/__snapshots__/Spinner.test.tsx.snap +10 -0
- package/src/components/feedback/Loaders/index.ts +2 -0
- package/src/components/feedback/NetworkDetector/NetworkDetector.stories.tsx +21 -0
- package/src/components/feedback/NetworkDetector/NetworkDetector.test.tsx +8 -0
- package/src/components/feedback/NetworkDetector/NetworkDetector.test.tsx.snap +3 -0
- package/src/components/feedback/NetworkDetector/__snapshots__/NetworkDetector.test.tsx.snap +3 -0
- package/src/components/feedback/NetworkDetector/index.tsx +41 -0
- package/src/components/feedback/NotificationBanner/NotificationBanner.stories.tsx +13 -0
- package/src/components/feedback/NotificationBanner/NotificationBanner.test.tsx +15 -0
- package/src/components/feedback/NotificationBanner/NotificationBanner.test.tsx.snap +3 -0
- package/src/components/feedback/NotificationBanner/__snapshots__/NotificationBanner.test.tsx.snap +3 -0
- package/src/components/feedback/NotificationBanner/index.tsx +78 -0
- package/src/components/feedback/Popover/Popover.stories.tsx +33 -0
- package/src/components/feedback/Popover/PopoverContent.tsx +46 -0
- package/src/components/feedback/Popover/PopoverContext.tsx +21 -0
- package/src/components/feedback/Popover/PopoverTrigger.tsx +45 -0
- package/src/components/feedback/Popover/index.tsx +22 -0
- package/src/components/feedback/Popover/usePopover.tsx +87 -0
- package/src/components/feedback/ProgressBar/ProgressBar.stories.tsx +38 -0
- package/src/components/feedback/ProgressBar/index.tsx +137 -0
- package/src/components/feedback/Skeleton/Skeleton.stories.tsx +33 -0
- package/src/components/feedback/Skeleton/Skeleton.test.tsx +8 -0
- package/src/components/feedback/Skeleton/Skeleton.test.tsx.snap +35 -0
- package/src/components/feedback/Skeleton/__snapshots__/Skeleton.test.tsx.snap +9 -0
- package/src/components/feedback/Skeleton/index.tsx +62 -0
- package/src/components/feedback/Tooltip/Tooltip.stories.tsx +69 -0
- package/src/components/feedback/Tooltip/TooltipContent.tsx +51 -0
- package/src/components/feedback/Tooltip/TooltipContext.tsx +13 -0
- package/src/components/feedback/Tooltip/TooltipTrigger.tsx +39 -0
- package/src/components/feedback/Tooltip/index.tsx +26 -0
- package/src/components/feedback/Tooltip/useTooltip.tsx +82 -0
- package/src/components/feedback/index.ts +37 -0
- package/src/components/feedback/snacks/CookieConsent/index.tsx +125 -0
- package/src/components/feedback/snacks/CookieConsent/style.ts +99 -0
- package/src/components/feedback/snacks/Default/default.stories.tsx +73 -0
- package/src/components/feedback/snacks/Default/index.tsx +78 -0
- package/src/components/feedback/snacks/Default/style.ts +58 -0
- package/src/components/feedback/snacks/Drawer/Drawer.stories.tsx +46 -0
- package/src/components/feedback/snacks/Drawer/index.tsx +85 -0
- package/src/components/feedback/snacks/NetworkDetector/index.tsx +73 -0
- package/src/components/feedback/snacks/index.d.ts +15 -0
- package/src/components/feedback/snacks/index.ts +8 -0
- package/src/components/index.ts +9 -0
- package/src/components/inputs/CheckBox/CheckBox.stories.tsx +64 -0
- package/src/components/inputs/CheckBox/Controlled.tsx +123 -0
- package/src/components/inputs/CheckBox/Generic.tsx +42 -0
- package/src/components/inputs/CheckBox/index.tsx +4 -0
- package/src/components/inputs/CheckBox/style.ts +56 -0
- package/src/components/inputs/CodeField/CodeField.stories.tsx +86 -0
- package/src/components/inputs/CodeField/index.tsx +174 -0
- package/src/components/inputs/CodeField/style.ts +50 -0
- package/src/components/inputs/Date/DatePicker/Controlled.tsx +103 -0
- package/src/components/inputs/Date/DatePicker/DatePicker.stories.tsx +267 -0
- package/src/components/inputs/Date/DatePicker/Generic.tsx +283 -0
- package/src/components/inputs/Date/DatePicker/formats.tsx +32 -0
- package/src/components/inputs/Date/DatePicker/style.ts +41 -0
- package/src/components/inputs/Date/DateRangePicker/Context.tsx +123 -0
- package/src/components/inputs/Date/DateRangePicker/Controlled.tsx +74 -0
- package/src/components/inputs/Date/DateRangePicker/DateRangePicker.stories.tsx +37 -0
- package/src/components/inputs/Date/DateRangePicker/DateSelector/Absolute.tsx +98 -0
- package/src/components/inputs/Date/DateRangePicker/DateSelector/Relative.tsx +54 -0
- package/src/components/inputs/Date/DateRangePicker/DateSelector/index.tsx +49 -0
- package/src/components/inputs/Date/DateRangePicker/Generic.tsx +167 -0
- package/src/components/inputs/Date/DateRangePicker/QuickSelect/index.tsx +254 -0
- package/src/components/inputs/Date/DateRangePicker/QuickSelect/style.ts +40 -0
- package/src/components/inputs/Date/DateRangePicker/style.ts +65 -0
- package/src/components/inputs/Date/subcomponents/Calendar/index.tsx +150 -0
- package/src/components/inputs/Date/subcomponents/Calendar/style.ts +61 -0
- package/src/components/inputs/Date/subcomponents/RelativePicker/index.tsx +170 -0
- package/src/components/inputs/Date/subcomponents/RelativePicker/style.ts +50 -0
- package/src/components/inputs/Date/subcomponents/TimePicker/TimePicker.stories.tsx +25 -0
- package/src/components/inputs/Date/subcomponents/TimePicker/index.tsx +40 -0
- package/src/components/inputs/Date/subcomponents/TimePicker/style.ts +34 -0
- package/src/components/inputs/Date/types.ts +14 -0
- package/src/components/inputs/DurationField/Controlled.tsx +69 -0
- package/src/components/inputs/DurationField/Duration.stories.tsx +57 -0
- package/src/components/inputs/DurationField/Generic.tsx +197 -0
- package/src/components/inputs/DurationField/__tests__/Generic.test.tsx +12 -0
- package/src/components/inputs/DurationField/index.tsx +5 -0
- package/src/components/inputs/DurationField/style.ts +42 -0
- package/src/components/inputs/EditableField/EditableField.stories.tsx +65 -0
- package/src/components/inputs/EditableField/index.tsx +135 -0
- package/src/components/inputs/EditableField/style.ts +11 -0
- package/src/components/inputs/FileField/Controlled.tsx +104 -0
- package/src/components/inputs/FileField/FileField.stories.tsx +210 -0
- package/src/components/inputs/FileField/Generic.tsx +133 -0
- package/src/components/inputs/FileField/index.tsx +5 -0
- package/src/components/inputs/FileField/style.ts +34 -0
- package/src/components/inputs/InputProps.ts +59 -0
- package/src/components/inputs/RadioGroup/Controlled.tsx +45 -0
- package/src/components/inputs/RadioGroup/Generic.tsx +34 -0
- package/src/components/inputs/RadioGroup/RadioGroup.stories.tsx +118 -0
- package/src/components/inputs/RadioGroup/RadioItem.tsx +130 -0
- package/src/components/inputs/RadioGroup/context.tsx +20 -0
- package/src/components/inputs/RadioGroup/index.ts +5 -0
- package/src/components/inputs/RadioGroup/style.ts +3 -0
- package/src/components/inputs/Switch/Controlled.tsx +57 -0
- package/src/components/inputs/Switch/Generic.tsx +40 -0
- package/src/components/inputs/Switch/Switch.stories.tsx +89 -0
- package/src/components/inputs/Switch/index.ts +5 -0
- package/src/components/inputs/Switch/style.ts +62 -0
- package/src/components/inputs/TagField/Controlled.tsx +103 -0
- package/src/components/inputs/TagField/Generic.tsx +138 -0
- package/src/components/inputs/TagField/TagField.stories.tsx +68 -0
- package/src/components/inputs/TagField/index.ts +5 -0
- package/src/components/inputs/TagField/style.ts +25 -0
- package/src/components/inputs/TagField/util.ts +34 -0
- package/src/components/inputs/TextAreaField/Controlled.tsx +101 -0
- package/src/components/inputs/TextAreaField/Generic.tsx +58 -0
- package/src/components/inputs/TextAreaField/TextAreaField.stories.tsx +99 -0
- package/src/components/inputs/TextAreaField/index.ts +5 -0
- package/src/components/inputs/TextAreaField/style.ts +28 -0
- package/src/components/inputs/TextField/Controlled.tsx +120 -0
- package/src/components/inputs/TextField/Generic.tsx +138 -0
- package/src/components/inputs/TextField/TextField.stories.tsx +217 -0
- package/src/components/inputs/TextField/index.ts +5 -0
- package/src/components/inputs/TextField/style.ts +103 -0
- package/src/components/inputs/TextField/util.ts +24 -0
- package/src/components/inputs/ValueConfirmationField/ValueConfirmationField.stories.tsx +33 -0
- package/src/components/inputs/ValueConfirmationField/index.tsx +54 -0
- package/src/components/inputs/index.ts +72 -0
- package/src/components/inputs/layout/Description.tsx +33 -0
- package/src/components/inputs/layout/ErrorMessage/ErrorMessage.stories.tsx +28 -0
- package/src/components/inputs/layout/ErrorMessage/index.tsx +63 -0
- package/src/components/inputs/layout/InputWrapper.ts +6 -0
- package/src/components/inputs/layout/Label/index.tsx +71 -0
- package/src/components/inputs/layout/Label/style.ts +50 -0
- package/src/components/inputs/layout/index.ts +8 -0
- package/src/components/inputs/layout/setAriaDescribedBy.ts +3 -0
- package/src/components/inputs/selects/SelectField/Controlled.tsx +127 -0
- package/src/components/inputs/selects/SelectField/Generic/FilterInput.tsx +48 -0
- package/src/components/inputs/selects/SelectField/Generic/index.tsx +299 -0
- package/src/components/inputs/selects/SelectField/SelectField.stories.tsx +252 -0
- package/src/components/inputs/selects/SelectField/index.ts +5 -0
- package/src/components/inputs/selects/SelectField/style.ts +29 -0
- package/src/components/inputs/selects/SelectQueryField/Controlled.tsx +110 -0
- package/src/components/inputs/selects/SelectQueryField/Generic/index.tsx +401 -0
- package/src/components/inputs/selects/SelectQueryField/SelectQueryField.stories.tsx +280 -0
- package/src/components/inputs/selects/SelectQueryField/index.tsx +5 -0
- package/src/components/inputs/selects/SelectQueryField/style.ts +13 -0
- package/src/components/inputs/selects/SubComponents/Option.tsx +114 -0
- package/src/components/inputs/selects/SubComponents/OptionGroup.tsx +18 -0
- package/src/components/inputs/selects/SubComponents/index.ts +10 -0
- package/src/components/inputs/selects/SubComponents/style.ts +51 -0
- package/src/components/inputs/selects/data.ts +123 -0
- package/src/components/inputs/selects/index.tsx +49 -0
- package/src/components/inputs/selects/sharedStyle.ts +76 -0
- package/src/components/layout/Container/index.ts +21 -0
- package/src/components/layout/index.ts +1 -0
- package/src/components/navigation/HorizontalNav/HorizontalNav.stories.tsx +29 -0
- package/src/components/navigation/HorizontalNav/index.tsx +13 -0
- package/src/components/navigation/HorizontalNav/style.ts +93 -0
- package/src/components/navigation/IconNav/IconNav.stories.tsx +46 -0
- package/src/components/navigation/IconNav/index.tsx +50 -0
- package/src/components/navigation/Steppers/SlimStepper/Stepper.stories.tsx +90 -0
- package/src/components/navigation/Steppers/SlimStepper/index.tsx +104 -0
- package/src/components/navigation/Steppers/SlimStepper/style.ts +85 -0
- package/src/components/navigation/Steppers/Stepper/Stepper.stories.tsx +88 -0
- package/src/components/navigation/Steppers/Stepper/index.tsx +114 -0
- package/src/components/navigation/Steppers/Stepper/style.ts +106 -0
- package/src/components/navigation/Steppers/context.tsx +2 -0
- package/src/components/navigation/Steppers/provider.tsx +18 -0
- package/src/components/navigation/Steppers/reducer.ts +51 -0
- package/src/components/navigation/Steppers/stepStates.ts +5 -0
- package/src/components/navigation/Steppers/useStepper.ts +34 -0
- package/src/components/navigation/Tabs/Content.tsx +39 -0
- package/src/components/navigation/Tabs/Context.tsx +21 -0
- package/src/components/navigation/Tabs/List.tsx +17 -0
- package/src/components/navigation/Tabs/Tabs.stories.tsx +119 -0
- package/src/components/navigation/Tabs/Trigger.tsx +66 -0
- package/src/components/navigation/Tabs/index.tsx +35 -0
- package/src/components/navigation/Tabs/useTabs.tsx +28 -0
- package/src/components/navigation/index.ts +17 -0
- package/src/components/other/ActionMenu/ActionMenu.stories.tsx +81 -0
- package/src/components/other/ActionMenu/index.tsx +62 -0
- package/src/components/other/ActionMenu/style.ts +47 -0
- package/src/components/other/ClipBoard/ClipBoard.stories.tsx +14 -0
- package/src/components/other/ClipBoard/ClipBoard.test.tsx +8 -0
- package/src/components/other/ClipBoard/ClipBoard.test.tsx.snap +70 -0
- package/src/components/other/ClipBoard/__snapshots__/ClipBoard.test.tsx.snap +36 -0
- package/src/components/other/ClipBoard/index.tsx +70 -0
- package/src/components/other/CollapseList/CollapseList.stories.tsx +21 -0
- package/src/components/other/CollapseList/index.tsx +110 -0
- package/src/components/other/Collapsible/Collapsible.stories.tsx +19 -0
- package/src/components/other/Collapsible/CollapsibleContent.tsx +29 -0
- package/src/components/other/Collapsible/CollapsibleContext.tsx +17 -0
- package/src/components/other/Collapsible/CollapsibleTrigger.tsx +83 -0
- package/src/components/other/Collapsible/index.tsx +21 -0
- package/src/components/other/Company/index.tsx +44 -0
- package/src/components/other/Company/style.ts +58 -0
- package/src/components/other/Empty/Empty.stories.tsx +28 -0
- package/src/components/other/Empty/Empty.test.tsx +10 -0
- package/src/components/other/Empty/Empty.test.tsx.snap +85 -0
- package/src/components/other/Empty/__snapshots__/Empty.test.tsx.snap +21 -0
- package/src/components/other/Empty/index.tsx +85 -0
- package/src/components/other/PermissionsGuard/PermissionsGuard.stories.tsx +55 -0
- package/src/components/other/PermissionsGuard/hasPermissionsHelper.ts +18 -0
- package/src/components/other/PermissionsGuard/index.tsx +27 -0
- package/src/components/other/Plan/Plan.stories.tsx +21 -0
- package/src/components/other/Plan/index.tsx +92 -0
- package/src/components/other/Plan/style.ts +61 -0
- package/src/components/other/Usage/Usage.stories.tsx +21 -0
- package/src/components/other/Usage/Usage.tsx +35 -0
- package/src/components/other/Usage/UsageCard.stories.tsx +31 -0
- package/src/components/other/Usage/UsageCard.tsx +88 -0
- package/src/components/other/index.ts +27 -0
- package/src/components/visual/Card/Card.stories.tsx +22 -0
- package/src/components/visual/Card/CardBody.tsx +11 -0
- package/src/components/visual/Card/CardTitle.tsx +29 -0
- package/src/components/visual/Card/index.tsx +58 -0
- package/src/components/visual/Divider/Divider.stories.tsx +19 -0
- package/src/components/visual/Divider/Divider.test.tsx +8 -0
- package/src/components/visual/Divider/Divider.test.tsx.snap +35 -0
- package/src/components/visual/Divider/__snapshots__/Divider.test.tsx.snap +13 -0
- package/src/components/visual/Divider/index.tsx +88 -0
- package/src/components/visual/Flag/index.tsx +61 -0
- package/src/components/visual/index.ts +8 -0
- package/src/errors/base.ts +14 -0
- package/src/errors/errors.ts +126 -0
- package/src/errors/index.ts +14 -0
- package/src/helpers/camelCaseToSpaces.ts +5 -0
- package/src/helpers/formatNumber.ts +6 -0
- package/src/helpers/getInitials.ts +16 -0
- package/src/helpers/getSnackbarProvider.tsx +57 -0
- package/src/helpers/getTransition.ts +9 -0
- package/src/helpers/index.ts +6 -0
- package/src/helpers/makeData.ts +34 -0
- package/src/helpers/regexprs.ts +9 -0
- package/src/hooks/index.ts +9 -0
- package/src/hooks/useCallbackRef.tsx +17 -0
- package/src/hooks/useDebounce.tsx +22 -0
- package/src/hooks/useFocus.tsx +12 -0
- package/src/hooks/useLocalStorage.tsx +35 -0
- package/src/hooks/useLockScroll.ts +26 -0
- package/src/hooks/useOnScreen.ts +24 -0
- package/src/hooks/useOutsideAlerter.tsx +15 -0
- package/src/hooks/useTableActions.ts +61 -0
- package/src/hooks/useTheme.tsx +7 -0
- package/src/images/company-icon-primary.svg +3 -0
- package/src/images/company-icon-secondary.svg +3 -0
- package/src/images/index.ts +4 -0
- package/src/index.ts +9 -0
- package/src/stories/Colors.mdx +35 -0
- package/src/stories/Introduction.mdx +17 -0
- package/src/stories/Spacing.mdx +25 -0
- package/src/styled/GlobalStyle.ts +209 -0
- package/src/styled/Snackbar.tsx +41 -0
- package/src/styled/animations.ts +36 -0
- package/src/styled/breakpoint.ts +7 -0
- package/src/styled/device.ts +10 -0
- package/src/styled/index.ts +10 -0
- package/src/styled/spacing.ts +38 -0
- package/src/styled/theme.tsx +110 -0
- package/src/styled/types.ts +5 -0
- package/src/styled/zIndex.ts +31 -0
- package/src/test/__mocks__/fileMock.ts +2 -0
- package/src/test/setupTests.ts +3 -0
- package/src/test/snapshotResolver.js +21 -0
- package/src/test/testUtils.tsx +34 -0
- package/src/types/index.ts +1 -0
- package/src/types/json.ts +5 -0
- package/src/views/LoadingPage.tsx +36 -0
- package/src/views/index.ts +1 -0
- package/tsconfig.json +14 -0
- package/vite.config.mts +17 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useEffect,
|
|
4
|
+
useRef,
|
|
5
|
+
useState,
|
|
6
|
+
Children,
|
|
7
|
+
PropsWithChildren,
|
|
8
|
+
isValidElement,
|
|
9
|
+
cloneElement,
|
|
10
|
+
useMemo,
|
|
11
|
+
MouseEvent,
|
|
12
|
+
} from 'react';
|
|
13
|
+
import {
|
|
14
|
+
autoUpdate,
|
|
15
|
+
FloatingFocusManager,
|
|
16
|
+
FloatingPortal,
|
|
17
|
+
size,
|
|
18
|
+
useDismiss,
|
|
19
|
+
flip,
|
|
20
|
+
useFloating,
|
|
21
|
+
useInteractions,
|
|
22
|
+
useRole,
|
|
23
|
+
offset,
|
|
24
|
+
useClick,
|
|
25
|
+
} from '@floating-ui/react';
|
|
26
|
+
import { AiOutlineSearch as SearchIcon, AiOutlineClose as ClearIcon } from 'react-icons/ai';
|
|
27
|
+
import { defaultInputProps, defaultInputPropsFactory, GenericInputPropsFunctionHandlers } from '../../../InputProps';
|
|
28
|
+
import { useDebounce } from '../../../../../hooks';
|
|
29
|
+
import { setAriaDescribedBy } from '../../../layout';
|
|
30
|
+
import { FeedBackContainer } from '../style';
|
|
31
|
+
import { SelectItem, SelectContext, getLabelFromChildren } from '../../';
|
|
32
|
+
import { PaginationProps } from '../../../';
|
|
33
|
+
|
|
34
|
+
/* The SearchField depends on a few things of <Select/> */
|
|
35
|
+
import { GroupLabel } from '../../SelectField/style';
|
|
36
|
+
import { SelectContainer, SelectButton, StyledArrowIcon } from '../../sharedStyle';
|
|
37
|
+
import { IconButton, InfiniteScroll, Spinner } from '../../../../../components';
|
|
38
|
+
import { GenericTextField } from '../../../TextField/Generic';
|
|
39
|
+
import { Option, OptionGroup, SubComponentTypes } from '../../SubComponents';
|
|
40
|
+
|
|
41
|
+
interface SharedSelectQueryFieldProps extends PaginationProps {
|
|
42
|
+
// Enables loading data feedback for user
|
|
43
|
+
isLoadingData?: boolean;
|
|
44
|
+
/// The placeholder text to show when the input is empty
|
|
45
|
+
placeholder?: string;
|
|
46
|
+
/// Debounce time in milliseconds (when clientside data it should be 0)
|
|
47
|
+
debounce?: number;
|
|
48
|
+
/// Triggered whenever the input value changes.
|
|
49
|
+
/// This is used to trigger the API call to get the new options
|
|
50
|
+
handleInputValueChange?: (value: string) => void;
|
|
51
|
+
|
|
52
|
+
/// When true, The select icon will be replaced by a cross icon to clear the selected value.
|
|
53
|
+
canClear?: boolean;
|
|
54
|
+
|
|
55
|
+
/// The selected items shown in the select field
|
|
56
|
+
render?: (selectedItems: SelectItem[]) => React.ReactNode;
|
|
57
|
+
|
|
58
|
+
/// Callback to be called when the select field is opened or closed
|
|
59
|
+
onOpenChange?: (open: boolean) => void;
|
|
60
|
+
|
|
61
|
+
/// The total options that will be visible when fully loaded
|
|
62
|
+
optionCount?: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface SingleSelectQueryFieldProps extends SharedSelectQueryFieldProps {
|
|
66
|
+
multiple?: false;
|
|
67
|
+
}
|
|
68
|
+
interface MultiSelectQueryFieldProps extends SharedSelectQueryFieldProps {
|
|
69
|
+
multiple: true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface SingleSelectQueryFieldHandlers extends GenericInputPropsFunctionHandlers<string, HTMLDivElement> {
|
|
73
|
+
onChange: (val: string) => void;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface MultiSelectQueryFieldHandlers extends GenericInputPropsFunctionHandlers<string[], HTMLDivElement> {
|
|
77
|
+
onChange: (val: string[]) => void;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type SelectQueryFieldProps = SingleSelectQueryFieldProps | MultiSelectQueryFieldProps;
|
|
81
|
+
export type GenericSelectQueryFieldProps = PropsWithChildren<
|
|
82
|
+
| (SingleSelectQueryFieldProps & SingleSelectQueryFieldHandlers)
|
|
83
|
+
| (MultiSelectQueryFieldProps & MultiSelectQueryFieldHandlers)
|
|
84
|
+
>;
|
|
85
|
+
const defaultsApplier = defaultInputPropsFactory<GenericSelectQueryFieldProps>(defaultInputProps);
|
|
86
|
+
|
|
87
|
+
export interface InputValue {
|
|
88
|
+
value: string;
|
|
89
|
+
label: string;
|
|
90
|
+
/// When the user selects an option from the list, there is no need to do another API call
|
|
91
|
+
shouldUpdate: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const _GenericSelectQueryField = forwardRef<HTMLInputElement, GenericSelectQueryFieldProps>(
|
|
95
|
+
function GenericSelectQueryField(props, ref) {
|
|
96
|
+
const {
|
|
97
|
+
onBlur = () => {},
|
|
98
|
+
onFocus = () => {},
|
|
99
|
+
onChange,
|
|
100
|
+
name,
|
|
101
|
+
disabled,
|
|
102
|
+
/// Value are the selected items (most cases these are a list of IDs only)
|
|
103
|
+
value,
|
|
104
|
+
id,
|
|
105
|
+
placeholder = 'Search field',
|
|
106
|
+
hasDescription,
|
|
107
|
+
hasError,
|
|
108
|
+
children,
|
|
109
|
+
readOnly,
|
|
110
|
+
isFetchingNextPage,
|
|
111
|
+
isFetching,
|
|
112
|
+
fetchNextPage,
|
|
113
|
+
hasNextPage,
|
|
114
|
+
render,
|
|
115
|
+
multiple = false,
|
|
116
|
+
canClear = false,
|
|
117
|
+
debounce = 250,
|
|
118
|
+
isLoadingData = false,
|
|
119
|
+
handleInputValueChange,
|
|
120
|
+
onOpenChange,
|
|
121
|
+
optionCount,
|
|
122
|
+
} = defaultsApplier(props);
|
|
123
|
+
|
|
124
|
+
const [open, setOpen] = useState<boolean>(false);
|
|
125
|
+
const [inputValue, setInputValue] = useState<InputValue>({ value: '', shouldUpdate: false, label: '' });
|
|
126
|
+
const [activeIndex, setActiveIndex] = useState<number | null>(null);
|
|
127
|
+
const [currentSelectedItems, setCurrentSelectedItems] = useState<SelectItem[]>([]);
|
|
128
|
+
const [persistedItems, setPersistedItems] = useState<SelectItem[]>([]);
|
|
129
|
+
|
|
130
|
+
const debouncedValue = useDebounce(inputValue.value, debounce);
|
|
131
|
+
const listItemsRef = useRef<Array<HTMLLIElement | null>>([]);
|
|
132
|
+
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
if (handleInputValueChange) {
|
|
135
|
+
if ((inputValue.shouldUpdate && debouncedValue) || (inputValue && debouncedValue === ''))
|
|
136
|
+
handleInputValueChange(debouncedValue);
|
|
137
|
+
}
|
|
138
|
+
}, [debouncedValue]);
|
|
139
|
+
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
if (onOpenChange) {
|
|
142
|
+
onOpenChange(open);
|
|
143
|
+
}
|
|
144
|
+
}, [open]);
|
|
145
|
+
|
|
146
|
+
const { refs, strategy, x, y, context } = useFloating<HTMLInputElement>({
|
|
147
|
+
whileElementsMounted: autoUpdate,
|
|
148
|
+
open,
|
|
149
|
+
onOpenChange: readOnly || disabled ? () => {} : setOpen,
|
|
150
|
+
middleware: [
|
|
151
|
+
offset(5),
|
|
152
|
+
size({
|
|
153
|
+
apply({ availableHeight, elements, availableWidth }) {
|
|
154
|
+
const refWidth = elements.reference.getBoundingClientRect().width;
|
|
155
|
+
const floatingContentWidth = elements.floating.scrollWidth;
|
|
156
|
+
|
|
157
|
+
const width =
|
|
158
|
+
availableWidth > refWidth
|
|
159
|
+
? `${Math.min(availableWidth, 500)}px`
|
|
160
|
+
: `${Math.max(refWidth, floatingContentWidth)}px`;
|
|
161
|
+
|
|
162
|
+
Object.assign(elements.floating.style, {
|
|
163
|
+
// Note: we cannot use the rects.reference.width here because if the referenced item is very small compared to the other options, there will be horizontal overflow.
|
|
164
|
+
// fit-content isn't the perfect solution either, because if there is no space available it might render outside the viewport.
|
|
165
|
+
width,
|
|
166
|
+
maxHeight: `${Math.max(150, availableHeight)}px`,
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
}),
|
|
170
|
+
flip({
|
|
171
|
+
fallbackStrategy: 'bestFit',
|
|
172
|
+
fallbackPlacements: ['top', 'bottom'],
|
|
173
|
+
}),
|
|
174
|
+
],
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([
|
|
178
|
+
useClick(context),
|
|
179
|
+
useRole(context, { role: 'listbox' }),
|
|
180
|
+
useDismiss(context),
|
|
181
|
+
]);
|
|
182
|
+
|
|
183
|
+
function onInputChange(event: React.ChangeEvent<HTMLInputElement>) {
|
|
184
|
+
const value = event.target.value;
|
|
185
|
+
setInputValue({ value, shouldUpdate: true, label: value });
|
|
186
|
+
if (value) {
|
|
187
|
+
setActiveIndex(0);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const handleClear = (e: MouseEvent) => {
|
|
192
|
+
e.preventDefault();
|
|
193
|
+
e.stopPropagation();
|
|
194
|
+
setCurrentSelectedItems([]);
|
|
195
|
+
setPersistedItems([]);
|
|
196
|
+
|
|
197
|
+
// the undefined is an expection
|
|
198
|
+
if (onChange) onChange(multiple ? ([] as string[]) : (undefined as any));
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
/* This handles the case where the value is changed externally (e.g. from a parent component) */
|
|
202
|
+
/* onChange propagates the value to the parent component, but since the value prop is not a required prop, the parent might not reflect the change
|
|
203
|
+
* which ends up not running this useEffect. Meaning we still need to update the selectedIndex when clicked on an option.
|
|
204
|
+
*/
|
|
205
|
+
useEffect(() => {
|
|
206
|
+
/// Value only contains the ids of the selected items, not their labels, The labels are fetched from the items found in the list of options.
|
|
207
|
+
/// One caveat is that the list of options might change due to the search query, so the labels might not be found.
|
|
208
|
+
const createItem = (v: string) => ({ value: v, label: getLabelFromChildren(children, v) as unknown as string });
|
|
209
|
+
|
|
210
|
+
if (Array.isArray(value)) {
|
|
211
|
+
// first we divide the value array into two arrays, one that are already in preserved (so we don't need to fetch the label again)
|
|
212
|
+
// and the other one that are not in the preserved array (so we need to fetch the label)
|
|
213
|
+
const [preserved, toFetch] = value.reduce(
|
|
214
|
+
(acc, v) => {
|
|
215
|
+
if (persistedItems.find((item) => item.value === v)) {
|
|
216
|
+
acc[0].push(v);
|
|
217
|
+
} else {
|
|
218
|
+
acc[1].push(v);
|
|
219
|
+
}
|
|
220
|
+
return acc;
|
|
221
|
+
},
|
|
222
|
+
[[], []] as [string[], string[]],
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
const fetched = toFetch.map(createItem);
|
|
226
|
+
const items = [...persistedItems.filter((item) => preserved.includes(item.value)), ...fetched];
|
|
227
|
+
setPersistedItems(items);
|
|
228
|
+
setCurrentSelectedItems(items);
|
|
229
|
+
} else if (typeof value === 'string' && value !== '') {
|
|
230
|
+
// check if value is already part of the persisted items
|
|
231
|
+
const item = persistedItems.find((item) => item.value === value);
|
|
232
|
+
// if yes, then we don't need to fetch the label again
|
|
233
|
+
if (item) {
|
|
234
|
+
setCurrentSelectedItems([item]);
|
|
235
|
+
} else {
|
|
236
|
+
setCurrentSelectedItems([createItem(value)]);
|
|
237
|
+
}
|
|
238
|
+
setCurrentSelectedItems([createItem(value)]);
|
|
239
|
+
}
|
|
240
|
+
}, [value, children]);
|
|
241
|
+
|
|
242
|
+
const renderSelect = () => {
|
|
243
|
+
const hasOptions =
|
|
244
|
+
options &&
|
|
245
|
+
options.some((optionGroup) =>
|
|
246
|
+
Children.toArray(optionGroup?.props?.children).some(
|
|
247
|
+
(option) => isValidElement(option) && option.props?.children,
|
|
248
|
+
),
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
// initialFocus=-1 is used to prevent the first item from being focused when the list opens
|
|
252
|
+
return (
|
|
253
|
+
<FloatingFocusManager context={context} visuallyHiddenDismiss initialFocus={-1}>
|
|
254
|
+
<SelectContainer
|
|
255
|
+
{...getFloatingProps({
|
|
256
|
+
ref: refs.setFloating,
|
|
257
|
+
style: {
|
|
258
|
+
position: strategy,
|
|
259
|
+
top: y ?? 0,
|
|
260
|
+
left: x ?? 0,
|
|
261
|
+
overflow: 'auto',
|
|
262
|
+
},
|
|
263
|
+
})}
|
|
264
|
+
>
|
|
265
|
+
{' '}
|
|
266
|
+
{handleInputValueChange && (
|
|
267
|
+
<GenericTextField
|
|
268
|
+
id={`${name}-input`}
|
|
269
|
+
name={`${name}-input`}
|
|
270
|
+
hasDescription={false}
|
|
271
|
+
icon={<SearchIcon />}
|
|
272
|
+
suffix={isLoadingData ? 'Loading' : optionCount !== undefined ? `Result: ${optionCount}` : undefined}
|
|
273
|
+
hasError={hasError}
|
|
274
|
+
value={inputValue.value}
|
|
275
|
+
onChange={onInputChange}
|
|
276
|
+
placeholder={placeholder}
|
|
277
|
+
ref={ref}
|
|
278
|
+
/>
|
|
279
|
+
)}
|
|
280
|
+
{/* it will always contain 1 because of the group label */}
|
|
281
|
+
{isLoadingData && (
|
|
282
|
+
<FeedBackContainer>
|
|
283
|
+
<Spinner size="small" />
|
|
284
|
+
<span style={{ marginLeft: '10px' }}>loading results</span>
|
|
285
|
+
</FeedBackContainer>
|
|
286
|
+
)}
|
|
287
|
+
{/* show options if they exist*/}
|
|
288
|
+
{hasOptions && options}
|
|
289
|
+
{/* Add an infinite scroll component add the bottom when there is data, and we are not already loading */}
|
|
290
|
+
{hasOptions && !isLoadingData && (
|
|
291
|
+
<InfiniteScroll
|
|
292
|
+
isFetching={isFetching}
|
|
293
|
+
hasNextPage={hasNextPage}
|
|
294
|
+
fetchNextPage={fetchNextPage}
|
|
295
|
+
isFetchingNextPage={isFetchingNextPage}
|
|
296
|
+
/>
|
|
297
|
+
)}
|
|
298
|
+
{/* Basically first interaction */}
|
|
299
|
+
{!hasOptions && inputValue.value === '' && handleInputValueChange && (
|
|
300
|
+
<FeedBackContainer>Start typing to search</FeedBackContainer>
|
|
301
|
+
)}
|
|
302
|
+
{/* When there is no result */}
|
|
303
|
+
{!hasOptions && !isLoadingData && <FeedBackContainer>No results found</FeedBackContainer>}
|
|
304
|
+
</SelectContainer>
|
|
305
|
+
</FloatingFocusManager>
|
|
306
|
+
);
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const options = useMemo(() => {
|
|
310
|
+
return [
|
|
311
|
+
/* this is needed, but we need to get rid of the items that are already present in the options that were actually loaded
|
|
312
|
+
persistedItems.length > 0 && (
|
|
313
|
+
<ul key={`selected-items-${name}`} role="group" aria-labelledby={'select-items-list'}>
|
|
314
|
+
<GroupLabel role="presentation" id={'select-group-label'} aria-hidden="false">
|
|
315
|
+
Selected items
|
|
316
|
+
</GroupLabel>
|
|
317
|
+
{persistedItems.map((item) => (
|
|
318
|
+
<Option key={`${name}-${item.value}`} value={item.value} label={item.label} onChange={onChange as any}>
|
|
319
|
+
{item.label}
|
|
320
|
+
</Option>
|
|
321
|
+
))}
|
|
322
|
+
</ul>
|
|
323
|
+
),
|
|
324
|
+
*/
|
|
325
|
+
|
|
326
|
+
...(Children.map(
|
|
327
|
+
// children here is an <OptionGroup/>
|
|
328
|
+
children,
|
|
329
|
+
(child) =>
|
|
330
|
+
isValidElement(child) && (
|
|
331
|
+
<ul key={child.props.label} role="group" aria-labelledby={`select-${child.props.label}`}>
|
|
332
|
+
{child.props.label && (
|
|
333
|
+
<GroupLabel role="presentation" id={`select-${child.props.label}`} aria-hidden="true">
|
|
334
|
+
{child.props.label}
|
|
335
|
+
</GroupLabel>
|
|
336
|
+
)}
|
|
337
|
+
{Children.map(child.props.children, (option) => {
|
|
338
|
+
return cloneElement(option, {
|
|
339
|
+
onChange: onChange,
|
|
340
|
+
});
|
|
341
|
+
})}
|
|
342
|
+
</ul>
|
|
343
|
+
),
|
|
344
|
+
)?.filter(Boolean) ?? []),
|
|
345
|
+
];
|
|
346
|
+
}, [children, persistedItems]);
|
|
347
|
+
|
|
348
|
+
return (
|
|
349
|
+
<SelectContext.Provider
|
|
350
|
+
value={{
|
|
351
|
+
listRef: listItemsRef,
|
|
352
|
+
setOpen,
|
|
353
|
+
getItemProps,
|
|
354
|
+
setActiveIndex,
|
|
355
|
+
activeIndex,
|
|
356
|
+
dataRef: context.dataRef,
|
|
357
|
+
multiple,
|
|
358
|
+
selectedItems: currentSelectedItems,
|
|
359
|
+
setSelectedItems: setCurrentSelectedItems,
|
|
360
|
+
name,
|
|
361
|
+
}}
|
|
362
|
+
>
|
|
363
|
+
<SelectButton
|
|
364
|
+
id={id}
|
|
365
|
+
ref={refs.setReference}
|
|
366
|
+
disabled={disabled}
|
|
367
|
+
readOnly={readOnly}
|
|
368
|
+
onBlur={onBlur}
|
|
369
|
+
onFocus={onFocus}
|
|
370
|
+
isOpen={open}
|
|
371
|
+
tabIndex={disabled ? -1 : 0}
|
|
372
|
+
hasError={hasError}
|
|
373
|
+
aria-describedby={setAriaDescribedBy(name, hasDescription)}
|
|
374
|
+
{...getReferenceProps()}
|
|
375
|
+
>
|
|
376
|
+
{render ? (
|
|
377
|
+
render(currentSelectedItems)
|
|
378
|
+
) : (
|
|
379
|
+
<div>
|
|
380
|
+
{currentSelectedItems.length === 0 ? 'Select' : currentSelectedItems.map((item) => item.label).join(', ')}
|
|
381
|
+
</div>
|
|
382
|
+
)}
|
|
383
|
+
|
|
384
|
+
{!readOnly && canClear && currentSelectedItems.length > 0 && !open ? (
|
|
385
|
+
<IconButton size="tiny" icon={<ClearIcon />} ariaLabel="clear" onClick={(e) => handleClear(e)} />
|
|
386
|
+
) : (
|
|
387
|
+
<StyledArrowIcon size={16} />
|
|
388
|
+
)}
|
|
389
|
+
</SelectButton>
|
|
390
|
+
{open && <FloatingPortal>{renderSelect()}</FloatingPortal>}
|
|
391
|
+
</SelectContext.Provider>
|
|
392
|
+
);
|
|
393
|
+
},
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
// TODO: type it correctly instead
|
|
397
|
+
type GenericSelectQueryFieldType = typeof _GenericSelectQueryField & SubComponentTypes;
|
|
398
|
+
export const GenericSelectQueryField = _GenericSelectQueryField as GenericSelectQueryFieldType;
|
|
399
|
+
|
|
400
|
+
GenericSelectQueryField.Option = Option;
|
|
401
|
+
GenericSelectQueryField.OptionGroup = OptionGroup;
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Meta, StoryFn } from '@storybook/react';
|
|
3
|
+
import { SubmitHandler, useForm } from 'react-hook-form';
|
|
4
|
+
import { Button, SelectQueryField, SelectQueryFieldProps } from '../../../../components';
|
|
5
|
+
import { films } from '../data';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
8
|
+
import { GenericSelectQueryField } from './Generic';
|
|
9
|
+
|
|
10
|
+
interface Film {
|
|
11
|
+
name: string;
|
|
12
|
+
decade: string;
|
|
13
|
+
icon: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
|
+
title: 'Inputs/SelectQueryField',
|
|
18
|
+
args: {
|
|
19
|
+
placeholder: 'Search for a film',
|
|
20
|
+
label: 'Film',
|
|
21
|
+
debounce: 250,
|
|
22
|
+
readOnly: false,
|
|
23
|
+
canClear: false,
|
|
24
|
+
},
|
|
25
|
+
} as Meta<SelectQueryFieldProps>;
|
|
26
|
+
|
|
27
|
+
export const ServerSideSubmit: StoryFn<SelectQueryFieldProps> = (args) => {
|
|
28
|
+
interface FormValues {
|
|
29
|
+
film: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const { control, handleSubmit } = useForm<FormValues>();
|
|
33
|
+
const [loading, setLoading] = useState<boolean>(false);
|
|
34
|
+
const [options, setOptions] = useState<Film[]>();
|
|
35
|
+
const [result, setResult] = useState<string>('');
|
|
36
|
+
|
|
37
|
+
// Function to handle the simulated API call
|
|
38
|
+
// This function will be called each time the input changes
|
|
39
|
+
const mockAPICall = (debouncedValue: string) => {
|
|
40
|
+
// Start loading
|
|
41
|
+
setLoading(true);
|
|
42
|
+
|
|
43
|
+
setTimeout(() => {
|
|
44
|
+
const filteredOptions = films.filter((film) =>
|
|
45
|
+
film.name.toLowerCase().trim().includes(debouncedValue.toLowerCase()),
|
|
46
|
+
);
|
|
47
|
+
setOptions(filteredOptions);
|
|
48
|
+
setLoading(false);
|
|
49
|
+
}, 1000);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const onSubmit: SubmitHandler<FormValues> = ({ film }) => {
|
|
53
|
+
setResult(film);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<>
|
|
58
|
+
it is important to note that it is required for the user to select an option from the list. If the user types
|
|
59
|
+
something, but does not select an item from the list, the input will be reset.
|
|
60
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
61
|
+
<SelectQueryField
|
|
62
|
+
debounce={args.debounce}
|
|
63
|
+
control={control}
|
|
64
|
+
loading={loading}
|
|
65
|
+
canClear={args.canClear}
|
|
66
|
+
placeholder={args.placeholder}
|
|
67
|
+
label={args.placeholder}
|
|
68
|
+
/* The onChange returns the not debounced value, probably never needed, but is inherited by all inputFields */
|
|
69
|
+
handleInputValueChange={mockAPICall}
|
|
70
|
+
isLoadingData={loading}
|
|
71
|
+
required={false}
|
|
72
|
+
hasNextPage={false}
|
|
73
|
+
optionCount={10}
|
|
74
|
+
isFetching={false}
|
|
75
|
+
isFetchingNextPage={false}
|
|
76
|
+
fetchNextPage={() => {}}
|
|
77
|
+
name="film"
|
|
78
|
+
>
|
|
79
|
+
{/* In this case the label is the same as the value but ofcourse that can differ*/}
|
|
80
|
+
<SelectQueryField.OptionGroup>
|
|
81
|
+
{options?.map(({ name, icon }) => (
|
|
82
|
+
<SelectQueryField.Option key={name} value={name} label={name}>
|
|
83
|
+
<div style={{ display: 'flex', alignItems: 'center' }}>
|
|
84
|
+
<img src={icon} width="20px" height="20px" style={{ borderRadius: '50%', marginRight: '10px' }} />
|
|
85
|
+
<span>{name}</span>
|
|
86
|
+
</div>
|
|
87
|
+
</SelectQueryField.Option>
|
|
88
|
+
))}
|
|
89
|
+
</SelectQueryField.OptionGroup>
|
|
90
|
+
</SelectQueryField>
|
|
91
|
+
<Button type="submit">Submit form</Button>
|
|
92
|
+
</form>
|
|
93
|
+
This is the submitted value:
|
|
94
|
+
<div>{result}</div>
|
|
95
|
+
</>
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const ClientSideSubmit: StoryFn<SelectQueryFieldProps> = (args) => {
|
|
100
|
+
interface FormValues {
|
|
101
|
+
film: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const { control, handleSubmit } = useForm<FormValues>();
|
|
105
|
+
const [options, setOptions] = useState<Film[]>();
|
|
106
|
+
const [result, setResult] = useState<string>('');
|
|
107
|
+
|
|
108
|
+
// Function to handle the simulated API call
|
|
109
|
+
// This function will be called each time the input changes
|
|
110
|
+
const handleInputChange = (value: string) => {
|
|
111
|
+
// Start loading
|
|
112
|
+
const filteredOptions = films.filter((film) => film.name.toLowerCase().trim().includes(value.toLowerCase()));
|
|
113
|
+
setOptions(filteredOptions);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const onSubmit: SubmitHandler<FormValues> = ({ film }) => {
|
|
117
|
+
setResult(film);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<>
|
|
122
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
123
|
+
<SelectQueryField
|
|
124
|
+
control={control}
|
|
125
|
+
placeholder="Search for a film"
|
|
126
|
+
label="Film"
|
|
127
|
+
canClear={args.canClear}
|
|
128
|
+
/* The onChange returns the not debounced value, probably never needed, but is inherited by all inputFields */
|
|
129
|
+
handleInputValueChange={handleInputChange}
|
|
130
|
+
required={false}
|
|
131
|
+
debounce={0}
|
|
132
|
+
hasNextPage={false}
|
|
133
|
+
optionCount={10}
|
|
134
|
+
isFetching={false}
|
|
135
|
+
isFetchingNextPage={false}
|
|
136
|
+
fetchNextPage={() => {}}
|
|
137
|
+
name="film"
|
|
138
|
+
>
|
|
139
|
+
{/* In this case the label is the same as the value but ofcourse that can differ*/}
|
|
140
|
+
<SelectQueryField.OptionGroup>
|
|
141
|
+
{options?.map(({ name, icon, decade }) => (
|
|
142
|
+
<SelectQueryField.Option key={name} value={decade} label={name}>
|
|
143
|
+
<div style={{ display: 'flex', alignItems: 'center' }}>
|
|
144
|
+
<img src={icon} width="20px" height="20px" style={{ borderRadius: '50%', marginRight: '10px' }} />
|
|
145
|
+
<span>{name}</span>
|
|
146
|
+
</div>
|
|
147
|
+
</SelectQueryField.Option>
|
|
148
|
+
))}
|
|
149
|
+
</SelectQueryField.OptionGroup>
|
|
150
|
+
</SelectQueryField>
|
|
151
|
+
<Button type="submit">Submit form</Button>
|
|
152
|
+
</form>
|
|
153
|
+
The result returns the decade of the film:
|
|
154
|
+
<div>{result}</div>
|
|
155
|
+
</>
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export const ClientSideMultiSelectSubmit: StoryFn<SelectQueryFieldProps> = (args) => {
|
|
160
|
+
interface FormValues {
|
|
161
|
+
films: string[];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const validationSchema = z.object({
|
|
165
|
+
films: z.array(z.string()).nonempty(),
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const { control, handleSubmit } = useForm<FormValues>({
|
|
169
|
+
resolver: zodResolver(validationSchema),
|
|
170
|
+
});
|
|
171
|
+
const [options, setOptions] = useState<Film[]>();
|
|
172
|
+
const [result, setResult] = useState<string[]>([]);
|
|
173
|
+
|
|
174
|
+
// Function to handle the simulated API call
|
|
175
|
+
// This function will be called each time the input changes
|
|
176
|
+
const handleInputChange = (value: string) => {
|
|
177
|
+
// Start loading
|
|
178
|
+
const filteredOptions = films.filter((film) => film.name.toLowerCase().trim().includes(value.toLowerCase()));
|
|
179
|
+
setOptions(filteredOptions);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const onSubmit: SubmitHandler<FormValues> = ({ films }) => {
|
|
183
|
+
if (films && films.length === 0) return;
|
|
184
|
+
setResult(films);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<>
|
|
189
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
190
|
+
<SelectQueryField
|
|
191
|
+
control={control}
|
|
192
|
+
placeholder="Search for a film"
|
|
193
|
+
label="Film"
|
|
194
|
+
/* The onChange returns the not debounced value, probably never needed, but is inherited by all inputFields */
|
|
195
|
+
handleInputValueChange={handleInputChange}
|
|
196
|
+
required={false}
|
|
197
|
+
canClear={args.canClear}
|
|
198
|
+
debounce={0}
|
|
199
|
+
hasNextPage={false}
|
|
200
|
+
optionCount={10}
|
|
201
|
+
isFetching={false}
|
|
202
|
+
isFetchingNextPage={false}
|
|
203
|
+
fetchNextPage={() => {}}
|
|
204
|
+
multiple
|
|
205
|
+
name="films"
|
|
206
|
+
>
|
|
207
|
+
{/* In this case the label is the same as the value but ofcourse that can differ*/}
|
|
208
|
+
<SelectQueryField.OptionGroup>
|
|
209
|
+
{options?.map(({ name, icon }) => (
|
|
210
|
+
<SelectQueryField.Option key={name} value={name} label={name}>
|
|
211
|
+
<div style={{ display: 'flex', alignItems: 'center' }}>
|
|
212
|
+
<img src={icon} width="20px" height="20px" style={{ borderRadius: '50%', marginRight: '10px' }} />
|
|
213
|
+
<span>{name}</span>
|
|
214
|
+
</div>
|
|
215
|
+
</SelectQueryField.Option>
|
|
216
|
+
))}
|
|
217
|
+
</SelectQueryField.OptionGroup>
|
|
218
|
+
</SelectQueryField>
|
|
219
|
+
<Button type="submit">Submit form</Button>
|
|
220
|
+
</form>
|
|
221
|
+
Selected films:
|
|
222
|
+
<div>{result && result.join(',')}</div>
|
|
223
|
+
</>
|
|
224
|
+
);
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
export const Generic: StoryFn<SelectQueryFieldProps> = () => {
|
|
228
|
+
const [options, setOptions] = useState<Film[]>();
|
|
229
|
+
const [result, setResult] = useState<string[]>([]);
|
|
230
|
+
|
|
231
|
+
// Function to handle the simulated API call
|
|
232
|
+
// This function will be called each time the input changes
|
|
233
|
+
const handleInputChange = (value: string) => {
|
|
234
|
+
// Start loading
|
|
235
|
+
const filteredOptions = films.filter((film) => film.name.toLowerCase().trim().includes(value.toLowerCase()));
|
|
236
|
+
setOptions(filteredOptions);
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const onChange = (values: string[]) => {
|
|
240
|
+
setResult(values);
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
return (
|
|
244
|
+
<>
|
|
245
|
+
<GenericSelectQueryField
|
|
246
|
+
id="films"
|
|
247
|
+
onChange={onChange}
|
|
248
|
+
placeholder="Search for a film"
|
|
249
|
+
/* The onChange returns the not debounced value, probably never needed, but is inherited by all inputFields */
|
|
250
|
+
handleInputValueChange={handleInputChange}
|
|
251
|
+
required={false}
|
|
252
|
+
debounce={0}
|
|
253
|
+
multiple
|
|
254
|
+
hasError={false}
|
|
255
|
+
hasDescription={false}
|
|
256
|
+
value={result}
|
|
257
|
+
name="film"
|
|
258
|
+
hasNextPage={false}
|
|
259
|
+
optionCount={10}
|
|
260
|
+
isFetching={false}
|
|
261
|
+
isFetchingNextPage={false}
|
|
262
|
+
fetchNextPage={() => {}}
|
|
263
|
+
>
|
|
264
|
+
{/* In this case the label is the same as the value but ofcourse that can differ*/}
|
|
265
|
+
<SelectQueryField.OptionGroup>
|
|
266
|
+
{options?.map(({ name, icon }) => (
|
|
267
|
+
<SelectQueryField.Option key={name} value={name} label={name}>
|
|
268
|
+
<div style={{ display: 'flex', alignItems: 'center' }}>
|
|
269
|
+
<img src={icon} width="20px" height="20px" style={{ borderRadius: '50%', marginRight: '10px' }} />
|
|
270
|
+
<span>{name}</span>
|
|
271
|
+
</div>
|
|
272
|
+
</SelectQueryField.Option>
|
|
273
|
+
))}
|
|
274
|
+
</SelectQueryField.OptionGroup>
|
|
275
|
+
</GenericSelectQueryField>
|
|
276
|
+
Selected films:
|
|
277
|
+
<div>{result.map((film) => film).join(',')}</div>
|
|
278
|
+
</>
|
|
279
|
+
);
|
|
280
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { GenericSelectQueryField } from '../SelectQueryField/Generic';
|
|
2
|
+
export type { SelectQueryFieldProps, GenericSelectQueryFieldProps } from '../SelectQueryField/Generic';
|
|
3
|
+
|
|
4
|
+
export { ControlledSelectQueryField } from './Controlled';
|
|
5
|
+
export type { ControlledSelectQueryFieldProps } from './Controlled';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { styled } from '../../../../styled';
|
|
2
|
+
|
|
3
|
+
export const FeedBackContainer = styled.div`
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direciton: row;
|
|
6
|
+
align-items: center;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
|
|
9
|
+
// loading
|
|
10
|
+
span {
|
|
11
|
+
margin-left: ${({ theme }) => theme.spacing['1']};
|
|
12
|
+
}
|
|
13
|
+
`;
|