fix: settings form select menu (#9179)

Closes: #8647 
Closes: #8649 

**Changes & Why**

1. Added a Search Input to `SettingsDataModelFieldAddressForm` &
`SettingsDataModelFieldCurrencyForm` as `Select` component already
accepts it as a prop.
2. Gave a fixed width to the dropdown of both the above components to
ensure it doesn't shrink on search for the menu items with low word
count.
3. Added countries Flag to `SettingsDataModelFieldAddressForm`. 
4. Replaced `MenuItem` with `MenuItemSelect` to get the desired
highlighted background for the selected item with `IconCheck` to
differentiate the current selected item. This is useful across all the
select components throughout the app.
5. I realized that in some components we might not need IconCheck and
only need a highlighted background for the selected item. For ex:
`SettingsDataModelFieldBooleanForm` . Therefore, I created a prop
`needIconCheck` with default as true so it doesn't break the existing
`MenuItemSelect` and we can pass that prop as false wherever needed.

[Screencast from 2024-12-21
12-08-08.webm](https://github.com/user-attachments/assets/4f8070a8-f339-4556-a137-bbbad58b171c)
This commit is contained in:
Harsh Singh
2024-12-24 16:54:40 +05:30
committed by GitHub
parent 801bf7c016
commit fe6948ba0b
6 changed files with 23 additions and 10 deletions

View File

@ -4,8 +4,8 @@ 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 { Select, SelectOption } from '@/ui/input/components/Select';
import { IconMap } from 'twenty-ui'; import { IconCircleOff, IconComponentProps, 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';
@ -33,13 +33,16 @@ export const SettingsDataModelFieldAddressForm = ({
const { control } = useFormContext<SettingsDataModelFieldTextFormValues>(); const { control } = useFormContext<SettingsDataModelFieldTextFormValues>();
const countries = useCountries() const countries = useCountries()
.sort((a, b) => a.countryName.localeCompare(b.countryName)) .sort((a, b) => a.countryName.localeCompare(b.countryName))
.map((country) => ({ .map<SelectOption<string>>(({ countryName, Flag }) => ({
label: country.countryName, label: countryName,
value: country.countryName, value: countryName,
Icon: (props: IconComponentProps) =>
Flag({ width: props.size, height: props.size }),
})); }));
countries.unshift({ countries.unshift({
label: 'No country', label: 'No country',
value: '', value: '',
Icon: IconCircleOff,
}); });
const defaultDefaultValue = { const defaultDefaultValue = {
addressStreet1: "''", addressStreet1: "''",
@ -69,7 +72,7 @@ export const SettingsDataModelFieldAddressForm = ({
description="The default country for new addresses" description="The default country for new addresses"
> >
<Select<string> <Select<string>
dropdownWidth={'auto'} dropdownWidth={220}
disabled={disabled} disabled={disabled}
dropdownId="selectDefaultCountry" dropdownId="selectDefaultCountry"
value={stripSimpleQuotesFromString(defaultCountry)} value={stripSimpleQuotesFromString(defaultCountry)}
@ -81,6 +84,7 @@ export const SettingsDataModelFieldAddressForm = ({
} }
options={countries} options={countries}
selectSizeVariant="small" selectSizeVariant="small"
withSearchInput={true}
/> />
</SettingsOptionCardContentSelect> </SettingsOptionCardContentSelect>
); );

View File

@ -44,6 +44,7 @@ export const SettingsDataModelFieldBooleanForm = ({
onChange={onChange} onChange={onChange}
dropdownId="object-field-default-value-select-boolean" dropdownId="object-field-default-value-select-boolean"
dropdownWidth={120} dropdownWidth={120}
needIconCheck={false}
options={[ options={[
{ {
value: true, value: true,

View File

@ -60,13 +60,14 @@ export const SettingsDataModelFieldCurrencyForm = ({
description="Choose the default currency that will apply" description="Choose the default currency that will apply"
> >
<Select<string> <Select<string>
dropdownWidth={'auto'} dropdownWidth={220}
value={value} value={value}
onChange={onChange} onChange={onChange}
disabled={disabled} disabled={disabled}
dropdownId="object-field-default-value-select-currency" dropdownId="object-field-default-value-select-currency"
options={OPTIONS} options={OPTIONS}
selectSizeVariant="small" selectSizeVariant="small"
withSearchInput={true}
/> />
</SettingsOptionCardContentSelect> </SettingsOptionCardContentSelect>
)} )}

View File

@ -59,6 +59,7 @@ export const SettingsDataModelFieldNumberForm = ({
value={type} value={type}
onChange={(value) => onChange({ type: value, decimals: count })} onChange={(value) => onChange({ type: value, decimals: count })}
disabled={disabled} disabled={disabled}
needIconCheck={false}
options={[ options={[
{ {
Icon: IconNumber9, Icon: IconNumber9,

View File

@ -1,6 +1,6 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { MouseEvent, useMemo, useRef, useState } from 'react'; import { MouseEvent, useMemo, useRef, useState } from 'react';
import { IconComponent, MenuItem } from 'twenty-ui'; import { IconComponent, MenuItem, MenuItemSelect } from 'twenty-ui';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -43,6 +43,7 @@ export type SelectProps<Value extends SelectValue> = {
options: SelectOption<Value>[]; options: SelectOption<Value>[];
value?: Value; value?: Value;
withSearchInput?: boolean; withSearchInput?: boolean;
needIconCheck?: boolean;
callToActionButton?: CallToActionButton; callToActionButton?: CallToActionButton;
}; };
@ -73,6 +74,7 @@ export const Select = <Value extends SelectValue>({
options, options,
value, value,
withSearchInput, withSearchInput,
needIconCheck,
callToActionButton, callToActionButton,
}: SelectProps<Value>) => { }: SelectProps<Value>) => {
const selectContainerRef = useRef<HTMLDivElement>(null); const selectContainerRef = useRef<HTMLDivElement>(null);
@ -148,10 +150,12 @@ export const Select = <Value extends SelectValue>({
{!!filteredOptions.length && ( {!!filteredOptions.length && (
<DropdownMenuItemsContainer hasMaxHeight> <DropdownMenuItemsContainer hasMaxHeight>
{filteredOptions.map((option) => ( {filteredOptions.map((option) => (
<MenuItem <MenuItemSelect
key={`${option.value}-${option.label}`} key={`${option.value}-${option.label}`}
LeftIcon={option.Icon} LeftIcon={option.Icon}
text={option.label} text={option.label}
selected={selectedOption.value === option.value}
needIconCheck={needIconCheck}
onClick={() => { onClick={() => {
onChange?.(option.value); onChange?.(option.value);
onBlur?.(); onBlur?.();

View File

@ -40,6 +40,7 @@ export const StyledMenuItemSelect = styled(StyledMenuItemBase)<{
type MenuItemSelectProps = { type MenuItemSelectProps = {
LeftIcon?: IconComponent | null | undefined; LeftIcon?: IconComponent | null | undefined;
selected: boolean; selected: boolean;
needIconCheck?: boolean;
text: string; text: string;
className?: string; className?: string;
onClick?: () => void; onClick?: () => void;
@ -52,6 +53,7 @@ export const MenuItemSelect = ({
LeftIcon, LeftIcon,
text, text,
selected, selected,
needIconCheck = true,
className, className,
onClick, onClick,
disabled, disabled,
@ -69,7 +71,7 @@ export const MenuItemSelect = ({
hovered={hovered} hovered={hovered}
> >
<MenuItemLeftContent LeftIcon={LeftIcon} text={text} /> <MenuItemLeftContent LeftIcon={LeftIcon} text={text} />
{selected && <IconCheck size={theme.icon.size.md} />} {selected && needIconCheck && <IconCheck size={theme.icon.size.md} />}
{hasSubMenu && ( {hasSubMenu && (
<IconChevronRight <IconChevronRight
size={theme.icon.size.sm} size={theme.icon.size.sm}