Remove deprecated EMAIL, PHONE, LINK (#7551)
In this PR: - remove deprecated EMAIL, PHONE, LINK field types (except for Zapier package as there is another work ongoing) - remove composite currency filter on currencyCode, actor filter on name and workspaceMember as the UX is not great yet
This commit is contained in:
@ -7,8 +7,6 @@ export const SORTABLE_FIELD_METADATA_TYPES = [
|
||||
FieldMetadataType.Text,
|
||||
FieldMetadataType.Boolean,
|
||||
FieldMetadataType.Select,
|
||||
FieldMetadataType.Phone,
|
||||
FieldMetadataType.Email,
|
||||
FieldMetadataType.Emails,
|
||||
FieldMetadataType.FullName,
|
||||
FieldMetadataType.Rating,
|
||||
|
||||
@ -26,10 +26,8 @@ export const formatFieldMetadataItemsAsFilterDefinitions = ({
|
||||
FieldMetadataType.DateTime,
|
||||
FieldMetadataType.Date,
|
||||
FieldMetadataType.Text,
|
||||
FieldMetadataType.Email,
|
||||
FieldMetadataType.Emails,
|
||||
FieldMetadataType.Number,
|
||||
FieldMetadataType.Link,
|
||||
FieldMetadataType.Links,
|
||||
FieldMetadataType.FullName,
|
||||
FieldMetadataType.Address,
|
||||
@ -68,8 +66,6 @@ export const getFilterTypeFromFieldType = (fieldType: FieldMetadataType) => {
|
||||
return 'DATE_TIME';
|
||||
case FieldMetadataType.Date:
|
||||
return 'DATE';
|
||||
case FieldMetadataType.Link:
|
||||
return 'LINK';
|
||||
case FieldMetadataType.Links:
|
||||
return 'LINKS';
|
||||
case FieldMetadataType.FullName:
|
||||
@ -78,12 +74,8 @@ export const getFilterTypeFromFieldType = (fieldType: FieldMetadataType) => {
|
||||
return 'NUMBER';
|
||||
case FieldMetadataType.Currency:
|
||||
return 'CURRENCY';
|
||||
case FieldMetadataType.Email:
|
||||
return 'EMAIL';
|
||||
case FieldMetadataType.Emails:
|
||||
return 'EMAILS';
|
||||
case FieldMetadataType.Phone:
|
||||
return 'PHONE';
|
||||
case FieldMetadataType.Phones:
|
||||
return 'PHONES';
|
||||
case FieldMetadataType.Relation:
|
||||
|
||||
@ -26,10 +26,8 @@ export const mapFieldMetadataToGraphQLQuery = ({
|
||||
const fieldIsSimpleValue = [
|
||||
FieldMetadataType.Uuid,
|
||||
FieldMetadataType.Text,
|
||||
FieldMetadataType.Phone,
|
||||
FieldMetadataType.DateTime,
|
||||
FieldMetadataType.Date,
|
||||
FieldMetadataType.Email,
|
||||
FieldMetadataType.Number,
|
||||
FieldMetadataType.Boolean,
|
||||
FieldMetadataType.Rating,
|
||||
@ -97,14 +95,6 @@ ${mapObjectMetadataToGraphQLQuery({
|
||||
}`;
|
||||
}
|
||||
|
||||
if (fieldType === FieldMetadataType.Link) {
|
||||
return `${field.name}
|
||||
{
|
||||
label
|
||||
url
|
||||
}`;
|
||||
}
|
||||
|
||||
if (fieldType === FieldMetadataType.Links) {
|
||||
return `${field.name}
|
||||
{
|
||||
|
||||
@ -131,7 +131,6 @@ export const getRecordNodeFromRecord = <T extends ObjectRecord>({
|
||||
},
|
||||
];
|
||||
}
|
||||
case FieldMetadataType.Link:
|
||||
case FieldMetadataType.Links:
|
||||
case FieldMetadataType.Address:
|
||||
case FieldMetadataType.FullName:
|
||||
|
||||
@ -36,7 +36,7 @@ const meta: Meta<typeof MultipleFiltersDropdownButton> = {
|
||||
fieldMetadataId: '2',
|
||||
iconName: 'Icon123',
|
||||
label: 'Email',
|
||||
type: FieldMetadataType.Email,
|
||||
type: FieldMetadataType.Emails,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: '3',
|
||||
|
||||
@ -4,16 +4,13 @@ import { PickLiteral } from '~/types/PickLiteral';
|
||||
export type FilterableFieldType = PickLiteral<
|
||||
FieldType,
|
||||
| 'TEXT'
|
||||
| 'PHONE'
|
||||
| 'PHONES'
|
||||
| 'EMAIL'
|
||||
| 'EMAILS'
|
||||
| 'DATE_TIME'
|
||||
| 'DATE'
|
||||
| 'NUMBER'
|
||||
| 'CURRENCY'
|
||||
| 'FULL_NAME'
|
||||
| 'LINK'
|
||||
| 'LINKS'
|
||||
| 'RELATION'
|
||||
| 'ADDRESS'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
|
||||
import { FilterableFieldType } from '@/object-record/object-filter-dropdown/types/FilterableFieldType';
|
||||
import { FilterDefinition } from '@/object-record/object-filter-dropdown/types/FilterDefinition';
|
||||
import { FilterableFieldType } from '@/object-record/object-filter-dropdown/types/FilterableFieldType';
|
||||
import { getOperandsForFilterDefinition } from '../getOperandsForFilterType';
|
||||
|
||||
describe('getOperandsForFilterType', () => {
|
||||
@ -34,10 +34,8 @@ describe('getOperandsForFilterType', () => {
|
||||
|
||||
const testCases = [
|
||||
['TEXT', [...containsOperands, ...emptyOperands]],
|
||||
['EMAIL', [...containsOperands, ...emptyOperands]],
|
||||
['FULL_NAME', [...containsOperands, ...emptyOperands]],
|
||||
['ADDRESS', [...containsOperands, ...emptyOperands]],
|
||||
['LINK', [...containsOperands, ...emptyOperands]],
|
||||
['LINKS', [...containsOperands, ...emptyOperands]],
|
||||
['ACTOR', [...containsOperands, ...emptyOperands]],
|
||||
['CURRENCY', [...numberOperands, ...emptyOperands]],
|
||||
|
||||
@ -14,12 +14,9 @@ export const getOperandsForFilterDefinition = (
|
||||
|
||||
switch (filterDefinition.type) {
|
||||
case 'TEXT':
|
||||
case 'EMAIL':
|
||||
case 'EMAILS':
|
||||
case 'FULL_NAME':
|
||||
case 'ADDRESS':
|
||||
case 'PHONE':
|
||||
case 'LINK':
|
||||
case 'LINKS':
|
||||
case 'ARRAY':
|
||||
case 'PHONES':
|
||||
|
||||
@ -3,7 +3,6 @@ import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinit
|
||||
import {
|
||||
FieldActorMetadata,
|
||||
FieldFullNameMetadata,
|
||||
FieldLinkMetadata,
|
||||
FieldRatingMetadata,
|
||||
FieldSelectMetadata,
|
||||
FieldTextMetadata
|
||||
@ -67,18 +66,6 @@ export const fullNameFieldDefinition: FieldDefinition<FieldFullNameMetadata> = {
|
||||
},
|
||||
};
|
||||
|
||||
export const linkFieldDefinition: FieldDefinition<FieldLinkMetadata> = {
|
||||
fieldMetadataId,
|
||||
label: 'LinkedIn URL',
|
||||
iconName: 'url',
|
||||
type: FieldMetadataType.Link,
|
||||
defaultValue: { url: '', label: '' },
|
||||
metadata: {
|
||||
fieldName: 'linkedInURL',
|
||||
placeHolder: 'https://linkedin.com/user',
|
||||
},
|
||||
};
|
||||
|
||||
const phonesFieldMetadataItem = mockedPersonObjectMetadataItem.fields?.find(
|
||||
({ name }) => name === 'phones',
|
||||
);
|
||||
|
||||
@ -13,7 +13,6 @@ import { isFieldIdentifierDisplay } from '@/object-record/record-field/meta-type
|
||||
import { isFieldActor } from '@/object-record/record-field/types/guards/isFieldActor';
|
||||
import { isFieldArray } from '@/object-record/record-field/types/guards/isFieldArray';
|
||||
import { isFieldBoolean } from '@/object-record/record-field/types/guards/isFieldBoolean';
|
||||
import { isFieldDisplayedAsPhone } from '@/object-record/record-field/types/guards/isFieldDisplayedAsPhone';
|
||||
import { isFieldEmails } from '@/object-record/record-field/types/guards/isFieldEmails';
|
||||
import { isFieldLinks } from '@/object-record/record-field/types/guards/isFieldLinks';
|
||||
import { isFieldPhones } from '@/object-record/record-field/types/guards/isFieldPhones';
|
||||
@ -27,13 +26,10 @@ import { ChipFieldDisplay } from '../meta-types/display/components/ChipFieldDisp
|
||||
import { CurrencyFieldDisplay } from '../meta-types/display/components/CurrencyFieldDisplay';
|
||||
import { DateFieldDisplay } from '../meta-types/display/components/DateFieldDisplay';
|
||||
import { DateTimeFieldDisplay } from '../meta-types/display/components/DateTimeFieldDisplay';
|
||||
import { EmailFieldDisplay } from '../meta-types/display/components/EmailFieldDisplay';
|
||||
import { FullNameFieldDisplay } from '../meta-types/display/components/FullNameFieldDisplay';
|
||||
import { JsonFieldDisplay } from '../meta-types/display/components/JsonFieldDisplay';
|
||||
import { LinkFieldDisplay } from '../meta-types/display/components/LinkFieldDisplay';
|
||||
import { MultiSelectFieldDisplay } from '../meta-types/display/components/MultiSelectFieldDisplay';
|
||||
import { NumberFieldDisplay } from '../meta-types/display/components/NumberFieldDisplay';
|
||||
import { PhoneFieldDisplay } from '../meta-types/display/components/PhoneFieldDisplay';
|
||||
import { RelationToOneFieldDisplay } from '../meta-types/display/components/RelationToOneFieldDisplay';
|
||||
import { SelectFieldDisplay } from '../meta-types/display/components/SelectFieldDisplay';
|
||||
import { TextFieldDisplay } from '../meta-types/display/components/TextFieldDisplay';
|
||||
@ -42,12 +38,9 @@ import { isFieldAddress } from '../types/guards/isFieldAddress';
|
||||
import { isFieldCurrency } from '../types/guards/isFieldCurrency';
|
||||
import { isFieldDate } from '../types/guards/isFieldDate';
|
||||
import { isFieldDateTime } from '../types/guards/isFieldDateTime';
|
||||
import { isFieldEmail } from '../types/guards/isFieldEmail';
|
||||
import { isFieldFullName } from '../types/guards/isFieldFullName';
|
||||
import { isFieldLink } from '../types/guards/isFieldLink';
|
||||
import { isFieldMultiSelect } from '../types/guards/isFieldMultiSelect';
|
||||
import { isFieldNumber } from '../types/guards/isFieldNumber';
|
||||
import { isFieldPhone } from '../types/guards/isFieldPhone';
|
||||
import { isFieldRawJson } from '../types/guards/isFieldRawJson';
|
||||
import { isFieldSelect } from '../types/guards/isFieldSelect';
|
||||
import { isFieldText } from '../types/guards/isFieldText';
|
||||
@ -67,23 +60,16 @@ export const FieldDisplay = () => {
|
||||
<RelationToOneFieldDisplay />
|
||||
) : isFieldRelationFromManyObjects(fieldDefinition) ? (
|
||||
<RelationFromManyFieldDisplay />
|
||||
) : isFieldPhone(fieldDefinition) ||
|
||||
isFieldDisplayedAsPhone(fieldDefinition) ? (
|
||||
<PhoneFieldDisplay />
|
||||
) : isFieldText(fieldDefinition) ? (
|
||||
<TextFieldDisplay />
|
||||
) : isFieldUuid(fieldDefinition) ? (
|
||||
<UuidFieldDisplay />
|
||||
) : isFieldEmail(fieldDefinition) ? (
|
||||
<EmailFieldDisplay />
|
||||
) : isFieldDateTime(fieldDefinition) ? (
|
||||
<DateTimeFieldDisplay />
|
||||
) : isFieldDate(fieldDefinition) ? (
|
||||
<DateFieldDisplay />
|
||||
) : isFieldNumber(fieldDefinition) ? (
|
||||
<NumberFieldDisplay />
|
||||
) : isFieldLink(fieldDefinition) ? (
|
||||
<LinkFieldDisplay />
|
||||
) : isFieldLinks(fieldDefinition) ? (
|
||||
<LinksFieldDisplay />
|
||||
) : isFieldCurrency(fieldDefinition) ? (
|
||||
|
||||
@ -11,44 +11,37 @@ import { RawJsonFieldInput } from '@/object-record/record-field/meta-types/input
|
||||
import { RelationFromManyFieldInput } from '@/object-record/record-field/meta-types/input/components/RelationFromManyFieldInput';
|
||||
import { SelectFieldInput } from '@/object-record/record-field/meta-types/input/components/SelectFieldInput';
|
||||
import { RecordFieldInputScope } from '@/object-record/record-field/scopes/RecordFieldInputScope';
|
||||
import { isFieldDate } from '@/object-record/record-field/types/guards/isFieldDate';
|
||||
import { isFieldDisplayedAsPhone } from '@/object-record/record-field/types/guards/isFieldDisplayedAsPhone';
|
||||
import { isFieldEmails } from '@/object-record/record-field/types/guards/isFieldEmails';
|
||||
import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName';
|
||||
import { isFieldLinks } from '@/object-record/record-field/types/guards/isFieldLinks';
|
||||
import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/isFieldMultiSelect';
|
||||
import { isFieldPhones } from '@/object-record/record-field/types/guards/isFieldPhones';
|
||||
import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson';
|
||||
import { isFieldRelationFromManyObjects } from '@/object-record/record-field/types/guards/isFieldRelationFromManyObjects';
|
||||
import { isFieldRelationToOneObject } from '@/object-record/record-field/types/guards/isFieldRelationToOneObject';
|
||||
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
|
||||
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
|
||||
|
||||
import { ArrayFieldInput } from '@/object-record/record-field/meta-types/input/components/ArrayFieldInput';
|
||||
import { RichTextFieldInput } from '@/object-record/record-field/meta-types/input/components/RichTextFieldInput';
|
||||
import { isFieldAddress } from '@/object-record/record-field/types/guards/isFieldAddress';
|
||||
import { isFieldArray } from '@/object-record/record-field/types/guards/isFieldArray';
|
||||
import { isFieldBoolean } from '@/object-record/record-field/types/guards/isFieldBoolean';
|
||||
import { isFieldCurrency } from '@/object-record/record-field/types/guards/isFieldCurrency';
|
||||
import { isFieldDate } from '@/object-record/record-field/types/guards/isFieldDate';
|
||||
import { isFieldDateTime } from '@/object-record/record-field/types/guards/isFieldDateTime';
|
||||
import { isFieldEmails } from '@/object-record/record-field/types/guards/isFieldEmails';
|
||||
import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName';
|
||||
import { isFieldLinks } from '@/object-record/record-field/types/guards/isFieldLinks';
|
||||
import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/isFieldMultiSelect';
|
||||
import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber';
|
||||
import { isFieldRating } from '@/object-record/record-field/types/guards/isFieldRating';
|
||||
import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson';
|
||||
import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText';
|
||||
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
|
||||
import { FieldContext } from '../contexts/FieldContext';
|
||||
import { BooleanFieldInput } from '../meta-types/input/components/BooleanFieldInput';
|
||||
import { CurrencyFieldInput } from '../meta-types/input/components/CurrencyFieldInput';
|
||||
import { DateTimeFieldInput } from '../meta-types/input/components/DateTimeFieldInput';
|
||||
import { EmailFieldInput } from '../meta-types/input/components/EmailFieldInput';
|
||||
import { LinkFieldInput } from '../meta-types/input/components/LinkFieldInput';
|
||||
import { NumberFieldInput } from '../meta-types/input/components/NumberFieldInput';
|
||||
import { PhoneFieldInput } from '../meta-types/input/components/PhoneFieldInput';
|
||||
import { RatingFieldInput } from '../meta-types/input/components/RatingFieldInput';
|
||||
import { RelationToOneFieldInput } from '../meta-types/input/components/RelationToOneFieldInput';
|
||||
import { TextFieldInput } from '../meta-types/input/components/TextFieldInput';
|
||||
import { FieldInputEvent } from '../types/FieldInputEvent';
|
||||
import { isFieldAddress } from '../types/guards/isFieldAddress';
|
||||
import { isFieldBoolean } from '../types/guards/isFieldBoolean';
|
||||
import { isFieldCurrency } from '../types/guards/isFieldCurrency';
|
||||
import { isFieldDateTime } from '../types/guards/isFieldDateTime';
|
||||
import { isFieldEmail } from '../types/guards/isFieldEmail';
|
||||
import { isFieldLink } from '../types/guards/isFieldLink';
|
||||
import { isFieldNumber } from '../types/guards/isFieldNumber';
|
||||
import { isFieldPhone } from '../types/guards/isFieldPhone';
|
||||
import { isFieldRating } from '../types/guards/isFieldRating';
|
||||
import { isFieldText } from '../types/guards/isFieldText';
|
||||
|
||||
type FieldInputProps = {
|
||||
@ -84,15 +77,6 @@ export const FieldInput = ({
|
||||
<RelationToOneFieldInput onSubmit={onSubmit} onCancel={onCancel} />
|
||||
) : isFieldRelationFromManyObjects(fieldDefinition) ? (
|
||||
<RelationFromManyFieldInput onSubmit={onSubmit} />
|
||||
) : isFieldPhone(fieldDefinition) ||
|
||||
isFieldDisplayedAsPhone(fieldDefinition) ? (
|
||||
<PhoneFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldPhones(fieldDefinition) ? (
|
||||
<PhonesFieldInput onCancel={onCancel} />
|
||||
) : isFieldText(fieldDefinition) ? (
|
||||
@ -103,14 +87,6 @@ export const FieldInput = ({
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldEmail(fieldDefinition) ? (
|
||||
<EmailFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldEmails(fieldDefinition) ? (
|
||||
<EmailsFieldInput onCancel={onCancel} />
|
||||
) : isFieldFullName(fieldDefinition) ? (
|
||||
@ -145,14 +121,6 @@ export const FieldInput = ({
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldLink(fieldDefinition) ? (
|
||||
<LinkFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldLinks(fieldDefinition) ? (
|
||||
<LinksFieldInput onCancel={onCancel} />
|
||||
) : isFieldCurrency(fieldDefinition) ? (
|
||||
|
||||
@ -35,14 +35,8 @@ import { isFieldCurrency } from '../types/guards/isFieldCurrency';
|
||||
import { isFieldCurrencyValue } from '../types/guards/isFieldCurrencyValue';
|
||||
import { isFieldDateTime } from '../types/guards/isFieldDateTime';
|
||||
import { isFieldDateTimeValue } from '../types/guards/isFieldDateTimeValue';
|
||||
import { isFieldEmail } from '../types/guards/isFieldEmail';
|
||||
import { isFieldEmailValue } from '../types/guards/isFieldEmailValue';
|
||||
import { isFieldLink } from '../types/guards/isFieldLink';
|
||||
import { isFieldLinkValue } from '../types/guards/isFieldLinkValue';
|
||||
import { isFieldNumber } from '../types/guards/isFieldNumber';
|
||||
import { isFieldNumberValue } from '../types/guards/isFieldNumberValue';
|
||||
import { isFieldPhone } from '../types/guards/isFieldPhone';
|
||||
import { isFieldPhoneValue } from '../types/guards/isFieldPhoneValue';
|
||||
import { isFieldRating } from '../types/guards/isFieldRating';
|
||||
import { isFieldRatingValue } from '../types/guards/isFieldRatingValue';
|
||||
import { isFieldText } from '../types/guards/isFieldText';
|
||||
@ -68,9 +62,6 @@ export const usePersistField = () => {
|
||||
const fieldIsText =
|
||||
isFieldText(fieldDefinition) && isFieldTextValue(valueToPersist);
|
||||
|
||||
const fieldIsEmail =
|
||||
isFieldEmail(fieldDefinition) && isFieldEmailValue(valueToPersist);
|
||||
|
||||
const fieldIsEmails =
|
||||
isFieldEmails(fieldDefinition) && isFieldEmailsValue(valueToPersist);
|
||||
|
||||
@ -81,9 +72,6 @@ export const usePersistField = () => {
|
||||
const fieldIsDate =
|
||||
isFieldDate(fieldDefinition) && isFieldDateValue(valueToPersist);
|
||||
|
||||
const fieldIsLink =
|
||||
isFieldLink(fieldDefinition) && isFieldLinkValue(valueToPersist);
|
||||
|
||||
const fieldIsLinks =
|
||||
isFieldLinks(fieldDefinition) && isFieldLinksValue(valueToPersist);
|
||||
|
||||
@ -105,9 +93,6 @@ export const usePersistField = () => {
|
||||
isFieldFullName(fieldDefinition) &&
|
||||
isFieldFullNameValue(valueToPersist);
|
||||
|
||||
const fieldIsPhone =
|
||||
isFieldPhone(fieldDefinition) && isFieldPhoneValue(valueToPersist);
|
||||
|
||||
const fieldIsPhones =
|
||||
isFieldPhones(fieldDefinition) && isFieldPhonesValue(valueToPersist);
|
||||
|
||||
@ -133,15 +118,12 @@ export const usePersistField = () => {
|
||||
fieldIsRelationToOneObject ||
|
||||
fieldIsText ||
|
||||
fieldIsBoolean ||
|
||||
fieldIsEmail ||
|
||||
fieldIsEmails ||
|
||||
fieldIsRating ||
|
||||
fieldIsNumber ||
|
||||
fieldIsDateTime ||
|
||||
fieldIsDate ||
|
||||
fieldIsPhone ||
|
||||
fieldIsPhones ||
|
||||
fieldIsLink ||
|
||||
fieldIsLinks ||
|
||||
fieldIsCurrency ||
|
||||
fieldIsFullName ||
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
import { useEmailFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useEmailFieldDisplay';
|
||||
import { EmailDisplay } from '@/ui/field/display/components/EmailDisplay';
|
||||
|
||||
export const EmailFieldDisplay = () => {
|
||||
const { fieldValue } = useEmailFieldDisplay();
|
||||
|
||||
return <EmailDisplay value={fieldValue} />;
|
||||
};
|
||||
@ -1,8 +0,0 @@
|
||||
import { useLinkFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useLinkFieldDisplay';
|
||||
import { LinkDisplay } from '@/ui/field/display/components/LinkDisplay';
|
||||
|
||||
export const LinkFieldDisplay = () => {
|
||||
const { fieldValue } = useLinkFieldDisplay();
|
||||
|
||||
return <LinkDisplay value={fieldValue} />;
|
||||
};
|
||||
@ -1,8 +0,0 @@
|
||||
import { usePhoneFieldDisplay } from '@/object-record/record-field/meta-types/hooks/usePhoneFieldDisplay';
|
||||
import { PhoneDisplay } from '@/ui/field/display/components/PhoneDisplay';
|
||||
|
||||
export const PhoneFieldDisplay = () => {
|
||||
const { fieldValue } = usePhoneFieldDisplay();
|
||||
|
||||
return <PhoneDisplay value={fieldValue} />;
|
||||
};
|
||||
@ -1,40 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput';
|
||||
import { FieldEmailValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { FieldContext } from '../../contexts/FieldContext';
|
||||
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
|
||||
import { isFieldEmail } from '../../types/guards/isFieldEmail';
|
||||
|
||||
export const useEmailField = () => {
|
||||
const { recordId, fieldDefinition, hotkeyScope } = useContext(FieldContext);
|
||||
|
||||
assertFieldMetadata(FieldMetadataType.Email, isFieldEmail, fieldDefinition);
|
||||
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
|
||||
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||
recordStoreFamilySelector({
|
||||
recordId,
|
||||
fieldName: fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldEmailValue>(`${recordId}-${fieldName}`);
|
||||
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
return {
|
||||
fieldDefinition,
|
||||
draftValue,
|
||||
setDraftValue,
|
||||
fieldValue,
|
||||
setFieldValue,
|
||||
hotkeyScope,
|
||||
};
|
||||
};
|
||||
@ -1,19 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { useRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
|
||||
|
||||
import { FieldContext } from '../../contexts/FieldContext';
|
||||
|
||||
export const useEmailFieldDisplay = () => {
|
||||
const { recordId, fieldDefinition, hotkeyScope } = useContext(FieldContext);
|
||||
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
|
||||
const fieldValue = useRecordFieldValue<string>(recordId, fieldName);
|
||||
|
||||
return {
|
||||
fieldDefinition,
|
||||
fieldValue,
|
||||
hotkeyScope,
|
||||
};
|
||||
};
|
||||
@ -1,53 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput';
|
||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { FieldContext } from '../../contexts/FieldContext';
|
||||
import { usePersistField } from '../../hooks/usePersistField';
|
||||
import { FieldLinkValue } from '../../types/FieldMetadata';
|
||||
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
|
||||
import { isFieldLink } from '../../types/guards/isFieldLink';
|
||||
import { isFieldLinkValue } from '../../types/guards/isFieldLinkValue';
|
||||
|
||||
export const useLinkField = () => {
|
||||
const { recordId, fieldDefinition, hotkeyScope } = useContext(FieldContext);
|
||||
|
||||
assertFieldMetadata(FieldMetadataType.Link, isFieldLink, fieldDefinition);
|
||||
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
|
||||
const [fieldValue, setFieldValue] = useRecoilState<FieldLinkValue>(
|
||||
recordStoreFamilySelector({
|
||||
recordId,
|
||||
fieldName: fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldLinkValue>(`${recordId}-${fieldName}`);
|
||||
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
const persistField = usePersistField();
|
||||
|
||||
const persistLinkField = (newValue: FieldLinkValue) => {
|
||||
if (!isFieldLinkValue(newValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
persistField(newValue);
|
||||
};
|
||||
|
||||
return {
|
||||
fieldDefinition,
|
||||
fieldValue,
|
||||
draftValue,
|
||||
setDraftValue,
|
||||
setFieldValue,
|
||||
hotkeyScope,
|
||||
persistLinkField,
|
||||
};
|
||||
};
|
||||
@ -1,21 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { useRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
|
||||
|
||||
import { FieldContext } from '../../contexts/FieldContext';
|
||||
import { FieldLinkValue } from '../../types/FieldMetadata';
|
||||
|
||||
export const useLinkFieldDisplay = () => {
|
||||
const { recordId, fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
const fieldValue = useRecordFieldValue<FieldLinkValue | undefined>(
|
||||
recordId,
|
||||
fieldName,
|
||||
);
|
||||
|
||||
return {
|
||||
fieldDefinition,
|
||||
fieldValue,
|
||||
};
|
||||
};
|
||||
@ -1,61 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { isPossiblePhoneNumber } from 'libphonenumber-js';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput';
|
||||
import { FieldPhoneValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { isFieldDisplayedAsPhone } from '@/object-record/record-field/types/guards/isFieldDisplayedAsPhone';
|
||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { FieldContext } from '../../contexts/FieldContext';
|
||||
import { usePersistField } from '../../hooks/usePersistField';
|
||||
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
|
||||
import { isFieldPhone } from '../../types/guards/isFieldPhone';
|
||||
|
||||
export const usePhoneField = () => {
|
||||
const { recordId, fieldDefinition, hotkeyScope } = useContext(FieldContext);
|
||||
|
||||
try {
|
||||
// TODO: temporary - remove when 'Phone' field in 'Person' object
|
||||
// is migrated to use FieldMetadataType.Phone as type.
|
||||
assertFieldMetadata(
|
||||
FieldMetadataType.Text,
|
||||
isFieldDisplayedAsPhone,
|
||||
fieldDefinition,
|
||||
);
|
||||
} catch {
|
||||
assertFieldMetadata(FieldMetadataType.Phone, isFieldPhone, fieldDefinition);
|
||||
}
|
||||
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
|
||||
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||
recordStoreFamilySelector({
|
||||
recordId,
|
||||
fieldName: fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const persistField = usePersistField();
|
||||
|
||||
const persistPhoneField = (newPhoneValue: string) => {
|
||||
if (!isPossiblePhoneNumber(newPhoneValue) && newPhoneValue !== '') return;
|
||||
|
||||
persistField(newPhoneValue);
|
||||
};
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldPhoneValue>(`${recordId}-${fieldName}`);
|
||||
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
return {
|
||||
fieldDefinition,
|
||||
fieldValue,
|
||||
setFieldValue,
|
||||
draftValue,
|
||||
setDraftValue,
|
||||
hotkeyScope,
|
||||
persistPhoneField,
|
||||
};
|
||||
};
|
||||
@ -1,19 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { useRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
|
||||
|
||||
import { FieldContext } from '../../contexts/FieldContext';
|
||||
|
||||
export const usePhoneFieldDisplay = () => {
|
||||
const { recordId, fieldDefinition, hotkeyScope } = useContext(FieldContext);
|
||||
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
|
||||
const fieldValue = useRecordFieldValue<string>(recordId, fieldName);
|
||||
|
||||
return {
|
||||
fieldDefinition,
|
||||
fieldValue,
|
||||
hotkeyScope,
|
||||
};
|
||||
};
|
||||
@ -1,72 +0,0 @@
|
||||
import { TextInput } from '@/ui/field/input/components/TextInput';
|
||||
|
||||
import { FieldInputOverlay } from '../../../../../ui/field/input/components/FieldInputOverlay';
|
||||
import { usePersistField } from '../../../hooks/usePersistField';
|
||||
import { useEmailField } from '../../hooks/useEmailField';
|
||||
|
||||
import { FieldInputEvent } from './DateTimeFieldInput';
|
||||
|
||||
export type EmailFieldInputProps = {
|
||||
onClickOutside?: FieldInputEvent;
|
||||
onEnter?: FieldInputEvent;
|
||||
onEscape?: FieldInputEvent;
|
||||
onTab?: FieldInputEvent;
|
||||
onShiftTab?: FieldInputEvent;
|
||||
};
|
||||
|
||||
export const EmailFieldInput = ({
|
||||
onEnter,
|
||||
onEscape,
|
||||
onClickOutside,
|
||||
onTab,
|
||||
onShiftTab,
|
||||
}: EmailFieldInputProps) => {
|
||||
const { fieldDefinition, draftValue, setDraftValue, hotkeyScope } =
|
||||
useEmailField();
|
||||
|
||||
const persistField = usePersistField();
|
||||
|
||||
const handleEnter = (newText: string) => {
|
||||
onEnter?.(() => persistField(newText));
|
||||
};
|
||||
|
||||
const handleEscape = (newText: string) => {
|
||||
onEscape?.(() => persistField(newText));
|
||||
};
|
||||
|
||||
const handleClickOutside = (
|
||||
event: MouseEvent | TouchEvent,
|
||||
newText: string,
|
||||
) => {
|
||||
onClickOutside?.(() => persistField(newText));
|
||||
};
|
||||
|
||||
const handleTab = (newText: string) => {
|
||||
onTab?.(() => persistField(newText));
|
||||
};
|
||||
|
||||
const handleShiftTab = (newText: string) => {
|
||||
onShiftTab?.(() => persistField(newText));
|
||||
};
|
||||
|
||||
const handleChange = (newText: string) => {
|
||||
setDraftValue(newText);
|
||||
};
|
||||
|
||||
return (
|
||||
<FieldInputOverlay>
|
||||
<TextInput
|
||||
placeholder={fieldDefinition.metadata.placeHolder}
|
||||
autoFocus
|
||||
value={draftValue ?? ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onShiftTab={handleShiftTab}
|
||||
onTab={handleTab}
|
||||
hotkeyScope={hotkeyScope}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</FieldInputOverlay>
|
||||
);
|
||||
};
|
||||
@ -1,97 +0,0 @@
|
||||
import { TextInput } from '@/ui/field/input/components/TextInput';
|
||||
|
||||
import { FieldInputOverlay } from '../../../../../ui/field/input/components/FieldInputOverlay';
|
||||
import { useLinkField } from '../../hooks/useLinkField';
|
||||
|
||||
import { FieldInputEvent } from './DateTimeFieldInput';
|
||||
|
||||
type LinkFieldInputProps = {
|
||||
onClickOutside?: FieldInputEvent;
|
||||
onEnter?: FieldInputEvent;
|
||||
onEscape?: FieldInputEvent;
|
||||
onTab?: FieldInputEvent;
|
||||
onShiftTab?: FieldInputEvent;
|
||||
};
|
||||
|
||||
export const LinkFieldInput = ({
|
||||
onEnter,
|
||||
onEscape,
|
||||
onClickOutside,
|
||||
onTab,
|
||||
onShiftTab,
|
||||
}: LinkFieldInputProps) => {
|
||||
const { draftValue, setDraftValue, hotkeyScope, persistLinkField } =
|
||||
useLinkField();
|
||||
|
||||
const handleEnter = (newURL: string) => {
|
||||
onEnter?.(() =>
|
||||
persistLinkField({
|
||||
url: newURL,
|
||||
label: '',
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const handleEscape = (newURL: string) => {
|
||||
onEscape?.(() =>
|
||||
persistLinkField({
|
||||
url: newURL,
|
||||
label: '',
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const handleClickOutside = (
|
||||
event: MouseEvent | TouchEvent,
|
||||
newURL: string,
|
||||
) => {
|
||||
onClickOutside?.(() =>
|
||||
persistLinkField({
|
||||
url: newURL,
|
||||
label: '',
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const handleTab = (newURL: string) => {
|
||||
onTab?.(() =>
|
||||
persistLinkField({
|
||||
url: newURL,
|
||||
label: '',
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const handleShiftTab = (newURL: string) => {
|
||||
onShiftTab?.(() =>
|
||||
persistLinkField({
|
||||
url: newURL,
|
||||
label: '',
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const handleChange = (newURL: string) => {
|
||||
setDraftValue({
|
||||
url: newURL,
|
||||
label: '',
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<FieldInputOverlay>
|
||||
<TextInput
|
||||
value={draftValue?.url ?? ''}
|
||||
autoFocus
|
||||
placeholder="URL"
|
||||
hotkeyScope={hotkeyScope}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onTab={handleTab}
|
||||
onShiftTab={handleShiftTab}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</FieldInputOverlay>
|
||||
);
|
||||
};
|
||||
@ -1,74 +0,0 @@
|
||||
import { PhoneInput } from '@/ui/field/input/components/PhoneInput';
|
||||
|
||||
import { FieldInputOverlay } from '../../../../../ui/field/input/components/FieldInputOverlay';
|
||||
import { usePhoneField } from '../../hooks/usePhoneField';
|
||||
|
||||
import { FieldInputEvent } from './DateTimeFieldInput';
|
||||
|
||||
export type PhoneFieldInputProps = {
|
||||
onClickOutside?: FieldInputEvent;
|
||||
onEnter?: FieldInputEvent;
|
||||
onEscape?: FieldInputEvent;
|
||||
onTab?: FieldInputEvent;
|
||||
onShiftTab?: FieldInputEvent;
|
||||
};
|
||||
|
||||
export const PhoneFieldInput = ({
|
||||
onEnter,
|
||||
onEscape,
|
||||
onClickOutside,
|
||||
onTab,
|
||||
onShiftTab,
|
||||
}: PhoneFieldInputProps) => {
|
||||
const {
|
||||
fieldDefinition,
|
||||
draftValue,
|
||||
setDraftValue,
|
||||
hotkeyScope,
|
||||
persistPhoneField,
|
||||
} = usePhoneField();
|
||||
|
||||
const handleEnter = (newText: string) => {
|
||||
onEnter?.(() => persistPhoneField(newText));
|
||||
};
|
||||
|
||||
const handleEscape = (newText: string) => {
|
||||
onEscape?.(() => persistPhoneField(newText));
|
||||
};
|
||||
|
||||
const handleClickOutside = (
|
||||
event: MouseEvent | TouchEvent,
|
||||
newText: string,
|
||||
) => {
|
||||
onClickOutside?.(() => persistPhoneField(newText));
|
||||
};
|
||||
|
||||
const handleTab = (newText: string) => {
|
||||
onTab?.(() => persistPhoneField(newText));
|
||||
};
|
||||
|
||||
const handleShiftTab = (newText: string) => {
|
||||
onShiftTab?.(() => persistPhoneField(newText));
|
||||
};
|
||||
|
||||
const handleChange = (newText: string) => {
|
||||
setDraftValue(newText);
|
||||
};
|
||||
|
||||
return (
|
||||
<FieldInputOverlay>
|
||||
<PhoneInput
|
||||
placeholder={fieldDefinition.metadata.placeHolder}
|
||||
autoFocus
|
||||
value={draftValue ?? ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onShiftTab={handleShiftTab}
|
||||
onTab={handleTab}
|
||||
hotkeyScope={hotkeyScope}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</FieldInputOverlay>
|
||||
);
|
||||
};
|
||||
@ -1,177 +0,0 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Decorator, Meta, StoryObj } from '@storybook/react';
|
||||
import { expect, fn, userEvent, waitFor, within } from '@storybook/test';
|
||||
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { FieldMetadataType } from '~/generated/graphql';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
|
||||
import { FieldContextProvider } from '../../../__stories__/FieldContextProvider';
|
||||
import { useEmailField } from '../../../hooks/useEmailField';
|
||||
import { EmailFieldInput, EmailFieldInputProps } from '../EmailFieldInput';
|
||||
|
||||
const EmailFieldValueSetterEffect = ({ value }: { value: string }) => {
|
||||
const { setFieldValue } = useEmailField();
|
||||
|
||||
useEffect(() => {
|
||||
setFieldValue(value);
|
||||
}, [setFieldValue, value]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
type EmailFieldInputWithContextProps = EmailFieldInputProps & {
|
||||
value: string;
|
||||
recordId?: string;
|
||||
};
|
||||
|
||||
const EmailFieldInputWithContext = ({
|
||||
recordId,
|
||||
value,
|
||||
onEnter,
|
||||
onEscape,
|
||||
onClickOutside,
|
||||
onTab,
|
||||
onShiftTab,
|
||||
}: EmailFieldInputWithContextProps) => {
|
||||
const setHotKeyScope = useSetHotkeyScope();
|
||||
|
||||
useEffect(() => {
|
||||
setHotKeyScope('hotkey-scope');
|
||||
}, [setHotKeyScope]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FieldContextProvider
|
||||
fieldDefinition={{
|
||||
fieldMetadataId: 'email',
|
||||
label: 'Email',
|
||||
type: FieldMetadataType.Email,
|
||||
iconName: 'IconLink',
|
||||
metadata: {
|
||||
fieldName: 'email',
|
||||
placeHolder: 'username@email.com',
|
||||
objectMetadataNameSingular: 'person',
|
||||
},
|
||||
}}
|
||||
recordId={recordId}
|
||||
>
|
||||
<EmailFieldValueSetterEffect value={value} />
|
||||
<EmailFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
</FieldContextProvider>
|
||||
<div data-testid="data-field-input-click-outside-div" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const enterJestFn = fn();
|
||||
const escapeJestfn = fn();
|
||||
const clickOutsideJestFn = fn();
|
||||
const tabJestFn = fn();
|
||||
const shiftTabJestFn = fn();
|
||||
|
||||
const clearMocksDecorator: Decorator = (Story, context) => {
|
||||
if (context.parameters.clearMocks === true) {
|
||||
enterJestFn.mockClear();
|
||||
escapeJestfn.mockClear();
|
||||
clickOutsideJestFn.mockClear();
|
||||
tabJestFn.mockClear();
|
||||
shiftTabJestFn.mockClear();
|
||||
}
|
||||
return <Story />;
|
||||
};
|
||||
|
||||
const meta: Meta = {
|
||||
title: 'UI/Data/Field/Input/EmailFieldInput',
|
||||
component: EmailFieldInputWithContext,
|
||||
args: {
|
||||
value: 'username@email.com',
|
||||
onEnter: enterJestFn,
|
||||
onEscape: escapeJestfn,
|
||||
onClickOutside: clickOutsideJestFn,
|
||||
onTab: tabJestFn,
|
||||
onShiftTab: shiftTabJestFn,
|
||||
},
|
||||
argTypes: {
|
||||
onEnter: { control: false },
|
||||
onEscape: { control: false },
|
||||
onClickOutside: { control: false },
|
||||
onTab: { control: false },
|
||||
onShiftTab: { control: false },
|
||||
},
|
||||
decorators: [clearMocksDecorator, SnackBarDecorator],
|
||||
parameters: {
|
||||
clearMocks: true,
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof EmailFieldInputWithContext>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
||||
export const Enter: Story = {
|
||||
play: async () => {
|
||||
expect(enterJestFn).toHaveBeenCalledTimes(0);
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.keyboard('{enter}');
|
||||
expect(enterJestFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const Escape: Story = {
|
||||
play: async () => {
|
||||
expect(escapeJestfn).toHaveBeenCalledTimes(0);
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.keyboard('{esc}');
|
||||
expect(escapeJestfn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const ClickOutside: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
expect(clickOutsideJestFn).toHaveBeenCalledTimes(0);
|
||||
|
||||
const emptyDiv = canvas.getByTestId('data-field-input-click-outside-div');
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.click(emptyDiv);
|
||||
expect(clickOutsideJestFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const Tab: Story = {
|
||||
play: async () => {
|
||||
expect(tabJestFn).toHaveBeenCalledTimes(0);
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.keyboard('{tab}');
|
||||
expect(tabJestFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const ShiftTab: Story = {
|
||||
play: async () => {
|
||||
expect(shiftTabJestFn).toHaveBeenCalledTimes(0);
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.keyboard('{shift>}{tab}');
|
||||
expect(shiftTabJestFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
};
|
||||
@ -1,178 +0,0 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Decorator, Meta, StoryObj } from '@storybook/react';
|
||||
import { expect, fn, userEvent, waitFor, within } from '@storybook/test';
|
||||
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { FieldMetadataType } from '~/generated/graphql';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
|
||||
import { FieldContextProvider } from '../../../__stories__/FieldContextProvider';
|
||||
import { usePhoneField } from '../../../hooks/usePhoneField';
|
||||
import { PhoneFieldInput, PhoneFieldInputProps } from '../PhoneFieldInput';
|
||||
|
||||
const PhoneFieldValueSetterEffect = ({ value }: { value: string }) => {
|
||||
const { setFieldValue } = usePhoneField();
|
||||
|
||||
useEffect(() => {
|
||||
setFieldValue(value);
|
||||
}, [setFieldValue, value]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
type PhoneFieldInputWithContextProps = PhoneFieldInputProps & {
|
||||
value: string;
|
||||
recordId?: string;
|
||||
};
|
||||
|
||||
const PhoneFieldInputWithContext = ({
|
||||
recordId,
|
||||
value,
|
||||
onEnter,
|
||||
onEscape,
|
||||
onClickOutside,
|
||||
onTab,
|
||||
onShiftTab,
|
||||
}: PhoneFieldInputWithContextProps) => {
|
||||
const setHotKeyScope = useSetHotkeyScope();
|
||||
|
||||
useEffect(() => {
|
||||
setHotKeyScope('hotkey-scope');
|
||||
}, [setHotKeyScope]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FieldContextProvider
|
||||
fieldDefinition={{
|
||||
fieldMetadataId: 'phone',
|
||||
label: 'Phone',
|
||||
type: FieldMetadataType.Phone,
|
||||
iconName: 'IconPhone',
|
||||
metadata: {
|
||||
fieldName: 'phone',
|
||||
placeHolder: 'Enter phone number',
|
||||
objectMetadataNameSingular: 'person',
|
||||
},
|
||||
}}
|
||||
recordId={recordId}
|
||||
>
|
||||
<PhoneFieldValueSetterEffect value={value} />
|
||||
<PhoneFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
</FieldContextProvider>
|
||||
<div data-testid="data-field-input-click-outside-div" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const enterJestFn = fn();
|
||||
const escapeJestfn = fn();
|
||||
const clickOutsideJestFn = fn();
|
||||
const tabJestFn = fn();
|
||||
const shiftTabJestFn = fn();
|
||||
|
||||
const clearMocksDecorator: Decorator = (Story, context) => {
|
||||
if (context.parameters.clearMocks === true) {
|
||||
enterJestFn.mockClear();
|
||||
escapeJestfn.mockClear();
|
||||
clickOutsideJestFn.mockClear();
|
||||
tabJestFn.mockClear();
|
||||
shiftTabJestFn.mockClear();
|
||||
}
|
||||
return <Story />;
|
||||
};
|
||||
|
||||
const meta: Meta = {
|
||||
title: 'UI/Data/Field/Input/PhoneFieldInput',
|
||||
component: PhoneFieldInputWithContext,
|
||||
args: {
|
||||
value: '+1-12-123-456',
|
||||
isPositive: true,
|
||||
onEnter: enterJestFn,
|
||||
onEscape: escapeJestfn,
|
||||
onClickOutside: clickOutsideJestFn,
|
||||
onTab: tabJestFn,
|
||||
onShiftTab: shiftTabJestFn,
|
||||
},
|
||||
argTypes: {
|
||||
onEnter: { control: false },
|
||||
onEscape: { control: false },
|
||||
onClickOutside: { control: false },
|
||||
onTab: { control: false },
|
||||
onShiftTab: { control: false },
|
||||
},
|
||||
decorators: [clearMocksDecorator, SnackBarDecorator],
|
||||
parameters: {
|
||||
clearMocks: true,
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof PhoneFieldInputWithContext>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
||||
export const Enter: Story = {
|
||||
play: async () => {
|
||||
expect(enterJestFn).toHaveBeenCalledTimes(0);
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.keyboard('{enter}');
|
||||
expect(enterJestFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const Escape: Story = {
|
||||
play: async () => {
|
||||
expect(escapeJestfn).toHaveBeenCalledTimes(0);
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.keyboard('{esc}');
|
||||
expect(escapeJestfn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const ClickOutside: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
expect(clickOutsideJestFn).toHaveBeenCalledTimes(0);
|
||||
|
||||
const emptyDiv = canvas.getByTestId('data-field-input-click-outside-div');
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.click(emptyDiv);
|
||||
expect(clickOutsideJestFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const Tab: Story = {
|
||||
play: async () => {
|
||||
expect(tabJestFn).toHaveBeenCalledTimes(0);
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.keyboard('{tab}');
|
||||
expect(tabJestFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const ShiftTab: Story = {
|
||||
play: async () => {
|
||||
expect(shiftTabJestFn).toHaveBeenCalledTimes(0);
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.keyboard('{shift>}{tab}');
|
||||
expect(shiftTabJestFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
};
|
||||
@ -6,15 +6,12 @@ import {
|
||||
FieldCurrencyValue,
|
||||
FieldDateTimeValue,
|
||||
FieldEmailsValue,
|
||||
FieldEmailValue,
|
||||
FieldFullNameValue,
|
||||
FieldJsonValue,
|
||||
FieldLinksValue,
|
||||
FieldLinkValue,
|
||||
FieldMultiSelectValue,
|
||||
FieldNumberValue,
|
||||
FieldPhonesValue,
|
||||
FieldPhoneValue,
|
||||
FieldRatingValue,
|
||||
FieldRelationFromManyValue,
|
||||
FieldRelationToOneValue,
|
||||
@ -27,13 +24,11 @@ import {
|
||||
export type FieldTextDraftValue = string;
|
||||
export type FieldNumberDraftValue = number;
|
||||
export type FieldDateTimeDraftValue = string;
|
||||
export type FieldPhoneDraftValue = string;
|
||||
export type FieldPhonesDraftValue = {
|
||||
primaryPhoneNumber: string;
|
||||
primaryPhoneCountryCode: string;
|
||||
additionalPhones?: PhoneRecord[] | null;
|
||||
};
|
||||
export type FieldEmailDraftValue = string;
|
||||
export type FieldEmailsDraftValue = {
|
||||
primaryEmail: string;
|
||||
additionalEmails: string[] | null;
|
||||
@ -42,7 +37,6 @@ export type FieldSelectDraftValue = string;
|
||||
export type FieldMultiSelectDraftValue = string[];
|
||||
export type FieldRelationDraftValue = string;
|
||||
export type FieldRelationManyDraftValue = string[];
|
||||
export type FieldLinkDraftValue = { url: string; label: string };
|
||||
export type FieldLinksDraftValue = {
|
||||
primaryLinkLabel: string;
|
||||
primaryLinkUrl: string;
|
||||
@ -80,36 +74,30 @@ export type FieldInputDraftValue<FieldValue> = FieldValue extends FieldTextValue
|
||||
? FieldNumberDraftValue
|
||||
: FieldValue extends FieldBooleanValue
|
||||
? FieldBooleanValue
|
||||
: FieldValue extends FieldPhoneValue
|
||||
? FieldPhoneDraftValue
|
||||
: FieldValue extends FieldPhonesValue
|
||||
? FieldPhonesDraftValue
|
||||
: FieldValue extends FieldEmailValue
|
||||
? FieldEmailDraftValue
|
||||
: FieldValue extends FieldEmailsValue
|
||||
? FieldEmailsDraftValue
|
||||
: FieldValue extends FieldLinkValue
|
||||
? FieldLinkDraftValue
|
||||
: FieldValue extends FieldLinksValue
|
||||
? FieldLinksDraftValue
|
||||
: FieldValue extends FieldCurrencyValue
|
||||
? FieldCurrencyDraftValue
|
||||
: FieldValue extends FieldFullNameValue
|
||||
? FieldFullNameDraftValue
|
||||
: FieldValue extends FieldRatingValue
|
||||
? FieldRatingValue
|
||||
: FieldValue extends FieldSelectValue
|
||||
? FieldSelectDraftValue
|
||||
: FieldValue extends FieldMultiSelectValue
|
||||
? FieldMultiSelectDraftValue
|
||||
: FieldValue extends FieldRelationToOneValue
|
||||
? FieldRelationDraftValue
|
||||
: FieldValue extends FieldRelationFromManyValue
|
||||
? FieldRelationManyDraftValue
|
||||
: FieldValue extends FieldAddressValue
|
||||
? FieldAddressDraftValue
|
||||
: FieldValue extends FieldJsonValue
|
||||
? FieldJsonDraftValue
|
||||
: FieldValue extends FieldActorValue
|
||||
? FieldActorDraftValue
|
||||
: never;
|
||||
: FieldValue extends FieldPhonesValue
|
||||
? FieldPhonesDraftValue
|
||||
: FieldValue extends FieldEmailsValue
|
||||
? FieldEmailsDraftValue
|
||||
: FieldValue extends FieldLinksValue
|
||||
? FieldLinksDraftValue
|
||||
: FieldValue extends FieldCurrencyValue
|
||||
? FieldCurrencyDraftValue
|
||||
: FieldValue extends FieldFullNameValue
|
||||
? FieldFullNameDraftValue
|
||||
: FieldValue extends FieldRatingValue
|
||||
? FieldRatingValue
|
||||
: FieldValue extends FieldSelectValue
|
||||
? FieldSelectDraftValue
|
||||
: FieldValue extends FieldMultiSelectValue
|
||||
? FieldMultiSelectDraftValue
|
||||
: FieldValue extends FieldRelationToOneValue
|
||||
? FieldRelationDraftValue
|
||||
: FieldValue extends FieldRelationFromManyValue
|
||||
? FieldRelationManyDraftValue
|
||||
: FieldValue extends FieldAddressValue
|
||||
? FieldAddressDraftValue
|
||||
: FieldValue extends FieldJsonValue
|
||||
? FieldJsonDraftValue
|
||||
: FieldValue extends FieldActorValue
|
||||
? FieldActorDraftValue
|
||||
: never;
|
||||
|
||||
@ -183,13 +183,10 @@ export type FieldDateValue = string | null;
|
||||
export type FieldNumberValue = number | null;
|
||||
export type FieldBooleanValue = boolean;
|
||||
|
||||
export type FieldPhoneValue = string;
|
||||
export type FieldEmailValue = string;
|
||||
export type FieldEmailsValue = {
|
||||
primaryEmail: string;
|
||||
additionalEmails: string[] | null;
|
||||
};
|
||||
export type FieldLinkValue = { url: string; label: string };
|
||||
export type FieldLinksValue = {
|
||||
primaryLinkLabel: string;
|
||||
primaryLinkUrl: string;
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { FieldDefinition } from '../FieldDefinition';
|
||||
import { FieldEmailMetadata, FieldMetadata } from '../FieldMetadata';
|
||||
|
||||
export const isFieldEmail = (
|
||||
field: Pick<FieldDefinition<FieldMetadata>, 'type'>,
|
||||
): field is FieldDefinition<FieldEmailMetadata> =>
|
||||
field.type === FieldMetadataType.Email;
|
||||
@ -1,8 +0,0 @@
|
||||
import { isString } from '@sniptt/guards';
|
||||
|
||||
import { FieldEmailValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add zod
|
||||
export const isFieldEmailValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldEmailValue => isString(fieldValue);
|
||||
@ -1,9 +0,0 @@
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { FieldDefinition } from '../FieldDefinition';
|
||||
import { FieldLinkMetadata, FieldMetadata } from '../FieldMetadata';
|
||||
|
||||
export const isFieldLink = (
|
||||
field: Pick<FieldDefinition<FieldMetadata>, 'type'>,
|
||||
): field is FieldDefinition<FieldLinkMetadata> =>
|
||||
field.type === FieldMetadataType.Link;
|
||||
@ -1,12 +0,0 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { FieldLinkValue } from '../FieldMetadata';
|
||||
|
||||
const linkSchema = z.object({
|
||||
url: z.string(),
|
||||
label: z.string(),
|
||||
});
|
||||
|
||||
export const isFieldLinkValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldLinkValue => linkSchema.safeParse(fieldValue).success;
|
||||
@ -1,9 +0,0 @@
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { FieldDefinition } from '../FieldDefinition';
|
||||
import { FieldMetadata, FieldPhoneMetadata } from '../FieldMetadata';
|
||||
|
||||
export const isFieldPhone = (
|
||||
field: Pick<FieldDefinition<FieldMetadata>, 'type'>,
|
||||
): field is FieldDefinition<FieldPhoneMetadata> =>
|
||||
field.type === FieldMetadataType.Phone;
|
||||
@ -1,8 +0,0 @@
|
||||
import { isString } from '@sniptt/guards';
|
||||
|
||||
import { FieldPhoneValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add zod
|
||||
export const isFieldPhoneValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldPhoneValue => isString(fieldValue);
|
||||
@ -2,7 +2,6 @@ import {
|
||||
booleanFieldDefinition,
|
||||
fieldMetadataId,
|
||||
fullNameFieldDefinition,
|
||||
linkFieldDefinition,
|
||||
relationFieldDefinition,
|
||||
selectFieldDefinition,
|
||||
} from '@/object-record/record-field/__mocks__/fieldDefinitions';
|
||||
@ -101,19 +100,4 @@ describe('isFieldValueEmpty', () => {
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('should return correct value for link field', () => {
|
||||
expect(
|
||||
isFieldValueEmpty({
|
||||
fieldDefinition: linkFieldDefinition,
|
||||
fieldValue: { url: '', label: '' },
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isFieldValueEmpty({
|
||||
fieldDefinition: linkFieldDefinition,
|
||||
fieldValue: { url: 'https://linkedin.com/user-slug', label: '' },
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@ -5,10 +5,8 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
|
||||
import { isFieldAddress } from '@/object-record/record-field/types/guards/isFieldAddress';
|
||||
import { isFieldCurrency } from '@/object-record/record-field/types/guards/isFieldCurrency';
|
||||
import { isFieldDateTime } from '@/object-record/record-field/types/guards/isFieldDateTime';
|
||||
import { isFieldEmail } from '@/object-record/record-field/types/guards/isFieldEmail';
|
||||
import { isFieldEmails } from '@/object-record/record-field/types/guards/isFieldEmails';
|
||||
import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName';
|
||||
import { isFieldLink } from '@/object-record/record-field/types/guards/isFieldLink';
|
||||
import { isFieldLinks } from '@/object-record/record-field/types/guards/isFieldLinks';
|
||||
import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber';
|
||||
import { isFieldPhones } from '@/object-record/record-field/types/guards/isFieldPhones';
|
||||
@ -33,14 +31,10 @@ export const computeDraftValueFromString = <FieldValue>({
|
||||
isFieldText(fieldDefinition) ||
|
||||
isFieldDateTime(fieldDefinition) ||
|
||||
isFieldNumber(fieldDefinition) ||
|
||||
isFieldEmail(fieldDefinition) ||
|
||||
isFieldRelation(fieldDefinition)
|
||||
) {
|
||||
return value as FieldInputDraftValue<FieldValue>;
|
||||
}
|
||||
if (isFieldLink(fieldDefinition)) {
|
||||
return { url: value, label: value } as FieldInputDraftValue<FieldValue>;
|
||||
}
|
||||
|
||||
if (isFieldCurrency(fieldDefinition)) {
|
||||
return {
|
||||
|
||||
@ -5,7 +5,6 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
|
||||
import { isFieldAddress } from '@/object-record/record-field/types/guards/isFieldAddress';
|
||||
import { isFieldCurrency } from '@/object-record/record-field/types/guards/isFieldCurrency';
|
||||
import { isFieldDateTime } from '@/object-record/record-field/types/guards/isFieldDateTime';
|
||||
import { isFieldEmail } from '@/object-record/record-field/types/guards/isFieldEmail';
|
||||
import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName';
|
||||
import { isFieldLinks } from '@/object-record/record-field/types/guards/isFieldLinks';
|
||||
import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber';
|
||||
@ -27,7 +26,6 @@ export const computeEmptyDraftValue = <FieldValue>({
|
||||
isFieldText(fieldDefinition) ||
|
||||
isFieldDateTime(fieldDefinition) ||
|
||||
isFieldNumber(fieldDefinition) ||
|
||||
isFieldEmail(fieldDefinition) ||
|
||||
isFieldRelation(fieldDefinition) ||
|
||||
isFieldRawJson(fieldDefinition)
|
||||
) {
|
||||
|
||||
@ -11,9 +11,6 @@ import { isFieldRelation } from '@/object-record/record-field/types/guards/isFie
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
import { isFieldArray } from '@/object-record/record-field/types/guards/isFieldArray';
|
||||
import { isFieldEmail } from '../types/guards/isFieldEmail';
|
||||
import { isFieldLink } from '../types/guards/isFieldLink';
|
||||
import { isFieldPhone } from '../types/guards/isFieldPhone';
|
||||
|
||||
export const getFieldButtonIcon = (
|
||||
fieldDefinition:
|
||||
@ -24,9 +21,6 @@ export const getFieldButtonIcon = (
|
||||
if (isUndefinedOrNull(fieldDefinition)) return undefined;
|
||||
|
||||
if (
|
||||
isFieldLink(fieldDefinition) ||
|
||||
isFieldEmail(fieldDefinition) ||
|
||||
isFieldPhone(fieldDefinition) ||
|
||||
isFieldDisplayedAsPhone(fieldDefinition) ||
|
||||
isFieldMultiSelect(fieldDefinition) ||
|
||||
(isFieldRelation(fieldDefinition) &&
|
||||
|
||||
@ -13,19 +13,15 @@ import { isFieldCurrency } from '@/object-record/record-field/types/guards/isFie
|
||||
import { isFieldCurrencyValue } from '@/object-record/record-field/types/guards/isFieldCurrencyValue';
|
||||
import { isFieldDate } from '@/object-record/record-field/types/guards/isFieldDate';
|
||||
import { isFieldDateTime } from '@/object-record/record-field/types/guards/isFieldDateTime';
|
||||
import { isFieldEmail } from '@/object-record/record-field/types/guards/isFieldEmail';
|
||||
import { isFieldEmails } from '@/object-record/record-field/types/guards/isFieldEmails';
|
||||
import { isFieldEmailsValue } from '@/object-record/record-field/types/guards/isFieldEmailsValue';
|
||||
import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName';
|
||||
import { isFieldFullNameValue } from '@/object-record/record-field/types/guards/isFieldFullNameValue';
|
||||
import { isFieldLink } from '@/object-record/record-field/types/guards/isFieldLink';
|
||||
import { isFieldLinkValue } from '@/object-record/record-field/types/guards/isFieldLinkValue';
|
||||
import { isFieldLinks } from '@/object-record/record-field/types/guards/isFieldLinks';
|
||||
import { isFieldLinksValue } from '@/object-record/record-field/types/guards/isFieldLinksValue';
|
||||
import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/isFieldMultiSelect';
|
||||
import { isFieldMultiSelectValue } from '@/object-record/record-field/types/guards/isFieldMultiSelectValue';
|
||||
import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber';
|
||||
import { isFieldPhone } from '@/object-record/record-field/types/guards/isFieldPhone';
|
||||
import { isFieldPhones } from '@/object-record/record-field/types/guards/isFieldPhones';
|
||||
import { isFieldPhonesValue } from '@/object-record/record-field/types/guards/isFieldPhonesValue';
|
||||
import { isFieldPosition } from '@/object-record/record-field/types/guards/isFieldPosition';
|
||||
@ -60,12 +56,10 @@ export const isFieldValueEmpty = ({
|
||||
isFieldDate(fieldDefinition) ||
|
||||
isFieldNumber(fieldDefinition) ||
|
||||
isFieldRating(fieldDefinition) ||
|
||||
isFieldEmail(fieldDefinition) ||
|
||||
isFieldBoolean(fieldDefinition) ||
|
||||
isFieldRelation(fieldDefinition) ||
|
||||
isFieldRawJson(fieldDefinition) ||
|
||||
isFieldRichText(fieldDefinition) ||
|
||||
isFieldPhone(fieldDefinition) ||
|
||||
isFieldPosition(fieldDefinition)
|
||||
) {
|
||||
return isValueEmpty(fieldValue);
|
||||
@ -101,10 +95,6 @@ export const isFieldValueEmpty = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (isFieldLink(fieldDefinition)) {
|
||||
return !isFieldLinkValue(fieldValue) || isValueEmpty(fieldValue?.url);
|
||||
}
|
||||
|
||||
if (isFieldAddress(fieldDefinition)) {
|
||||
return (
|
||||
!isFieldAddressValue(fieldValue) ||
|
||||
|
||||
@ -32,8 +32,6 @@ export const applyEmptyFilters = (
|
||||
|
||||
switch (definition.type) {
|
||||
case 'TEXT':
|
||||
case 'EMAIL':
|
||||
case 'PHONE':
|
||||
emptyRecordFilter = {
|
||||
or: [
|
||||
{ [correspondingField.name]: { ilike: '' } as StringFilter },
|
||||
@ -113,16 +111,6 @@ export const applyEmptyFilters = (
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'LINK':
|
||||
emptyRecordFilter = {
|
||||
or: [
|
||||
{ [correspondingField.name]: { url: { ilike: '' } } as URLFilter },
|
||||
{
|
||||
[correspondingField.name]: { url: { is: 'NULL' } } as URLFilter,
|
||||
},
|
||||
],
|
||||
};
|
||||
break;
|
||||
case 'LINKS': {
|
||||
if (!isCompositeField) {
|
||||
const linksFilters = generateILikeFiltersForCompositeFields(
|
||||
|
||||
@ -17,7 +17,6 @@ import {
|
||||
PhonesFilter,
|
||||
RecordGqlOperationFilter,
|
||||
StringFilter,
|
||||
URLFilter,
|
||||
UUIDFilter,
|
||||
} from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
import { isMatchingBooleanFilter } from '@/object-record/record-filter/utils/isMatchingBooleanFilter';
|
||||
@ -144,8 +143,6 @@ export const isRecordMatchingFilter = ({
|
||||
}
|
||||
|
||||
switch (objectMetadataField.type) {
|
||||
case FieldMetadataType.Email:
|
||||
case FieldMetadataType.Phone:
|
||||
case FieldMetadataType.Select:
|
||||
case FieldMetadataType.Rating:
|
||||
case FieldMetadataType.MultiSelect:
|
||||
@ -155,22 +152,6 @@ export const isRecordMatchingFilter = ({
|
||||
value: record[filterKey],
|
||||
});
|
||||
}
|
||||
case FieldMetadataType.Link: {
|
||||
const urlFilter = filterValue as URLFilter;
|
||||
|
||||
return (
|
||||
(urlFilter.url === undefined ||
|
||||
isMatchingStringFilter({
|
||||
stringFilter: urlFilter.url,
|
||||
value: record[filterKey].url,
|
||||
})) &&
|
||||
(urlFilter.label === undefined ||
|
||||
isMatchingStringFilter({
|
||||
stringFilter: urlFilter.label,
|
||||
value: record[filterKey].label,
|
||||
}))
|
||||
);
|
||||
}
|
||||
case FieldMetadataType.FullName: {
|
||||
const fullNameFilter = filterValue as FullNameFilter;
|
||||
|
||||
|
||||
@ -10,7 +10,6 @@ import {
|
||||
RecordGqlOperationFilter,
|
||||
RelationFilter,
|
||||
StringFilter,
|
||||
URLFilter,
|
||||
UUIDFilter,
|
||||
} from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
|
||||
@ -388,43 +387,6 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'LINK':
|
||||
switch (rawUIFilter.operand) {
|
||||
case ViewFilterOperand.Contains:
|
||||
objectRecordFilters.push({
|
||||
[correspondingField.name]: {
|
||||
url: {
|
||||
ilike: `%${rawUIFilter.value}%`,
|
||||
},
|
||||
} as URLFilter,
|
||||
});
|
||||
break;
|
||||
case ViewFilterOperand.DoesNotContain:
|
||||
objectRecordFilters.push({
|
||||
not: {
|
||||
[correspondingField.name]: {
|
||||
url: {
|
||||
ilike: `%${rawUIFilter.value}%`,
|
||||
},
|
||||
} as URLFilter,
|
||||
},
|
||||
});
|
||||
break;
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
applyEmptyFilters(
|
||||
rawUIFilter.operand,
|
||||
correspondingField,
|
||||
objectRecordFilters,
|
||||
rawUIFilter.definition,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'LINKS': {
|
||||
const linksFilters = generateILikeFiltersForCompositeFields(
|
||||
rawUIFilter.value,
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import { isValidPhoneNumber } from 'libphonenumber-js';
|
||||
|
||||
import { FieldValidationDefinition } from '@/spreadsheet-import/types';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
@ -41,15 +39,6 @@ export const getSpreadSheetFieldValidationDefinitions = (
|
||||
level: 'error',
|
||||
},
|
||||
];
|
||||
case FieldMetadataType.Phone:
|
||||
return [
|
||||
{
|
||||
rule: 'function',
|
||||
isValid: (value: string) => isValidPhoneNumber(value),
|
||||
errorMessage: fieldName + ' is not valid',
|
||||
level: 'error',
|
||||
},
|
||||
];
|
||||
case FieldMetadataType.Relation:
|
||||
return [
|
||||
{
|
||||
|
||||
@ -8,20 +8,12 @@ export const generateEmptyFieldValue = (
|
||||
fieldMetadataItem: Pick<FieldMetadataItem, 'type' | 'relationDefinition'>,
|
||||
) => {
|
||||
switch (fieldMetadataItem.type) {
|
||||
case FieldMetadataType.Email:
|
||||
case FieldMetadataType.Phone:
|
||||
case FieldMetadataType.Text: {
|
||||
return '';
|
||||
}
|
||||
case FieldMetadataType.Emails: {
|
||||
return { primaryEmail: '', additionalEmails: null };
|
||||
}
|
||||
case FieldMetadataType.Link: {
|
||||
return {
|
||||
label: '',
|
||||
url: '',
|
||||
};
|
||||
}
|
||||
case FieldMetadataType.Links: {
|
||||
return { primaryLinkUrl: '', primaryLinkLabel: '', secondaryLinks: null };
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@ import {
|
||||
FieldEmailsValue,
|
||||
FieldFullNameValue,
|
||||
FieldLinksValue,
|
||||
FieldLinkValue,
|
||||
FieldPhonesValue,
|
||||
} from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { SettingsFieldTypeConfig } from '@/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs';
|
||||
@ -38,8 +37,8 @@ export const SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS = {
|
||||
[FieldMetadataType.Currency]: {
|
||||
label: 'Currency',
|
||||
Icon: IllustrationIconCurrency,
|
||||
subFields: ['amountMicros', 'currencyCode'],
|
||||
filterableSubFields: ['amountMicros', 'currencyCode'],
|
||||
subFields: ['amountMicros'],
|
||||
filterableSubFields: ['amountMicros'],
|
||||
labelBySubField: {
|
||||
amountMicros: 'Amount',
|
||||
currencyCode: 'Currency',
|
||||
@ -69,18 +68,6 @@ export const SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS = {
|
||||
},
|
||||
category: 'Basic',
|
||||
} as const satisfies SettingsCompositeFieldTypeConfig<FieldEmailsValue>,
|
||||
[FieldMetadataType.Link]: {
|
||||
label: 'Link',
|
||||
Icon: IllustrationIconLink,
|
||||
exampleValue: { url: 'www.twenty.com', label: '' },
|
||||
category: 'Basic',
|
||||
subFields: ['url', 'label'],
|
||||
filterableSubFields: ['url', 'label'],
|
||||
labelBySubField: {
|
||||
url: 'URL',
|
||||
label: 'Label',
|
||||
},
|
||||
} as const satisfies SettingsCompositeFieldTypeConfig<FieldLinkValue>,
|
||||
[FieldMetadataType.Links]: {
|
||||
label: 'Links',
|
||||
Icon: IllustrationIconLink,
|
||||
@ -178,8 +165,8 @@ export const SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS = {
|
||||
label: 'Actor',
|
||||
Icon: IllustrationIconSetting,
|
||||
category: 'Basic',
|
||||
subFields: ['source', 'name', 'workspaceMemberId'],
|
||||
filterableSubFields: ['source', 'name', 'workspaceMemberId'],
|
||||
subFields: ['source'],
|
||||
filterableSubFields: ['source'],
|
||||
labelBySubField: {
|
||||
source: 'Source',
|
||||
name: 'Name',
|
||||
|
||||
@ -4,10 +4,8 @@ import {
|
||||
IllustrationIconCalendarEvent,
|
||||
IllustrationIconCalendarTime,
|
||||
IllustrationIconJson,
|
||||
IllustrationIconMail,
|
||||
IllustrationIconNumbers,
|
||||
IllustrationIconOneToMany,
|
||||
IllustrationIconPhone,
|
||||
IllustrationIconSetting,
|
||||
IllustrationIconStar,
|
||||
IllustrationIconTag,
|
||||
@ -22,11 +20,9 @@ import {
|
||||
FieldBooleanValue,
|
||||
FieldDateTimeValue,
|
||||
FieldDateValue,
|
||||
FieldEmailValue,
|
||||
FieldJsonValue,
|
||||
FieldMultiSelectValue,
|
||||
FieldNumberValue,
|
||||
FieldPhoneValue,
|
||||
FieldRatingValue,
|
||||
FieldRelationValue,
|
||||
FieldRichTextValue,
|
||||
@ -114,17 +110,6 @@ export const SETTINGS_NON_COMPOSITE_FIELD_TYPE_CONFIGS: SettingsNonCompositeFiel
|
||||
Icon: IllustrationIconOneToMany,
|
||||
category: 'Relation',
|
||||
} as const satisfies SettingsFieldTypeConfig<FieldRelationValue<any>>,
|
||||
[FieldMetadataType.Email]: {
|
||||
label: 'Email',
|
||||
Icon: IllustrationIconMail,
|
||||
category: 'Basic',
|
||||
} as const satisfies SettingsFieldTypeConfig<FieldEmailValue>,
|
||||
[FieldMetadataType.Phone]: {
|
||||
label: 'Phone',
|
||||
Icon: IllustrationIconPhone,
|
||||
exampleValue: '+1234-567-890',
|
||||
category: 'Basic',
|
||||
} as const satisfies SettingsFieldTypeConfig<FieldPhoneValue>,
|
||||
[FieldMetadataType.Rating]: {
|
||||
label: 'Rating',
|
||||
Icon: IllustrationIconStar,
|
||||
|
||||
@ -113,11 +113,9 @@ const previewableTypes = [
|
||||
FieldMetadataType.DateTime,
|
||||
FieldMetadataType.Emails,
|
||||
FieldMetadataType.FullName,
|
||||
FieldMetadataType.Link,
|
||||
FieldMetadataType.Links,
|
||||
FieldMetadataType.MultiSelect,
|
||||
FieldMetadataType.Number,
|
||||
FieldMetadataType.Phone,
|
||||
FieldMetadataType.Phones,
|
||||
FieldMetadataType.Rating,
|
||||
FieldMetadataType.RawJson,
|
||||
|
||||
@ -5,7 +5,6 @@ import { PickLiteral } from '~/types/PickLiteral';
|
||||
export const COMPOSITE_FIELD_TYPES = [
|
||||
'CURRENCY',
|
||||
'EMAILS',
|
||||
'LINK',
|
||||
'LINKS',
|
||||
'ADDRESS',
|
||||
'PHONES',
|
||||
|
||||
@ -43,13 +43,13 @@ export const SIGN_IN_BACKGROUND_MOCK_FILTER_DEFINITIONS = [
|
||||
fieldMetadataId: '20202020-a61d-4b78-b998-3fd88b4f73a1',
|
||||
label: 'Linkedin',
|
||||
iconName: 'IconBrandLinkedin',
|
||||
type: 'LINK',
|
||||
type: 'LINKS',
|
||||
},
|
||||
{
|
||||
fieldMetadataId: '20202020-46e3-479a-b8f4-77137c74daa6',
|
||||
label: 'X',
|
||||
iconName: 'IconBrandX',
|
||||
type: 'LINK',
|
||||
type: 'LINKS',
|
||||
},
|
||||
{
|
||||
fieldMetadataId: '20202020-4a5a-466f-92d9-c3870d9502a9',
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
|
||||
import { FieldLinkValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { RoundedLink } from '@/ui/navigation/link/components/RoundedLink';
|
||||
import {
|
||||
LinkType,
|
||||
@ -8,7 +7,7 @@ import {
|
||||
} from '@/ui/navigation/link/components/SocialLink';
|
||||
|
||||
type LinkDisplayProps = {
|
||||
value?: FieldLinkValue;
|
||||
value?: { url: string; label?: string };
|
||||
};
|
||||
|
||||
export const LinkDisplay = ({ value }: LinkDisplayProps) => {
|
||||
|
||||
Reference in New Issue
Block a user