import { 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'; 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: { listItems: { description: 'The items which will be shown in the list.' }, multiple: { description: 'Allow multiple selection. Notice your selection logic should also support multiple selection.' }, showSelectedIcon: { description: 'Whether or not to show selected icon on single selection.' }, search: { description: 'Whether or not to allow searching items inside the menu, notice you must pass `handleSearchChangeCallback` to make it work.' }, searchPlaceholder: { description: 'Place holder for the search field.' }, deletable: { description: 'Whether or not to show the delete icon for menu items, notice you must pass `handleDelete` to make it work.' }, width: { description: 'The width of the menu.' }, sortable: { description: 'Whether or not to show the sort icon for menu items, notice you must pass `onSortEnd` to make it work.' }, onSortEnd: { description: 'Callback which is fired on sort end.' }, handleSearchChangeCallback: { description: 'Callback which is fired on search text change.' }, bottomButtonText: { description: 'If passed, a button will appear in the bottom of the menu, pass `bottomButtonClickHandler` to make it work.' }, bottomButtonClickHandler: { description: 'Call back which will be fired on click the bottom button.' }, handleDelete: { description: 'Call back which will be fired on click a delete icon.' }, handleClick: { description: 'Call back which will be fired on clicking a menu item.' } } } as Meta; const createClasses = makeStyles(theme => ({ menuList: { maxHeight: 'none' } })); const Template: Story = args => { const styles = createClasses(); const { multiple } = args; const [listItems, setListItems] = useState(args.listItems); 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); } }; return ( ); }; export const Primary = Template.bind({}); Primary.args = { ariaLabel: 'Items List', listItems: [ { name: 'Item Number 1' }, { name: 'Item Number 2' }, { name: 'Item Number 3' }, { 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: 'Disabled Item', disabled: true, selected: true } ], multiple: false, showSelectedIcon: true, search: false, searchDebounceTime: 0, searchHighlight: true, searchPlaceholder: '', deletable: false, sortable: false, bottomButtonText: '', variableSizeList: false, variableSizeListProps: { height: 420 } }; export const WithSearch = Template.bind({}); WithSearch.args = { ariaLabel: 'Searchable List', listItems: getMockCompaniesMenuItems(100), multiple: true, showSelectedIcon: false, search: true, noOptionsText: 'No options', noResultsText: 'No results', deletable: false, sortable: false, searchHighlight: true, searchPlaceholder: 'Search placeholder', bottomButtonText: '', variableSizeList: false, variableSizeListProps: { height: 420 } }; const mockCompaniesMenuItems = getMockCompaniesMenuItems(50); export const WithGroups = Template.bind({}); WithGroups.args = { ariaLabel: 'Grouped List', listItems: [ { id: '__first', name: 'First Group Title', options: mockCompaniesMenuItems.slice(0, 10) }, { id: '__second', name: 'Second Group Title', options: mockCompaniesMenuItems.slice(10, 35) }, { id: '__third', name: 'Third Group Title', options: mockCompaniesMenuItems.slice(35, 45) }, { id: '__forth', name: 'Forth Group Title', options: mockCompaniesMenuItems.slice(45) } ], multiple: true, search: false, searchDebounceTime: 0, searchHighlight: true, searchPlaceholder: 'Search placeholder', variableSizeList: false, variableSizeListProps: { height: 420 } }; export const DynamicActions = Template.bind({}); DynamicActions.args = { ariaLabel: 'Dynamic Action Icons', listItems: mockCompaniesMenuItems, modifyMenuItem: props => props.selected ? { actionIcons: [], showSelectedIcon: true } : { actionIcons: actionIcons.slice(0, 1) }, search: false, searchHighlight: true, searchPlaceholder: 'Search placeholder', deletable: false, sortable: false, bottomButtonText: '', variableSizeList: false, variableSizeListProps: { height: 420 } };