import { useRef, useState } from 'react'; import type { Story, Meta } from '@storybook/react'; import { randCompanyName } from '@ngneat/falso'; import { makeStyles, Theme } from '../../@styles'; import { arrayMove } from '../../../utils'; import { areEqualDropdownMenuItems, filterDropdownMenuItemsRecursive, processDropdownMenuItemsRecursive } from '../helpers'; import { MenuContent } from '../dropdown-menu'; import type { MenuContentProps, MenuItemProps } from '../types'; import { actionIcons } from '../__mocks__/menuItems'; import noop from 'lodash/noop'; function getMockCompaniesMenuItems(length = 10) { return randCompanyName({ length }).map( (companyName, index) => ({ name: companyName, value: index, selected: false } as MenuItemProps) ); } export default { component: MenuContent, title: 'Forms/Dropdown Menu/Menu Content', argTypes: {} } as Meta; const createClasses = makeStyles(theme => ({ menuList: { marginRight: 0, maxHeight: 420 } })); const InfinityTemplate: Story = args => { const styles = createClasses(); const { multiple } = args; const [listItems, setListItems] = useState(args.listItems); const portionCount = useRef(1); const portion = [ { id: 1, name: `test1-port${portionCount.current}` }, { id: 2, name: `test2-port${portionCount.current}` }, { id: 3, name: `test3-port${portionCount.current}` }, { id: 4, name: `test4-port${portionCount.current}` }, { id: 5, name: `test5-port${portionCount.current}` }, { id: 6, name: `test6-port${portionCount.current}` }, { id: 7, name: `test7-port${portionCount.current}` }, { id: 8, name: `test8-port${portionCount.current}` }, { id: 9, name: `test9-port${portionCount.current}` }, { id: 10, name: `test10-port${portionCount.current}` } ]; const groupPortion = [ { name: `test1-port${portionCount.current}`, group: `${portionCount.current + 2} Group Title`, value: portionCount.current * 100 + 1 }, { name: `test2-port${portionCount.current}`, group: `${portionCount.current + 2} Group Title`, value: portionCount.current * 100 + 2 }, { name: `test3-port${portionCount.current}`, group: `${portionCount.current + 2} Group Title`, value: portionCount.current * 100 + 3 }, { name: `test4-port${portionCount.current}`, group: `${portionCount.current + 2} Group Title`, value: portionCount.current * 100 + 4 }, { name: `test5-port${portionCount.current}`, group: `${portionCount.current + 3} Group Title`, value: portionCount.current * 100 + 5 }, { name: `test6-port${portionCount.current}`, group: `${portionCount.current + 3} Group Title`, value: portionCount.current * 100 + 6 }, { name: `test7-port${portionCount.current}`, group: `${portionCount.current + 3} Group Title`, value: portionCount.current * 100 + 7 } ]; const groupExists = listItems.some(item => !!item.options); const handleMenuClick: NonNullable = (name, value) => () => { const itemToSelect = { name, value }; const newItems = processDropdownMenuItemsRecursive(listItems, item => { const areEqual = areEqualDropdownMenuItems(item, itemToSelect); if (multiple ? areEqual : areEqual || item.selected) { return { ...item, selected: !item.selected }; } return item; }); setListItems(newItems); }; const handleDelete: MenuContentProps['handleDelete'] = (name, value) => event => { event.stopPropagation(); const itemToDelete = { name, value }; const newItems = filterDropdownMenuItemsRecursive( listItems, item => !areEqualDropdownMenuItems(item, itemToDelete) ); setListItems(newItems); }; // @todo: fix issue with groups sorting const onSortEnd: MenuContentProps['onSortEnd'] = sortArgs => { const { oldIndex, newIndex } = sortArgs; if (oldIndex !== newIndex) { const newArray = arrayMove(listItems, oldIndex, newIndex); setListItems(newArray); } }; const fetchFunction = () => { portionCount.current = portionCount.current + 1; if (groupExists) { setListItems(prevState => { const newState = [...prevState]; for (let i = 0; i < groupPortion.length; i++) { let index = newState.findIndex(item => item.name === groupPortion[i].group); if (index === -1) { index = newState.length; newState.push({ id: groupPortion[i].group, name: groupPortion[i].group, options: [] }); } newState[index]?.options?.push({ name: groupPortion[i].name, value: groupPortion[i].value }); } return newState; }); } else { setListItems(prevState => [...prevState, ...portion]); } }; return ( ); }; export const InfinityScrollPrimary = InfinityTemplate.bind({}); InfinityScrollPrimary.args = { ariaLabel: 'List with Infinity Scroll', listItems: [ { name: 'Item Number 1' }, { name: 'Item Number 2' }, { name: 'Item Number 3' }, { name: 'Item Number 4' }, { name: 'Item Number 5' }, { name: 'Action Icons 1', actionIcons: actionIcons.slice(0, 2) }, { name: 'Action Icons 2', actionIcons: actionIcons.slice(2, 4) }, { name: 'Action Icons 3', actionIcons: actionIcons.slice(0, 3) }, { name: 'Action Icons 4', actionIcons: actionIcons.slice(0, 2) }, { name: 'Action Icons 5', actionIcons: actionIcons.slice(2, 4) }, { name: 'Action Icons 6', actionIcons: actionIcons.slice(0, 3) }, { name: 'Disabled Item', disabled: true, selected: true }, { name: 'Disabled Item 2', disabled: true, selected: true }, { name: 'Disabled Item 3', disabled: true, selected: true } ], multiple: false, showSelectedIcon: true, sortable: false, variableSizeList: false, variableSizeListProps: { height: 420 }, infinityScrollProps: { fetchFunction: noop, allItemsCount: 100, threshold: 2 }, loadingText: 'Loading...' }; const mockCompaniesMenuItems = getMockCompaniesMenuItems(10); export const InfinityScrollWithGroups = InfinityTemplate.bind({}); InfinityScrollWithGroups.args = { ariaLabel: 'Grouped List with Infinity Scroll', listItems: [ { id: '__first', name: '1 Group Title', options: mockCompaniesMenuItems.slice(0, 4) }, { id: '__second', name: '2 Group Title', options: mockCompaniesMenuItems.slice(4, 8) }, { id: '__third', name: '3 Group Title', options: mockCompaniesMenuItems.slice(8, 10) } ], multiple: true, infinityScrollProps: { fetchFunction: noop, allItemsCount: 100, threshold: 2 }, loadingText: 'Loading...' };