Settings Form Select refacto (#8875)

fixes #8751
This commit is contained in:
Guillim
2024-12-05 14:44:24 +01:00
committed by GitHub
parent 6c78cac908
commit 9ed9b4746a
8 changed files with 156 additions and 142 deletions

View File

@ -5,45 +5,31 @@ import {
StyledSettingsOptionCardTitle, StyledSettingsOptionCardTitle,
} from '@/settings/components/SettingsOptions/SettingsOptionCardContentBase'; } from '@/settings/components/SettingsOptions/SettingsOptionCardContentBase';
import { SettingsOptionIconCustomizer } from '@/settings/components/SettingsOptions/SettingsOptionIconCustomizer'; import { SettingsOptionIconCustomizer } from '@/settings/components/SettingsOptions/SettingsOptionIconCustomizer';
import { Select, SelectValue } from '@/ui/input/components/Select';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { IconComponent } from 'twenty-ui'; import { IconComponent } from 'twenty-ui';
type SettingsOptionCardContentSelectProps<Value extends SelectValue> = { type SettingsOptionCardContentSelectProps = {
Icon?: IconComponent; Icon?: IconComponent;
title: React.ReactNode; title: React.ReactNode;
description?: string; description?: string;
divider?: boolean; divider?: boolean;
disabled?: boolean; disabled?: boolean;
value: Value;
onChange: (value: Value) => void;
options: {
value: Value;
label: string;
Icon?: IconComponent;
}[];
selectClassName?: string; selectClassName?: string;
dropdownId: string; children?: React.ReactNode;
fullWidth?: boolean;
}; };
const StyledSelectContainer = styled.div` const StyledSelectContainer = styled.div`
margin-left: auto; margin-left: auto;
`; `;
export const SettingsOptionCardContentSelect = <Value extends SelectValue>({ export const SettingsOptionCardContentSelect = ({
Icon, Icon,
title, title,
description, description,
divider, divider,
disabled = false, disabled = false,
value, children,
onChange, }: SettingsOptionCardContentSelectProps) => {
options,
selectClassName,
dropdownId,
fullWidth,
}: SettingsOptionCardContentSelectProps<Value>) => {
return ( return (
<StyledSettingsOptionCardContent divider={divider} disabled={disabled}> <StyledSettingsOptionCardContent divider={divider} disabled={disabled}>
{Icon && ( {Icon && (
@ -57,18 +43,7 @@ export const SettingsOptionCardContentSelect = <Value extends SelectValue>({
{description} {description}
</StyledSettingsOptionCardDescription> </StyledSettingsOptionCardDescription>
</div> </div>
<StyledSelectContainer> <StyledSelectContainer>{children}</StyledSelectContainer>
<Select<Value>
className={selectClassName}
dropdownWidth={fullWidth ? 'auto' : 120}
disabled={disabled}
dropdownId={dropdownId}
value={value}
onChange={onChange}
options={options}
selectSizeVariant="small"
/>
</StyledSelectContainer>
</StyledSettingsOptionCardContent> </StyledSettingsOptionCardContent>
); );
}; };

View File

@ -1,4 +1,5 @@
import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect'; import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect';
import { Select, SelectValue } from '@/ui/input/components/Select';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { Meta, StoryObj } from '@storybook/react'; import { Meta, StoryObj } from '@storybook/react';
import { useState } from 'react'; import { useState } from 'react';
@ -16,28 +17,37 @@ const StyledContainer = styled.div`
width: 480px; width: 480px;
`; `;
type SelectValue = string | number | boolean | null; interface SettingsOptionCardContentSelectProps
extends React.ComponentProps<typeof SettingsOptionCardContentSelect> {}
interface SettingsOptionCardContentSelectWrapperProps
extends SettingsOptionCardContentSelectProps {
onChange: any;
options: any;
value: any;
dropdownId: string;
}
const SettingsOptionCardContentSelectWrapper = <Value extends SelectValue>( const SettingsOptionCardContentSelectWrapper = <Value extends SelectValue>(
args: React.ComponentProps<typeof SettingsOptionCardContentSelect<Value>>, args: SettingsOptionCardContentSelectWrapperProps,
) => { ) => {
const [value, setValue] = useState<Value>(args.value); const [value] = useState<Value>(args.value);
return ( return (
<StyledContainer> <StyledContainer>
<SettingsOptionCardContentSelect <SettingsOptionCardContentSelect
value={value}
onChange={(newValue) => setValue(newValue as Value)}
Icon={args.Icon} Icon={args.Icon}
title={args.title} title={args.title}
description={args.description} description={args.description}
divider={args.divider} >
disabled={args.disabled} <Select<Value>
options={args.options} value={value}
selectClassName={args.selectClassName} onChange={args.onChange}
dropdownId={args.dropdownId} dropdownId={args.dropdownId}
fullWidth={args.fullWidth} options={args.options}
/> selectSizeVariant="small"
dropdownWidth={120}
/>
</SettingsOptionCardContentSelect>
</StyledContainer> </StyledContainer>
); );
}; };
@ -137,6 +147,5 @@ export const FullWidth: Story = {
}, },
], ],
dropdownId: 'full-width-select', dropdownId: 'full-width-select',
fullWidth: true,
}, },
}; };

View File

@ -4,11 +4,11 @@ import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { addressSchema as addressFieldDefaultValueSchema } from '@/object-record/record-field/types/guards/isFieldAddressValue'; import { addressSchema as addressFieldDefaultValueSchema } from '@/object-record/record-field/types/guards/isFieldAddressValue';
import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect'; import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect';
import { useCountries } from '@/ui/input/components/internal/hooks/useCountries'; import { useCountries } from '@/ui/input/components/internal/hooks/useCountries';
import { Select } from '@/ui/input/components/Select';
import { IconMap } from 'twenty-ui'; import { IconMap } from 'twenty-ui';
import { z } from 'zod'; import { z } from 'zod';
import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString'; import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString';
import { stripSimpleQuotesFromString } from '~/utils/string/stripSimpleQuotesFromString'; import { stripSimpleQuotesFromString } from '~/utils/string/stripSimpleQuotesFromString';
type SettingsDataModelFieldAddressFormProps = { type SettingsDataModelFieldAddressFormProps = {
disabled?: boolean; disabled?: boolean;
defaultCountry?: string; defaultCountry?: string;
@ -63,22 +63,26 @@ export const SettingsDataModelFieldAddressForm = ({
render={({ field: { onChange, value } }) => { render={({ field: { onChange, value } }) => {
const defaultCountry = value?.addressCountry || ''; const defaultCountry = value?.addressCountry || '';
return ( return (
<SettingsOptionCardContentSelect<string> <SettingsOptionCardContentSelect
Icon={IconMap} Icon={IconMap}
dropdownId="selectDefaultCountry"
title="Default Country" title="Default Country"
description="The default country for new addresses" description="The default country for new addresses"
value={stripSimpleQuotesFromString(defaultCountry)} >
onChange={(newCountry) => <Select<string>
onChange({ dropdownWidth={'auto'}
...value, disabled={disabled}
addressCountry: applySimpleQuotesToString(newCountry), dropdownId="selectDefaultCountry"
}) value={stripSimpleQuotesFromString(defaultCountry)}
} onChange={(newCountry) =>
disabled={disabled} onChange({
options={countries} ...value,
fullWidth={true} addressCountry: applySimpleQuotesToString(newCountry),
/> })
}
options={countries}
selectSizeVariant="small"
/>
</SettingsOptionCardContentSelect>
); );
}} }}
/> />

View File

@ -5,6 +5,7 @@ import { z } from 'zod';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect'; import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect';
import { useBooleanSettingsFormInitialValues } from '@/settings/data-model/fields/forms/boolean/hooks/useBooleanSettingsFormInitialValues'; import { useBooleanSettingsFormInitialValues } from '@/settings/data-model/fields/forms/boolean/hooks/useBooleanSettingsFormInitialValues';
import { Select } from '@/ui/input/components/Select';
export const settingsDataModelFieldBooleanFormSchema = z.object({ export const settingsDataModelFieldBooleanFormSchema = z.object({
defaultValue: z.boolean(), defaultValue: z.boolean(),
@ -15,12 +16,10 @@ export type SettingsDataModelFieldBooleanFormValues = z.infer<
>; >;
type SettingsDataModelFieldBooleanFormProps = { type SettingsDataModelFieldBooleanFormProps = {
className?: string;
fieldMetadataItem: Pick<FieldMetadataItem, 'defaultValue'>; fieldMetadataItem: Pick<FieldMetadataItem, 'defaultValue'>;
}; };
export const SettingsDataModelFieldBooleanForm = ({ export const SettingsDataModelFieldBooleanForm = ({
className,
fieldMetadataItem, fieldMetadataItem,
}: SettingsDataModelFieldBooleanFormProps) => { }: SettingsDataModelFieldBooleanFormProps) => {
const { control } = useFormContext<SettingsDataModelFieldBooleanFormValues>(); const { control } = useFormContext<SettingsDataModelFieldBooleanFormValues>();
@ -39,23 +38,27 @@ export const SettingsDataModelFieldBooleanForm = ({
Icon={IconCheck} Icon={IconCheck}
title="Default Value" title="Default Value"
description="Select the default value for this boolean field" description="Select the default value for this boolean field"
value={value} >
onChange={onChange} <Select<boolean>
selectClassName={className} value={value}
dropdownId="object-field-default-value-select-boolean" onChange={onChange}
options={[ dropdownId="object-field-default-value-select-boolean"
{ dropdownWidth={120}
value: true, options={[
label: 'True', {
Icon: IconCheck, value: true,
}, label: 'True',
{ Icon: IconCheck,
value: false, },
label: 'False', {
Icon: IconX, value: false,
}, label: 'False',
]} Icon: IconX,
/> },
]}
selectSizeVariant="small"
/>
</SettingsOptionCardContentSelect>
)} )}
/> />
); );

View File

@ -2,6 +2,7 @@ import { Controller, useFormContext } from 'react-hook-form';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect'; import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect';
import { Select } from '@/ui/input/components/Select';
import { IconTextWrap } from 'twenty-ui'; import { IconTextWrap } from 'twenty-ui';
import { z } from 'zod'; import { z } from 'zod';
@ -44,35 +45,39 @@ export const SettingsDataModelFieldTextForm = ({
<> <>
<SettingsOptionCardContentSelect <SettingsOptionCardContentSelect
Icon={IconTextWrap} Icon={IconTextWrap}
dropdownId="text-wrap"
title="Wrap on record pages" title="Wrap on record pages"
description="Display text on multiple lines" description="Display text on multiple lines"
value={displayedMaxRows} >
onChange={(value) => onChange({ displayedMaxRows: value })} <Select<number>
disabled={disabled} dropdownId="text-wrap"
options={[ value={displayedMaxRows}
{ onChange={(value) => onChange({ displayedMaxRows: value })}
label: 'Deactivated', disabled={disabled}
value: 0, options={[
}, {
{ label: 'Deactivated',
label: 'First 2 lines', value: 0,
value: 2, },
}, {
{ label: 'First 2 lines',
label: 'First 5 lines', value: 2,
value: 5, },
}, {
{ label: 'First 5 lines',
label: 'First 10 lines', value: 5,
value: 10, },
}, {
{ label: 'First 10 lines',
label: 'All lines', value: 10,
value: 99, },
}, {
]} label: 'All lines',
/> value: 99,
},
]}
selectSizeVariant="small"
/>
</SettingsOptionCardContentSelect>
</> </>
); );
}} }}

View File

@ -6,6 +6,7 @@ import { currencyFieldDefaultValueSchema } from '@/object-record/record-field/va
import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect'; import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect';
import { SETTINGS_FIELD_CURRENCY_CODES } from '@/settings/data-model/constants/SettingsFieldCurrencyCodes'; import { SETTINGS_FIELD_CURRENCY_CODES } from '@/settings/data-model/constants/SettingsFieldCurrencyCodes';
import { useCurrencySettingsFormInitialValues } from '@/settings/data-model/fields/forms/currency/hooks/useCurrencySettingsFormInitialValues'; import { useCurrencySettingsFormInitialValues } from '@/settings/data-model/fields/forms/currency/hooks/useCurrencySettingsFormInitialValues';
import { Select } from '@/ui/input/components/Select';
import { IconCurrencyDollar } from 'twenty-ui'; import { IconCurrencyDollar } from 'twenty-ui';
import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString'; import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString';
@ -57,13 +58,17 @@ export const SettingsDataModelFieldCurrencyForm = ({
Icon={IconCurrencyDollar} Icon={IconCurrencyDollar}
title="Default Value" title="Default Value"
description="Choose the default currency that will apply" description="Choose the default currency that will apply"
value={value} >
onChange={onChange} <Select<string>
disabled={disabled} dropdownWidth={'auto'}
fullWidth value={value}
dropdownId="object-field-default-value-select-currency" onChange={onChange}
options={OPTIONS} disabled={disabled}
/> dropdownId="object-field-default-value-select-currency"
options={OPTIONS}
selectSizeVariant="small"
/>
</SettingsOptionCardContentSelect>
)} )}
/> />
</> </>

View File

@ -5,6 +5,7 @@ import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { numberFieldDefaultValueSchema } from '@/object-record/record-field/validation-schemas/numberFieldDefaultValueSchema'; import { numberFieldDefaultValueSchema } from '@/object-record/record-field/validation-schemas/numberFieldDefaultValueSchema';
import { SettingsOptionCardContentCounter } from '@/settings/components/SettingsOptions/SettingsOptionCardContentCounter'; import { SettingsOptionCardContentCounter } from '@/settings/components/SettingsOptions/SettingsOptionCardContentCounter';
import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect'; import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect';
import { Select } from '@/ui/input/components/Select';
import { IconDecimal, IconEye, IconNumber9, IconPercentage } from 'twenty-ui'; import { IconDecimal, IconEye, IconNumber9, IconPercentage } from 'twenty-ui';
import { DEFAULT_DECIMAL_VALUE } from '~/utils/format/number'; import { DEFAULT_DECIMAL_VALUE } from '~/utils/format/number';
@ -47,25 +48,30 @@ export const SettingsDataModelFieldNumberForm = ({
<> <>
<SettingsOptionCardContentSelect <SettingsOptionCardContentSelect
Icon={IconEye} Icon={IconEye}
dropdownId="number-type"
title="Number type" title="Number type"
description="Display as a plain number or a percentage" description="Display as a plain number or a percentage"
value={type} >
onChange={(value) => onChange({ type: value, decimals: count })} <Select<string>
disabled={disabled} selectSizeVariant="small"
options={[ dropdownId="number-type"
{ dropdownWidth={120}
Icon: IconNumber9, value={type}
label: 'Number', onChange={(value) => onChange({ type: value, decimals: count })}
value: 'number', disabled={disabled}
}, options={[
{ {
Icon: IconPercentage, Icon: IconNumber9,
label: 'Percentage', label: 'Number',
value: 'percentage', value: 'number',
}, },
]} {
/> Icon: IconPercentage,
label: 'Percentage',
value: 'percentage',
},
]}
/>
</SettingsOptionCardContentSelect>
<SettingsOptionCardContentCounter <SettingsOptionCardContentCounter
Icon={IconDecimal} Icon={IconDecimal}
title="Number of decimals" title="Number of decimals"

View File

@ -4,6 +4,7 @@ import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { phonesSchema as phonesFieldDefaultValueSchema } from '@/object-record/record-field/types/guards/isFieldPhonesValue'; import { phonesSchema as phonesFieldDefaultValueSchema } from '@/object-record/record-field/types/guards/isFieldPhonesValue';
import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect'; import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect';
import { useCountries } from '@/ui/input/components/internal/hooks/useCountries'; import { useCountries } from '@/ui/input/components/internal/hooks/useCountries';
import { Select } from '@/ui/input/components/Select';
import { IconMap } from 'twenty-ui'; import { IconMap } from 'twenty-ui';
import { z } from 'zod'; import { z } from 'zod';
import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString'; import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString';
@ -56,23 +57,29 @@ export const SettingsDataModelFieldPhonesForm = ({
control={control} control={control}
render={({ field: { onChange, value } }) => { render={({ field: { onChange, value } }) => {
return ( return (
<SettingsOptionCardContentSelect<string> <SettingsOptionCardContentSelect
Icon={IconMap} Icon={IconMap}
dropdownId="selectDefaultCountryCode"
title="Default Country Code" title="Default Country Code"
description="The default country code for new phone numbers." description="The default country code for new phone numbers."
value={stripSimpleQuotesFromString(value?.primaryPhoneCountryCode)} >
onChange={(newPhoneCountryCode) => <Select<string>
onChange({ dropdownWidth={'auto'}
...value, dropdownId="selectDefaultCountryCode"
primaryPhoneCountryCode: value={stripSimpleQuotesFromString(
applySimpleQuotesToString(newPhoneCountryCode), value?.primaryPhoneCountryCode,
}) )}
} onChange={(newPhoneCountryCode) =>
disabled={disabled} onChange({
options={countries} ...value,
fullWidth={true} primaryPhoneCountryCode:
/> applySimpleQuotesToString(newPhoneCountryCode),
})
}
disabled={disabled}
options={countries}
selectSizeVariant="small"
/>
</SettingsOptionCardContentSelect>
); );
}} }}
/> />