feat: lazy load IconPicker icons (#1753)

Closes #1750
This commit is contained in:
Thaïs
2023-09-29 16:23:36 +02:00
committed by GitHub
parent 1c35ccce4e
commit 4e181aa40e
3 changed files with 4235 additions and 31 deletions

View File

@ -1,4 +1,4 @@
import { useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { LightIconButton } from '@/ui/button/components/LightIconButton'; import { LightIconButton } from '@/ui/button/components/LightIconButton';
@ -8,9 +8,10 @@ import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/Style
import { StyledDropdownMenuSeparator } from '@/ui/dropdown/components/StyledDropdownMenuSeparator'; import { StyledDropdownMenuSeparator } from '@/ui/dropdown/components/StyledDropdownMenuSeparator';
import { IconComponent } from '@/ui/icon/types/IconComponent'; import { IconComponent } from '@/ui/icon/types/IconComponent';
import { DropdownMenuSkeletonItem } from '../relation-picker/components/skeletons/DropdownMenuSkeletonItem';
type IconPickerProps = { type IconPickerProps = {
icons: Record<string, IconComponent>; onChange: (params: { iconKey: string; Icon: IconComponent }) => void;
onChange: (iconName: string) => void;
selectedIconKey?: string; selectedIconKey?: string;
}; };
@ -29,24 +30,29 @@ const StyledLightIconButton = styled(LightIconButton)<{ isSelected?: boolean }>`
isSelected ? theme.background.transparent.light : 'transparent'}; isSelected ? theme.background.transparent.light : 'transparent'};
`; `;
const convertIconKeyToLabel = (iconName: string) => const convertIconKeyToLabel = (iconKey: string) =>
iconName.replace(/[A-Z]/g, (letter) => ` ${letter}`).trim(); iconKey.replace(/[A-Z]/g, (letter) => ` ${letter}`).trim();
export const IconPicker = ({ export const IconPicker = ({ onChange, selectedIconKey }: IconPickerProps) => {
icons,
onChange,
selectedIconKey,
}: IconPickerProps) => {
const [searchString, setSearchString] = useState(''); const [searchString, setSearchString] = useState('');
const [isLoading, setIsLoading] = useState(true);
const [icons, setIcons] = useState<Record<string, IconComponent>>({});
useEffect(() => {
import('../constants/icons').then((lazyLoadedIcons) => {
setIcons(lazyLoadedIcons);
setIsLoading(false);
});
}, []);
const iconKeys = useMemo(() => { const iconKeys = useMemo(() => {
const filteredIconKeys = Object.keys(icons).filter( const filteredIconKeys = Object.keys(icons).filter(
(iconKey) => (iconKey) =>
iconKey !== selectedIconKey && iconKey !== selectedIconKey &&
(!searchString || (!searchString ||
convertIconKeyToLabel(iconKey) [iconKey, convertIconKeyToLabel(iconKey)].some((label) =>
.toLowerCase() label.toLowerCase().includes(searchString.toLowerCase()),
.includes(searchString.toLowerCase())), )),
); );
return ( return (
@ -65,15 +71,19 @@ export const IconPicker = ({
/> />
<StyledDropdownMenuSeparator /> <StyledDropdownMenuSeparator />
<StyledMenuIconItemsContainer> <StyledMenuIconItemsContainer>
{iconKeys.map((iconKey) => ( {isLoading ? (
<StyledLightIconButton <DropdownMenuSkeletonItem />
aria-label={convertIconKeyToLabel(iconKey)} ) : (
isSelected={selectedIconKey === iconKey} iconKeys.map((iconKey) => (
size="medium" <StyledLightIconButton
Icon={icons[iconKey]} aria-label={convertIconKeyToLabel(iconKey)}
onClick={() => onChange(iconKey)} isSelected={selectedIconKey === iconKey}
/> size="medium"
))} Icon={icons[iconKey]}
onClick={() => onChange({ iconKey, Icon: icons[iconKey] })}
/>
))
)}
</StyledMenuIconItemsContainer> </StyledMenuIconItemsContainer>
</StyledIconPickerDropdownMenu> </StyledIconPickerDropdownMenu>
); );

View File

@ -2,8 +2,8 @@ import { expect } from '@storybook/jest';
import { Meta, StoryObj } from '@storybook/react'; import { Meta, StoryObj } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library'; import { userEvent, within } from '@storybook/testing-library';
import * as icons from '@/ui/icon';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { sleep } from '~/testing/sleep';
import { IconPicker } from '../IconPicker'; import { IconPicker } from '../IconPicker';
@ -11,14 +11,6 @@ const meta: Meta<typeof IconPicker> = {
title: 'UI/Input/IconPicker', title: 'UI/Input/IconPicker',
component: IconPicker, component: IconPicker,
decorators: [ComponentDecorator], decorators: [ComponentDecorator],
args: { icons },
argTypes: {
icons: { control: false },
selectedIconKey: {
options: Object.keys(icons),
control: { type: 'select' },
},
},
}; };
export default meta; export default meta;
@ -38,6 +30,8 @@ export const WithSearch: Story = {
await userEvent.type(searchInput, 'Building skyscraper'); await userEvent.type(searchInput, 'Building skyscraper');
await sleep(1000);
const searchedIcon = canvas.getByRole('button', { const searchedIcon = canvas.getByRole('button', {
name: 'Icon Building Skyscraper', name: 'Icon Building Skyscraper',
}); });

File diff suppressed because it is too large Load Diff