Feature : Adding percentage option to Input Number (#8481)
fixing #7375 --------- Co-authored-by: guillim <guillaume@twenty.com>
This commit is contained in:
@ -361,7 +361,6 @@ export const RecordBoardCard = ({
|
||||
metadata: fieldDefinition.metadata,
|
||||
type: fieldDefinition.type,
|
||||
}),
|
||||
settings: fieldDefinition.settings,
|
||||
},
|
||||
useUpdateRecord: useUpdateOneRecordHook,
|
||||
hotkeyScope: InlineCellHotkeyScope.InlineCell,
|
||||
|
||||
@ -1,12 +1,16 @@
|
||||
import { useNumberFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useNumberFieldDisplay';
|
||||
import { NumberDisplay } from '@/ui/field/display/components/NumberDisplay';
|
||||
import { formatNumber } from '~/utils/format/number';
|
||||
|
||||
export const NumberFieldDisplay = () => {
|
||||
const { fieldValue, fieldDefinition } = useNumberFieldDisplay();
|
||||
return (
|
||||
<NumberDisplay
|
||||
value={fieldValue}
|
||||
decimals={fieldDefinition.settings?.decimals}
|
||||
/>
|
||||
);
|
||||
const decimals = fieldDefinition.metadata.settings?.decimals;
|
||||
const type = fieldDefinition.metadata.settings?.type;
|
||||
const value =
|
||||
type === 'percentage' && fieldValue
|
||||
? `${formatNumber(Number(fieldValue) * 100, decimals)}%`
|
||||
: fieldValue
|
||||
? formatNumber(Number(fieldValue), decimals)
|
||||
: null;
|
||||
return <NumberDisplay value={value} decimals={decimals} />;
|
||||
};
|
||||
|
||||
@ -11,6 +11,7 @@ import {
|
||||
castAsNumberOrNull,
|
||||
} from '~/utils/cast-as-number-or-null';
|
||||
|
||||
import { isNull } from '@sniptt/guards';
|
||||
import { FieldContext } from '../../contexts/FieldContext';
|
||||
import { usePersistField } from '../../hooks/usePersistField';
|
||||
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
|
||||
@ -33,12 +34,23 @@ export const useNumberField = () => {
|
||||
const persistField = usePersistField();
|
||||
|
||||
const persistNumberField = (newValue: string) => {
|
||||
if (fieldDefinition?.metadata?.settings?.type === 'percentage') {
|
||||
newValue = newValue.replaceAll('%', '');
|
||||
if (!canBeCastAsNumberOrNull(newValue)) {
|
||||
return;
|
||||
}
|
||||
const castedValue = castAsNumberOrNull(newValue);
|
||||
if (!isNull(castedValue)) {
|
||||
persistField(castedValue / 100);
|
||||
return;
|
||||
}
|
||||
persistField(null);
|
||||
return;
|
||||
}
|
||||
if (!canBeCastAsNumberOrNull(newValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const castedValue = castAsNumberOrNull(newValue);
|
||||
|
||||
persistField(castedValue);
|
||||
};
|
||||
|
||||
|
||||
@ -16,7 +16,4 @@ export type FieldDefinition<T extends FieldMetadata> = {
|
||||
infoTooltipContent?: string;
|
||||
defaultValue?: any;
|
||||
editButtonIcon?: IconComponent;
|
||||
settings?: {
|
||||
decimals?: number;
|
||||
};
|
||||
};
|
||||
|
||||
@ -10,17 +10,20 @@ import { CurrencyCode } from './CurrencyCode';
|
||||
export type FieldUuidMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
fieldName: string;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldBooleanMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
fieldName: string;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldTextMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
placeHolder: string;
|
||||
fieldName: string;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldDateTimeMetadata = {
|
||||
@ -46,17 +49,23 @@ export type FieldNumberMetadata = {
|
||||
fieldName: string;
|
||||
placeHolder: string;
|
||||
isPositive?: boolean;
|
||||
settings?: {
|
||||
decimals?: number;
|
||||
type?: 'percentage' | 'number';
|
||||
};
|
||||
};
|
||||
|
||||
export type FieldLinkMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
placeHolder: string;
|
||||
fieldName: string;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldLinksMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
fieldName: string;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldCurrencyMetadata = {
|
||||
@ -64,56 +73,66 @@ export type FieldCurrencyMetadata = {
|
||||
fieldName: string;
|
||||
placeHolder: string;
|
||||
isPositive?: boolean;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldFullNameMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
placeHolder: string;
|
||||
fieldName: string;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldEmailMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
placeHolder: string;
|
||||
fieldName: string;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldEmailsMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
fieldName: string;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldPhoneMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
placeHolder: string;
|
||||
fieldName: string;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldRatingMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
fieldName: string;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldAddressMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
placeHolder: string;
|
||||
fieldName: string;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldRawJsonMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
fieldName: string;
|
||||
placeHolder: string;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldRichTextMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
fieldName: string;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldPositionMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
fieldName: string;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldRelationMetadata = {
|
||||
@ -125,6 +144,7 @@ export type FieldRelationMetadata = {
|
||||
relationType?: RelationDefinitionType;
|
||||
targetFieldMetadataName?: string;
|
||||
useEditButton?: boolean;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldSelectMetadata = {
|
||||
@ -132,33 +152,39 @@ export type FieldSelectMetadata = {
|
||||
fieldName: string;
|
||||
options: { label: string; color: ThemeColor; value: string }[];
|
||||
isNullable: boolean;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldMultiSelectMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
fieldName: string;
|
||||
options: { label: string; color: ThemeColor; value: string }[];
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldActorMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
fieldName: string;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldArrayMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
fieldName: string;
|
||||
values: { label: string; value: string }[];
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldPhonesMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
fieldName: string;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldTsVectorMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
fieldName: string;
|
||||
settings?: Record<string, never>;
|
||||
};
|
||||
|
||||
export type FieldMetadata =
|
||||
|
||||
@ -3,6 +3,8 @@ import { FieldInputDraftValue } from '@/object-record/record-field/types/FieldIn
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { isFieldCurrency } from '@/object-record/record-field/types/guards/isFieldCurrency';
|
||||
import { isFieldCurrencyValue } from '@/object-record/record-field/types/guards/isFieldCurrencyValue';
|
||||
import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber';
|
||||
import { isFieldNumberValue } from '@/object-record/record-field/types/guards/isFieldNumberValue';
|
||||
import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson';
|
||||
import { isFieldRawJsonValue } from '@/object-record/record-field/types/guards/isFieldRawJsonValue';
|
||||
import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation';
|
||||
@ -12,7 +14,7 @@ import { isDefined } from '~/utils/isDefined';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
type computeDraftValueFromFieldValueParams<FieldValue> = {
|
||||
fieldDefinition: Pick<FieldDefinition<FieldMetadata>, 'type'>;
|
||||
fieldDefinition: Pick<FieldDefinition<FieldMetadata>, 'type' | 'metadata'>;
|
||||
fieldValue: FieldValue;
|
||||
};
|
||||
|
||||
@ -40,6 +42,18 @@ export const computeDraftValueFromFieldValue = <FieldValue>({
|
||||
} as unknown as FieldInputDraftValue<FieldValue>;
|
||||
}
|
||||
|
||||
if (
|
||||
isFieldNumber(fieldDefinition) &&
|
||||
isFieldNumberValue(fieldValue) &&
|
||||
fieldDefinition.metadata.settings?.type === 'percentage'
|
||||
) {
|
||||
return (isUndefinedOrNull(fieldValue)
|
||||
? ''
|
||||
: (
|
||||
fieldValue * 100
|
||||
).toString()) as unknown as FieldInputDraftValue<FieldValue>;
|
||||
}
|
||||
|
||||
if (isFieldRelation(fieldDefinition)) {
|
||||
return computeEmptyDraftValue<FieldValue>({ fieldDefinition });
|
||||
}
|
||||
|
||||
@ -2,4 +2,5 @@ import { z } from 'zod';
|
||||
|
||||
export const numberFieldDefaultValueSchema = z.object({
|
||||
decimals: z.number().nullable(),
|
||||
type: z.enum(['percentage', 'number']).nullable(),
|
||||
});
|
||||
|
||||
@ -304,9 +304,6 @@ describe('useRecordData', () => {
|
||||
},
|
||||
},
|
||||
position: expect.any(Number),
|
||||
settings: {
|
||||
displayAsRelativeDate: true,
|
||||
},
|
||||
showLabel: undefined,
|
||||
size: 100,
|
||||
type: 'DATE_TIME',
|
||||
|
||||
Reference in New Issue
Block a user