import React, { useMemo, useState } from 'react' import { type Meta, type StoryObj } from '@storybook/react-vite' import { type Placement } from '@floating-ui/react' import { type DateRange } from 'react-day-picker' import { DocsTemplate } from '../../../.storybook' import ExposedFilterComponent from './ExposedFilter' import { type MultiSelectDataItemBase } from '../MultiSelect/MultiSelect' import { type MinMaxRange } from '../RangeFilter/RangeFilter' import { type ExposedFilterNameType, type ExposedFilterProps, } from './ExposedFilter.models' import { CheckboxState, type DateCheckState, getFlattenNestedTreeList, } from '../Form/NestedCheckboxHelper' import { generateDummyNestedMultiSelectData } from '../MultiSelect/MultiSelectStoryHelpers' import FormFooter from '../Form/FormFooter' import Checkbox from '../Form/Checkbox' interface FilterType extends MultiSelectDataItemBase, ExposedFilterNameType {} const meta: Meta = { title: 'Components/ExposedFilter/ExposedFilter', component: ExposedFilterComponent, parameters: { design: { type: 'figma', url: 'https://www.figma.com/design/RvhKD82948FMQnh5MyCi0o/Web-Design-System?node-id=14732-85850&p=f&t=9R4mlUY42yEaNlLp-0', }, docs: { page: () => ( The ExposedFilter component can be used when filters need to be exposed directly on page. } infoBullets={[ Utilize the ExposedFilter component to expose filters directly without SideDrawer , ]} /> ), }, }, } export default meta type Story = StoryObj const generatePaginatedData = (count: number) => { return Array.from({ length: count }, (_, index) => ({ id: index + 1, name: `Status ${index + 1}`, })) } const paginatedStatuses = generatePaginatedData(100) const ExposedFilter = (args) => { interface FilterStateType { status: FilterType[] date?: Date dateRange?: DateRange priority?: FilterType amount?: MinMaxRange nestedItemStates?: { id: string | number state: CheckboxState }[] modal?: string[] } const [filtersState, setFiltersState] = useState({ status: [], date: undefined, dateRange: undefined, priority: undefined, amount: { min: undefined, max: undefined }, nestedItemStates: [], modal: [], }) const [tempModalSelection, setTempModalSelection] = useState( filtersState?.modal || [], ) const [isModalOpen, setIsModalOpen] = useState(false) const onChangeCallout: ExposedFilterProps['onChangeCallout'] = ( filterName, value, ) => { setFiltersState((prevState) => ({ ...prevState, [filterName]: value, })) if (filterName === 'modal') { setTempModalSelection((value as string[]) || []) } } const nestedMultiSelectData = generateDummyNestedMultiSelectData() const flattenData = useMemo( () => getFlattenNestedTreeList({ nestedData: nestedMultiSelectData ?? [], childrenKey: 'children', parentKey: 'parent_category_id', idKey: 'id', }), [nestedMultiSelectData], ) 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) if (args.type === 'multi-select') { if (args.stateName === 'nestedItemStates') { return ( ) } else return ( ) } else if (args?.dateProps?.type === 'single') { return ( ) } else if (args?.dateProps?.type === 'range') { return ( ) } else if (args.type === 'select' && args.disabled) { return ( ) } else if (args.type === 'select') { return ( ) } else if (args.type === 'range') { return ( ) } else if (args.type === 'modal') { return ( setIsModalOpen(true)} selectedOption={filtersState.modal} modalProps={{ isOpen: isModalOpen, closeCallout: () => { setIsModalOpen(false) }, size: 'sm', headerContent: 'Item Filter', footerContent: ( { setFiltersState((prevState) => ({ ...prevState, modal: tempModalSelection, // update main state on Save })) setIsModalOpen(false) }, children: 'filter', styleType: 'primary', }} cancelButtonProps={{ onClick: () => { setIsModalOpen(false) }, }} /> ), children: (
{ setTempModalSelection((prev) => prev.includes('P0NEIWB5') ? prev.filter((item) => item !== 'P0NEIWB5') : [...prev, 'P0NEIWB5'], ) }} checkboxColor='blue' type='checkbox' /> { setTempModalSelection((prev) => prev.includes('P0OGSJWM') ? prev.filter((item) => item !== 'P0OGSJWM') : [...prev, 'P0OGSJWM'], ) }} checkboxColor='blue' type='checkbox' />
), }} onChangeCallout={onChangeCallout} /> ) } } const Template: Story = { render: (args) => , } const statuses = [ { id: 1, name: 'Created' }, { id: 2, name: 'Arrived' }, { id: 3, name: 'Processed' }, { id: 4, name: 'Receiving' }, { id: 5, name: 'Canceled' }, { id: 6, name: 'Pending' }, { id: 7, name: 'In Transit' }, { id: 8, name: 'Delivered' }, { id: 9, name: 'Returned' }, { id: 10, name: 'On Hold' }, { id: 11, name: 'Backordered' }, { id: 12, name: 'Failed' }, { id: 13, name: 'Completed' }, { id: 14, name: 'Refunded' }, { id: 15, name: 'Shipped' }, { id: 16, name: 'Pre-ordered' }, { id: 17, name: 'Out of Stock' }, { id: 18, name: 'Partially Shipped' }, { id: 19, name: 'Awaiting Approval' }, { id: 20, name: 'Quality Check' }, { id: 21, name: 'Rejected' }, { id: 22, name: 'Disputed' }, { id: 23, name: 'Under Review' }, { id: 24, name: 'Damaged' }, { id: 25, name: 'Archived' }, ] const multiSelectProps = { options: statuses, searchBarProps: { placeholder: 'Search', }, emptyStateProps: { primaryText: 'No Status Found', secondaryText: 'Try searching for a different status', }, labelKey: 'name', } const priorities = [ { id: 1, name: 'High' }, { id: 2, name: 'Medium' }, { id: 3, name: 'Low' }, ] const selectProps = { options: priorities, searchBarProps: { placeholder: 'Search priorities', }, emptyStateProps: { primaryText: 'No Priority Found', secondaryText: 'Try searching for a different priority', }, labelKey: 'name', } export const MultiSelect: Story = { ...Template, args: { multiSelectProps: multiSelectProps, type: 'multi-select', labelName: 'Status', stateName: 'status', }, } export const MultiSelectWithPagination: Story = { ...Template, args: { multiSelectProps: { ...multiSelectProps, options: paginatedStatuses, fetchData: () => { // Simulate API call for pagination console.log('Fetching more data...') }, hasMore: true, isFetching: false, }, type: 'multi-select', labelName: 'Status', stateName: 'status', }, } export const MultiSelectWithEmptyState: Story = { ...Template, args: { multiSelectProps: { options: [], searchBarProps: { placeholder: 'Search', }, emptyStateProps: { primaryText: 'No records found', secondaryText: 'Try to change your search', }, }, type: 'multi-select', labelName: 'Status', stateName: 'status', }, } export const Date: Story = { ...Template, args: { type: 'date', dateProps: { type: 'single' }, labelName: 'Date', stateName: 'date', }, } export const DateRangeFilter: Story = { ...Template, args: { type: 'date', dateProps: { type: 'range' }, labelName: 'Date Range', stateName: 'dateRange', }, } export const Select: Story = { ...Template, args: { selectProps: selectProps, type: 'select', labelName: 'Priority', stateName: 'priority', }, } export const Range: Story = { ...Template, args: { type: 'range', labelName: 'Amount', stateName: 'amount', }, } export const Nested: Story = { ...Template, args: { type: 'multi-select', labelName: 'Nested', stateName: 'nestedItemStates', }, } export const Modal: Story = { ...Template, args: { type: 'modal', labelName: 'Modal', stateName: 'modal', }, } export const DisabledFilterOption: Story = { ...Template, args: { selectProps: selectProps, type: 'select', disabled: true, disabledTooltip: { tooltipContent: 'This filter is disabled', position: 'top' as Placement, }, labelName: 'Priority', stateName: 'priority', }, }