Fix display empty value if boolean instead of false on show page (#4468)

* default value boolean fixed

* fixed creation, fixed updating a value to false

* fixed default value for default value if boolean

* fixed tests

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
brendanlaschke 2024-03-30 11:38:08 +01:00 committed by GitHub
parent 1d351a29b8
commit da8f1b0a66
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 136 additions and 15 deletions

View File

@ -17,7 +17,7 @@ export const useFieldMetadataItem = () => {
const { deleteOneFieldMetadataItem } = useDeleteOneFieldMetadataItem();
const createMetadataField = (
input: Pick<Field, 'label' | 'icon' | 'description'> & {
input: Pick<Field, 'label' | 'icon' | 'description' | 'defaultValue'> & {
defaultValue?: unknown;
objectMetadataId: string;
options?: Omit<FieldMetadataOption, 'id'>[];
@ -26,7 +26,9 @@ export const useFieldMetadataItem = () => {
) => {
const formatedInput = formatFieldMetadataItemInput(input);
const defaultValue = input.defaultValue
? `'${input.defaultValue}'`
? typeof input.defaultValue == 'string'
? `'${input.defaultValue}'`
: input.defaultValue
: formatedInput.defaultValue ?? undefined;
return createOneFieldMetadataItem({
@ -38,14 +40,25 @@ export const useFieldMetadataItem = () => {
};
const editMetadataField = (
input: Pick<Field, 'id' | 'label' | 'icon' | 'description'> & {
input: Pick<
Field,
'id' | 'label' | 'icon' | 'description' | 'defaultValue'
> & {
options?: FieldMetadataOption[];
},
) =>
updateOneFieldMetadataItem({
) => {
const formatedInput = formatFieldMetadataItemInput(input);
const defaultValue = input.defaultValue
? typeof input.defaultValue == 'string'
? `'${input.defaultValue}'`
: input.defaultValue
: formatedInput.defaultValue ?? undefined;
return updateOneFieldMetadataItem({
fieldMetadataIdToUpdate: input.id,
updatePayload: formatFieldMetadataItemInput({
...input,
defaultValue,
// In Edit mode, all options need an id,
// so we generate an id for newly created options.
options: input.options?.map((option) =>
@ -53,6 +66,7 @@ export const useFieldMetadataItem = () => {
),
}),
});
};
const activateMetadataField = (metadataField: FieldMetadataItem) =>
updateOneFieldMetadataItem({

View File

@ -45,7 +45,7 @@ export const formatFieldMetadataItemInput = (
return {
defaultValue: defaultOption
? `'${getOptionValueFromLabel(defaultOption.label)}'`
: undefined,
: input.defaultValue,
description: input.description?.trim() ?? null,
icon: input.icon,
label: input.label.trim(),

View File

@ -0,0 +1,60 @@
import styled from '@emotion/styled';
import { IconCheck, IconX } from '@/ui/display/icon';
import { Select } from '@/ui/input/components/Select';
import { CardContent } from '@/ui/layout/card/components/CardContent';
type SettingsDataModelDefaultValueFormProps = {
className?: string;
disabled?: boolean;
onChange?: (defaultValue: SettingsDataModelDefaultValue) => void;
value?: SettingsDataModelDefaultValue;
};
export type SettingsDataModelDefaultValue = any;
const StyledContainer = styled(CardContent)`
padding-bottom: ${({ theme }) => theme.spacing(3.5)};
`;
const StyledLabel = styled.span`
color: ${({ theme }) => theme.font.color.light};
display: block;
font-size: ${({ theme }) => theme.font.size.xs};
font-weight: ${({ theme }) => theme.font.weight.semiBold};
margin-bottom: 6px;
margin-top: ${({ theme }) => theme.spacing(1)};
`;
export const SettingsDataModelDefaultValueForm = ({
className,
disabled,
onChange,
value,
}: SettingsDataModelDefaultValueFormProps) => {
return (
<StyledContainer>
<StyledLabel>Default Value</StyledLabel>
<Select
className={className}
fullWidth
disabled={disabled}
dropdownId="object-field-default-value-select"
value={value}
onChange={(value) => onChange?.(value)}
options={[
{
value: true,
label: 'True',
Icon: IconCheck,
},
{
value: false,
label: 'False',
Icon: IconX,
},
]}
/>
</StyledContainer>
);
};

View File

@ -2,6 +2,7 @@ import styled from '@emotion/styled';
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { SettingsDataModelDefaultValueForm } from '@/settings/data-model/components/SettingsDataModelDefaultValue';
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
import {
SettingsObjectFieldCurrencyForm,
@ -26,6 +27,7 @@ export type SettingsDataModelFieldSettingsFormValues = {
currency: SettingsObjectFieldCurrencyFormValues;
relation: SettingsObjectFieldRelationFormValues;
select: SettingsObjectFieldSelectFormValues;
defaultValue: any;
};
type SettingsDataModelFieldSettingsFormCardProps = {
@ -152,6 +154,13 @@ export const SettingsDataModelFieldSettingsFormCard = ({
onChange({ select: nextSelectValues })
}
/>
) : fieldMetadataItem.type === FieldMetadataType.Boolean ? (
<SettingsDataModelDefaultValueForm
value={values.defaultValue}
onChange={(nextValueDefaultValue) =>
onChange({ defaultValue: nextValueDefaultValue })
}
/>
) : undefined
}
/>

View File

@ -6,12 +6,19 @@ import {
} from '@/settings/data-model/constants/SettingsFieldTypeConfigs';
import { SettingsSupportedFieldType } from '@/settings/data-model/types/SettingsSupportedFieldType';
import { Select, SelectOption } from '@/ui/input/components/Select';
import { FieldMetadataType } from '~/generated-metadata/graphql';
type SettingsDataModelFieldTypeSelectProps = {
className?: string;
disabled?: boolean;
excludedFieldTypes?: SettingsSupportedFieldType[];
onChange?: ({ type }: { type: SettingsSupportedFieldType }) => void;
onChange?: ({
type,
defaultValue,
}: {
type: SettingsSupportedFieldType;
defaultValue: any;
}) => void;
value?: SettingsSupportedFieldType;
};
@ -41,7 +48,12 @@ export const SettingsDataModelFieldTypeSelect = ({
disabled={disabled}
dropdownId="object-field-type-select"
value={value}
onChange={(value) => onChange?.({ type: value })}
onChange={(value) =>
onChange?.({
type: value,
defaultValue: value === FieldMetadataType.Boolean ? false : undefined,
})
}
options={fieldTypeOptions}
/>
);

View File

@ -26,6 +26,7 @@ const defaultValues = {
currency: fieldMetadataFormDefaultValues.currency,
relation: fieldMetadataFormDefaultValues.relation,
select: fieldMetadataFormDefaultValues.select,
defaultValue: fieldMetadataFormDefaultValues.defaultValue,
};
const meta: Meta<typeof SettingsDataModelFieldSettingsFormCard> = {
@ -110,6 +111,7 @@ export const WithSelectForm: Story = {
},
{ color: 'green', label: '🌏 NGO', value: 'NGO' },
],
defaultValue: undefined,
},
},
};

View File

@ -23,6 +23,7 @@ describe('useFieldMetadataForm', () => {
objectMetadataId: '',
field: { label: '' },
},
defaultValue: null,
select: [
{ color: 'green', label: 'Option 1', value: expect.any(String) },
],

View File

@ -18,6 +18,7 @@ type FormValues = {
description?: string;
icon: string;
label: string;
defaultValue: any;
type: SettingsSupportedFieldType;
} & SettingsDataModelFieldSettingsFormValues;
@ -31,6 +32,7 @@ export const fieldMetadataFormDefaultValues: FormValues = {
objectMetadataId: '',
field: { label: '' },
},
defaultValue: null,
select: [{ color: 'green', label: 'Option 1', value: v4() }],
};
@ -38,6 +40,7 @@ const fieldSchema = z.object({
description: z.string().optional(),
icon: z.string().startsWith('Icon'),
label: z.string().min(1),
defaultValue: z.any(),
});
const currencySchema = fieldSchema.merge(
@ -124,6 +127,8 @@ export const useFieldMetadataForm = () => {
const [hasCurrencyFormChanged, setHasCurrencyFormChanged] = useState(false);
const [hasRelationFormChanged, setHasRelationFormChanged] = useState(false);
const [hasSelectFormChanged, setHasSelectFormChanged] = useState(false);
const [hasDefaultValueChanged, setHasDefaultValueFormChanged] =
useState(false);
const [validationResult, setValidationResult] = useState(
schema.safeParse(formValues),
);
@ -169,12 +174,14 @@ export const useFieldMetadataForm = () => {
currency: initialCurrencyFormValues,
relation: initialRelationFormValues,
select: initialSelectFormValues,
defaultValue: initalDefaultValue,
...initialFieldFormValues
} = initialFormValues;
const {
currency: nextCurrencyFormValues,
relation: nextRelationFormValues,
select: nextSelectFormValues,
defaultValue: nextDefaultValue,
...nextFieldFormValues
} = nextFormValues;
@ -193,6 +200,10 @@ export const useFieldMetadataForm = () => {
nextFieldFormValues.type === FieldMetadataType.Select &&
!isDeeplyEqual(initialSelectFormValues, nextSelectFormValues),
);
setHasDefaultValueFormChanged(
nextFieldFormValues.type === FieldMetadataType.Boolean &&
!isDeeplyEqual(initalDefaultValue, nextDefaultValue),
);
};
return {
@ -203,9 +214,11 @@ export const useFieldMetadataForm = () => {
hasFieldFormChanged ||
hasCurrencyFormChanged ||
hasRelationFormChanged ||
hasSelectFormChanged,
hasSelectFormChanged ||
hasDefaultValueChanged,
hasRelationFormChanged,
hasSelectFormChanged,
hasDefaultValueChanged,
initForm,
isInitialized,
isValid: validationResult.success,

View File

@ -83,6 +83,7 @@ export const SettingsObjectFieldEdit = () => {
formValues,
handleFormChange,
hasFieldFormChanged,
hasDefaultValueChanged,
hasFormChanged,
hasRelationFormChanged,
hasSelectFormChanged,
@ -132,6 +133,7 @@ export const SettingsObjectFieldEdit = () => {
objectMetadataId: relationObjectMetadataItem?.id || '',
type: relationType || RelationMetadataType.OneToMany,
},
defaultValue: activeMetadataField.defaultValue,
...(selectOptions?.length ? { select: selectOptions } : {}),
});
}, [
@ -170,13 +172,17 @@ export const SettingsObjectFieldEdit = () => {
label: validatedFormValues.relation.field.label,
});
}
if (hasFieldFormChanged || hasSelectFormChanged) {
if (
hasFieldFormChanged ||
hasSelectFormChanged ||
hasDefaultValueChanged
) {
await editMetadataField({
description: validatedFormValues.description,
icon: validatedFormValues.icon,
id: activeMetadataField.id,
label: validatedFormValues.label,
defaultValue: validatedFormValues.defaultValue,
options:
validatedFormValues.type === FieldMetadataType.Select
? validatedFormValues.select
@ -255,6 +261,7 @@ export const SettingsObjectFieldEdit = () => {
currency: formValues.currency,
relation: formValues.relation,
select: formValues.select,
defaultValue: formValues.defaultValue,
}}
/>
</Section>

View File

@ -213,7 +213,7 @@ export const SettingsObjectNewFieldStep2 = () => {
amountMicros: null,
currencyCode: validatedFormValues.currency.currencyCode,
}
: undefined,
: validatedFormValues.defaultValue,
description: validatedFormValues.description,
icon: validatedFormValues.icon,
label: validatedFormValues.label ?? '',
@ -320,6 +320,7 @@ export const SettingsObjectNewFieldStep2 = () => {
currency: formValues.currency,
relation: formValues.relation,
select: formValues.select,
defaultValue: formValues.defaultValue,
}}
/>
</Section>

View File

@ -300,10 +300,12 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
await fieldMetadataRepository.update(id, {
...updatableFieldInput,
defaultValue:
// Todo: we need to handle default value for all field types. Right now we are only allowing update for SELECt
existingFieldMetadata.type !== FieldMetadataType.SELECT
// Todo: we handle default value for all field types.
![FieldMetadataType.SELECT, FieldMetadataType.BOOLEAN].includes(
existingFieldMetadata.type,
)
? existingFieldMetadata.defaultValue
: updatableFieldInput.defaultValue
: updatableFieldInput.defaultValue !== null
? updatableFieldInput.defaultValue
: null,
// If the name is updated, the targetColumnMap should be updated as well