feat: add New Field Step 2 form (#2138)

Closes #2001

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Thaïs
2023-10-21 13:28:15 +02:00
committed by GitHub
parent c90cf1eb8f
commit 34bbbdff41
15 changed files with 367 additions and 34 deletions

View File

@ -246,6 +246,7 @@ const StyledButton = styled.button<
return '0';
}
}};
box-sizing: content-box;
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
display: flex;
flex-direction: row;

View File

@ -10,7 +10,7 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { IconButton } from '../button/components/IconButton';
import { IconButton, IconButtonVariant } from '../button/components/IconButton';
import { LightIconButton } from '../button/components/LightIconButton';
import { IconApps } from '../constants/icons';
import { useLazyLoadIcons } from '../hooks/useLazyLoadIcons';
@ -24,6 +24,7 @@ type IconPickerProps = {
onClickOutside?: () => void;
onClose?: () => void;
onOpen?: () => void;
variant?: IconButtonVariant;
};
const StyledMenuIconItemsContainer = styled.div`
@ -47,6 +48,7 @@ export const IconPicker = ({
onClickOutside,
onClose,
onOpen,
variant = 'secondary',
}: IconPickerProps) => {
const [searchString, setSearchString] = useState('');
@ -79,7 +81,7 @@ export const IconPicker = ({
<IconButton
disabled={disabled}
Icon={selectedIconKey ? icons[selectedIconKey] : IconApps}
variant="secondary"
variant={variant}
/>
}
dropdownComponents={
@ -119,7 +121,7 @@ export const IconPicker = ({
setSearchString('');
}}
onOpen={onOpen}
></Dropdown>
/>
</DropdownScope>
);
};

View File

@ -0,0 +1,94 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconChevronDown } from '@/ui/display/icon';
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { SelectHotkeyScope } from '../types/SelectHotkeyScope';
export type SelectProps<Value extends string> = {
dropdownScopeId: string;
onChange: (value: Value) => void;
options: { value: Value; label: string; Icon?: IconComponent }[];
value?: Value;
};
const StyledContainer = styled.div`
align-items: center;
background-color: ${({ theme }) => theme.background.transparent.lighter};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.border.radius.sm};
color: ${({ theme }) => theme.font.color.primary};
cursor: pointer;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
height: ${({ theme }) => theme.spacing(8)};
justify-content: space-between;
padding: 0 ${({ theme }) => theme.spacing(2)};
`;
const StyledLabel = styled.div`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
`;
export const Select = <Value extends string>({
dropdownScopeId,
onChange,
options,
value,
}: SelectProps<Value>) => {
const theme = useTheme();
const selectedOption =
options.find(({ value: key }) => key === value) || options[0];
const { closeDropdown } = useDropdown({ dropdownScopeId });
return (
<DropdownScope dropdownScopeId={dropdownScopeId}>
<Dropdown
dropdownMenuWidth={176}
dropdownPlacement="bottom-start"
clickableComponent={
<StyledContainer>
<StyledLabel>
{!!selectedOption.Icon && (
<selectedOption.Icon
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
)}
{selectedOption.label}
</StyledLabel>
<IconChevronDown
color={theme.font.color.tertiary}
size={theme.icon.size.md}
/>
</StyledContainer>
}
dropdownComponents={
<DropdownMenuItemsContainer>
{options.map((option) => (
<MenuItem
key={option.value}
LeftIcon={option.Icon}
text={option.label}
onClick={() => {
onChange(option.value);
closeDropdown();
}}
/>
))}
</DropdownMenuItemsContainer>
}
dropdownHotkeyScope={{ scope: SelectHotkeyScope.Select }}
/>
</DropdownScope>
);
};

View File

@ -0,0 +1,51 @@
import { useState } from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { Select, SelectProps } from '../Select';
type RenderProps = SelectProps<string>;
const Render = (args: RenderProps) => {
const [value, setValue] = useState(args.value);
const handleChange = (value: string) => {
args.onChange?.(value);
setValue(value);
};
// eslint-disable-next-line react/jsx-props-no-spreading
return <Select {...args} value={value} onChange={handleChange} />;
};
const meta: Meta<typeof Select> = {
title: 'UI/Input/Select',
component: Select,
decorators: [ComponentDecorator],
args: {
dropdownScopeId: 'select',
value: 'a',
options: [
{ value: 'a', label: 'Option A' },
{ value: 'b', label: 'Option B' },
{ value: 'c', label: 'Option C' },
],
},
render: Render,
};
export default meta;
type Story = StoryObj<typeof Select>;
export const Default: Story = {};
export const Open: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const selectLabel = await canvas.getByText('Option A');
await userEvent.click(selectLabel);
},
};

View File

@ -0,0 +1,3 @@
export enum SelectHotkeyScope {
Select = 'select',
}