import React, { useEffect, useMemo, useState } from 'react' import { type Meta, type StoryObj } from '@storybook/react-vite' import { DocsTemplate } from '../../../.storybook' import MultiSelect from './MultiSelect' import { generateArray, generateDummyNestedMultiSelectData, genericPropsMultiSelect, multiSelectDisabledList, multiSelectOptions, useApiMultiSelectStory, } from './MultiSelectStoryHelpers' import { CheckboxState, type DateCheckState, getFlattenNestedTreeList, } from '../Form/NestedCheckboxHelper' export default { title: 'Components/FormComponents/MultiSelect/Exposed', component: MultiSelect, parameters: { layout: 'centered', docs: { page: () => ( This version of the MultiSelect component is used to quickly see the multi-select form instead of opening the dropdown. } infoBullets={[ Implement this version of MultiSelect component in forms, filtering interfaces, or settings sections where the height of its container is known. , This version of MultiSelect also has all the abilities of the base MultiSelect component like Search, disabling options initially and allowing secondary text for options etc. , ]} /> ), }, }, } as Meta const nestedMultiSelectData = generateDummyNestedMultiSelectData() const HandleMultiSelect = (args) => { const { options, selectedOptions, searchPlaceholder, noListDataText, maxHeight, loading, isPaginatable, } = args const [selected, setSelected] = useState(selectedOptions) const [searchText, setSearchText] = useState('') const { apiData, selectedData, fetchNew, setSelectedData } = useApiMultiSelectStory(searchText) return (
{isPaginatable ? ( { setSelectedData(selectedList) }} selectPlaceholder='--- Users ---' fetchData={fetchNew} hasMore={searchText.length === 0 && apiData.length < 100} exposed={args.exposed} /> ) : ( { setSelected(selectedList) }} searchPlaceholder={searchPlaceholder} noListDataText={noListDataText} maxHeight={maxHeight} loading={loading} /> )}
) } const HandleNestedMultiSelect = (args) => { const { options, selectedOptions, noListDataText } = args const [selected, setSelected] = useState(selectedOptions) const flattenData = getFlattenNestedTreeList({ nestedData: nestedMultiSelectData ?? [], childrenKey: 'children', parentKey: 'parent_category_id', idKey: 'id', }) const defaultItemStates = useMemo( () => flattenData.map((i) => ({ id: i['id'] as string | number, state: CheckboxState.UNCHECKED, })), [flattenData], ) const identifierKeys = { displayKey: 'name', idKey: 'id', parentKey: 'parent_category_id', } const tableConfig = [ { label: '', dataKey: 'name', children: (data) => {data.name}, }, ] const [itemStates, setItemStates] = useState(defaultItemStates) return (
{ setSelected(selectedList) }} noListDataText={noListDataText} labelKey='name' nestedConfig={{ data: flattenData, itemStates: itemStates, setItemStates: setItemStates, identifierKeys: identifierKeys, tableConfig, }} searchBarProps={{ ...args.searchBarProps, show: true, }} />
) } type Story = StoryObj const NestedTemplate: Story = { render: (args) => { return }, } const Template: Story = { render: (args) => { return }, } const options = multiSelectOptions const disabledList = multiSelectDisabledList const genericProps = { ...genericPropsMultiSelect, exposed: true, } export const Basic: Story = { ...Template, args: { ...genericProps, }, } export const SelectedOptions: Story = { ...Template, args: { ...genericProps, selectedOptions: [options[32], options[6]], }, } export const DisabledOptions: Story = { ...Template, args: { ...genericProps, selectedOptions: [disabledList[23], disabledList[32], disabledList[3]], options: disabledList, }, } export const ManyOptions: Story = { ...Template, args: { ...genericProps, options: generateArray(1000), }, } export const Nested: Story = { ...NestedTemplate, args: { ...genericProps, formLabelProps: { ...genericProps.formLabelProps, label: 'Category', }, options: nestedMultiSelectData, }, } export const LongNameOptions: Story = { ...Template, args: { ...genericProps, options: [ { ...options[0], name: 'This is a long option name that will wrap to the next line and continue with another sentence to demonstrate the wrapping behavior', }, ...options.slice(1), ], }, } export const WordBreak: Story = { ...Template, args: { ...genericProps, options: [ { ...options[0], name: 'This is a loooooooooooooooooooooooooooooooooooooooooong character name', }, ...options.slice(1), ], }, } export const WithSecondaryOptions: Story = { ...Template, args: { ...genericProps, options: generateArray(50, true), }, } export const MaxHeight: Story = { ...Template, args: { ...genericProps, maxHeight: 500, }, } export const Loading: Story = { ...Template, args: { ...genericProps, loading: true, }, } export const HideSelectAll: Story = { ...Template, args: { ...genericProps, hideSelectAll: true, }, } export const MultiSelectWithPagination: Story = { render: (args) => { return }, } export const ReorderableMultiSelect: Story = { ...Template, args: { ...genericProps, isReorderable: true, }, } export const DisabledItemsSection: Story = { ...Template, args: { ...genericProps, options: disabledList, disabledItemsSection: { enabled: true, }, }, } export const DisabledSectionHeaderTitle: Story = { ...Template, args: { ...genericProps, options: disabledList.map((item) => item.disabled ? { ...item, secondaryOption: 'Secondary Text' } : item, ), disabledItemsSection: { enabled: true, primaryTitle: 'Removed', secondaryTitle: 'Reason', }, }, } export const DisabledSectionWithNoDisabledItems: Story = { ...Template, args: { ...genericProps, disabledItemsSection: { enabled: true, }, }, } export const NoBorder: Story = { ...Template, args: { ...genericProps, removeBorder: true, }, } const HandleMultiSelectDisabledSectionWithCustomDisabledKey = (args) => { const { options, searchPlaceholder, noListDataText, maxHeight, loading } = args const [selected, setSelected] = useState([]) const [listOptions, setListOptions] = useState(options) const [isMaxItemsSelected, setIsMaxItemsSelected] = useState(false) useEffect(() => { const unselectedItems = [ ...options .filter( (option) => !selected.find( (selectedOption) => selectedOption.name === option.name, ), ) .map((option) => ({ ...option, disabled: isMaxItemsSelected })), ] setListOptions(unselectedItems) }, [selected, isMaxItemsSelected, setListOptions, options]) return (
{ setSelected(selectedList) if (selectedList.length >= 5) { setIsMaxItemsSelected(true) } else { setIsMaxItemsSelected(false) } }} searchPlaceholder={searchPlaceholder} noListDataText={noListDataText} maxHeight={maxHeight} loading={loading} />
) } const DisabledSectionWithCustomDisabledKeyTemplate: Story = { render: ({ ...args }) => { return }, } export const DisabledSectionWithCustomDisabledKey: Story = { ...DisabledSectionWithCustomDisabledKeyTemplate, args: { ...genericProps, formLabelProps: { ...genericProps.formLabelProps, rightLabel: 'Max 5 options can be selected', }, options: disabledList.map((item) => item.disabled ? { ...item, secondaryOption: 'Secondary Text', notAllowed: true, } : item, ), disabledItemsSection: { enabled: true, primaryTitle: 'Removed', secondaryTitle: 'Reason', disabledItemsKey: 'notAllowed', }, }, }