test: improve utils coverage (#4230)

* test: improve utils coverage

* refactor: review - rename isDefined to isNonNullable, update tests and return statement
This commit is contained in:
Thaïs
2024-02-29 13:03:52 -03:00
committed by GitHub
parent 6ec0e5e995
commit 30df6c10ea
85 changed files with 396 additions and 240 deletions

View File

@ -22,7 +22,7 @@ import {
} from '@/ui/input/components/Checkbox';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
const StyledEditableTitleInput = styled.input<{
completed: boolean;
@ -166,7 +166,7 @@ export const ActivityTitle = ({ activityId }: ActivityTitleProps) => {
});
};
const completed = isDefined(activity.completedAt);
const completed = isNonNullable(activity.completedAt);
return (
<StyledContainer>

View File

@ -8,7 +8,7 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
import { getEmptyPageInfo } from '@/object-record/cache/utils/getEmptyPageInfo';
import { useMapConnectionToRecords } from '@/object-record/hooks/useMapConnectionToRecords';
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const useActivityConnectionUtils = () => {
const mapConnectionToRecords = useMapConnectionToRecords();
@ -19,7 +19,7 @@ export const useActivityConnectionUtils = () => {
comments: ObjectRecordConnection<Comment>;
},
) => {
if (!isDefined(activityWithConnections)) {
if (!isNonNullable(activityWithConnections)) {
throw new Error('Activity with connections is not defined');
}

View File

@ -7,7 +7,7 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { Nullable } from '~/types/Nullable';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const useActivityTargetObjectRecords = ({
activityId,
@ -31,7 +31,7 @@ export const useActivityTargetObjectRecords = ({
.map<Nullable<ActivityTargetWithTargetRecord>>((activityTarget) => {
const correspondingObjectMetadataItem = objectMetadataItems.find(
(objectMetadataItem) =>
isDefined(activityTarget[objectMetadataItem.nameSingular]) &&
isNonNullable(activityTarget[objectMetadataItem.nameSingular]) &&
!objectMetadataItem.isSystem,
);
@ -47,7 +47,7 @@ export const useActivityTargetObjectRecords = ({
targetObjectNameSingular: correspondingObjectMetadataItem.nameSingular,
};
})
.filter(isDefined);
.filter(isNonNullable);
return {
activityTargetObjectRecords,

View File

@ -6,7 +6,7 @@ import { triggerAttachRelationOptimisticEffect } from '@/apollo/optimistic-effec
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { getObjectMetadataItemByNameSingular } from '@/object-metadata/utils/getObjectMetadataItemBySingularName';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const useAttachRelationInBothDirections = () => {
const { objectMetadataItems } = useObjectMetadataItems();
@ -46,7 +46,7 @@ export const useAttachRelationInBothDirections = () => {
(field) => field.name === fieldNameOnSourceRecord,
);
if (!isDefined(fieldMetadataItemOnSourceRecord)) {
if (!isNonNullable(fieldMetadataItemOnSourceRecord)) {
throw new Error(
`Field ${fieldNameOnSourceRecord} not found on object ${sourceObjectNameSingular}`,
);
@ -57,7 +57,7 @@ export const useAttachRelationInBothDirections = () => {
objectMetadataItems,
});
if (!isDefined(relationDefinition)) {
if (!isNonNullable(relationDefinition)) {
throw new Error(
`Relation metadata not found for field ${fieldNameOnSourceRecord} on object ${sourceObjectNameSingular}`,
);

View File

@ -14,7 +14,7 @@ import { useCreateOneRecordInCache } from '@/object-record/hooks/useCreateOneRec
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const useCreateActivityInCache = () => {
const { createManyRecordsInCache: createManyActivityTargetsInCache } =
@ -71,7 +71,7 @@ export const useCreateActivityInCache = () => {
return targetObject;
})
.filter(isDefined);
.filter(isNonNullable);
const activityTargetsToCreate =
makeActivityTargetsToCreateFromTargetableObjects({

View File

@ -5,7 +5,7 @@ import { ActivityType } from '@/activities/types/Activity';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { ActivityTargetableObject } from '../types/ActivityTargetableEntity';
@ -47,7 +47,7 @@ export const useOpenCreateActivityDrawerForSelectedRowIds = (
targetObjectRecord,
};
})
.filter(isDefined);
.filter(isNonNullable);
if (relatedEntities) {
activityTargetableObjectArray =

View File

@ -16,7 +16,7 @@ import { objectShowPageTargetableObjectState } from '@/activities/timeline/state
import { Activity } from '@/activities/types/Activity';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
// TODO: create a generic way to have records only in cache for create mode and delete them afterwards ?
export const useUpsertActivity = () => {
@ -85,7 +85,7 @@ export const useUpsertActivity = () => {
makeActivityWithConnection(activityToCreate);
if (weAreOnTaskPage) {
if (isDefined(activityWithConnection.completedAt)) {
if (isNonNullable(activityWithConnection.completedAt)) {
injectActivitiesQueries({
activitiesFilters: currentCompletedTaskQueryVariables?.filter,
activitiesOrderByVariables:
@ -125,7 +125,7 @@ export const useUpsertActivity = () => {
const injectOnlyInIdFilterForNotesQueries =
activityWithConnection.type !== 'Note';
if (isDefined(currentCompletedTaskQueryVariables)) {
if (isNonNullable(currentCompletedTaskQueryVariables)) {
injectActivitiesQueries({
activitiesFilters: currentCompletedTaskQueryVariables?.filter,
activitiesOrderByVariables:
@ -137,7 +137,7 @@ export const useUpsertActivity = () => {
});
}
if (isDefined(currentIncompleteTaskQueryVariables)) {
if (isNonNullable(currentIncompleteTaskQueryVariables)) {
injectActivitiesQueries({
activitiesFilters:
currentIncompleteTaskQueryVariables?.filter ?? {},
@ -150,7 +150,7 @@ export const useUpsertActivity = () => {
});
}
if (isDefined(currentNotesQueryVariables)) {
if (isNonNullable(currentNotesQueryVariables)) {
injectActivitiesQueries({
activitiesFilters: currentNotesQueryVariables?.filter,
activitiesOrderByVariables: currentNotesQueryVariables?.orderBy,

View File

@ -28,7 +28,7 @@ import { mapToRecordId } from '@/object-record/utils/mapToObjectId';
import { IconPlus, IconTrash } from '@/ui/display/icon';
import { IconButton } from '@/ui/input/button/components/IconButton';
import { isRightDrawerOpenState } from '@/ui/layout/right-drawer/states/isRightDrawerOpenState';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
const StyledButtonContainer = styled.div`
display: inline-flex;
@ -109,7 +109,10 @@ export const ActivityActionBar = () => {
setIsRightDrawerOpen(false);
if (viewableActivityId) {
if (isActivityInCreateMode && isDefined(temporaryActivityForEditor)) {
if (
isActivityInCreateMode &&
isNonNullable(temporaryActivityForEditor)
) {
deleteActivityFromCache(temporaryActivityForEditor);
setTemporaryActivityForEditor(null);
} else {
@ -136,7 +139,7 @@ export const ActivityActionBar = () => {
});
} else if (
weAreOnObjectShowPage &&
isDefined(objectShowPageTargetableObject)
isNonNullable(objectShowPageTargetableObject)
) {
removeFromActivitiesQueries({
activityIdToRemove: viewableActivityId,
@ -146,7 +149,7 @@ export const ActivityActionBar = () => {
FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY,
});
if (isDefined(currentCompletedTaskQueryVariables)) {
if (isNonNullable(currentCompletedTaskQueryVariables)) {
removeFromActivitiesQueries({
activityIdToRemove: viewableActivityId,
targetableObjects: [objectShowPageTargetableObject],
@ -157,7 +160,7 @@ export const ActivityActionBar = () => {
});
}
if (isDefined(currentIncompleteTaskQueryVariables)) {
if (isNonNullable(currentIncompleteTaskQueryVariables)) {
removeFromActivitiesQueries({
activityIdToRemove: viewableActivityId,
targetableObjects: [objectShowPageTargetableObject],
@ -168,7 +171,7 @@ export const ActivityActionBar = () => {
});
}
if (isDefined(currentNotesQueryVariables)) {
if (isNonNullable(currentNotesQueryVariables)) {
removeFromActivitiesQueries({
activityIdToRemove: viewableActivityId,
targetableObjects: [objectShowPageTargetableObject],

View File

@ -12,7 +12,7 @@ import { Activity } from '@/activities/types/Activity';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { sortObjectRecordByDateField } from '@/object-record/utils/sortObjectRecordByDateField';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const TimelineQueryEffect = ({
targetableObject,
@ -31,7 +31,7 @@ export const TimelineQueryEffect = ({
targetableObjects: [targetableObject],
activitiesFilters: {},
activitiesOrderByVariables: FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY,
skip: !isDefined(targetableObject),
skip: !isNonNullable(targetableObject),
});
const [timelineActivitiesNetworking, setTimelineActivitiesNetworking] =
@ -41,7 +41,7 @@ export const TimelineQueryEffect = ({
useRecoilState(timelineActivitiesForGroupState);
useEffect(() => {
if (!isDefined(targetableObject)) {
if (!isNonNullable(targetableObject)) {
return;
}

View File

@ -13,7 +13,7 @@ import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getR
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { sortByAscString } from '~/utils/array/sortByAscString';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const useTimelineActivities = ({
targetableObject,
@ -27,7 +27,7 @@ export const useTimelineActivities = ({
);
useEffect(() => {
if (isDefined(targetableObject)) {
if (isNonNullable(targetableObject)) {
setObjectShowPageTargetableObject(targetableObject);
}
}, [targetableObject, setObjectShowPageTargetableObject]);
@ -100,7 +100,7 @@ export const useTimelineActivities = ({
const activities = activitiesWithConnection
?.map(makeActivityWithoutConnection as any)
.map(({ activity }: any) => activity as any)
.filter(isDefined);
.filter(isNonNullable);
return {
activities,

View File

@ -4,7 +4,7 @@ import { ReadFieldFunction } from '@apollo/client/cache/core/types/common';
import { CachedObjectRecordEdge } from '@/apollo/types/CachedObjectRecordEdge';
import { OrderBy } from '@/object-metadata/types/OrderBy';
import { OrderByField } from '@/object-metadata/types/OrderByField';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { sortAsc, sortDesc, sortNullsFirst, sortNullsLast } from '~/utils/sort';
export const sortCachedObjectEdges = ({
@ -31,7 +31,7 @@ export const sortCachedObjectEdges = ({
orderByFieldName,
recordFromCache,
) ?? null;
const isSubFieldFilter = isDefined(fieldValue) && !!orderBySubFieldName;
const isSubFieldFilter = isNonNullable(fieldValue) && !!orderBySubFieldName;
if (!isSubFieldFilter) return fieldValue as string | number | null;

View File

@ -2,7 +2,7 @@ import { ApolloCache, StoreObject } from '@apollo/client';
import { isCachedObjectRecordConnection } from '@/apollo/optimistic-effect/utils/isCachedObjectRecordConnection';
import { CachedObjectRecordEdge } from '@/apollo/types/CachedObjectRecordEdge';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { capitalize } from '~/utils/string/capitalize';
export const triggerAttachRelationOptimisticEffect = ({
@ -43,7 +43,7 @@ export const triggerAttachRelationOptimisticEffect = ({
__typename: sourceRecordTypeName,
});
if (!isDefined(sourceRecordReference)) {
if (!isNonNullable(sourceRecordReference)) {
return targetRecordFieldValue;
}

View File

@ -6,7 +6,7 @@ import { CachedObjectRecord } from '@/apollo/types/CachedObjectRecord';
import { CachedObjectRecordEdge } from '@/apollo/types/CachedObjectRecordEdge';
import { CachedObjectRecordQueryVariables } from '@/apollo/types/CachedObjectRecordQueryVariables';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { parseApolloStoreFieldName } from '~/utils/parseApolloStoreFieldName';
export const triggerDeleteRecordsOptimisticEffect = ({
@ -38,7 +38,7 @@ export const triggerDeleteRecordsOptimisticEffect = ({
const rootQueryCachedObjectRecordConnection = rootQueryCachedResponse;
const { fieldArguments: rootQueryVariables } =
const { fieldVariables: rootQueryVariables } =
parseApolloStoreFieldName<CachedObjectRecordQueryVariables>(
storeFieldName,
);
@ -68,7 +68,7 @@ export const triggerDeleteRecordsOptimisticEffect = ({
// TODO: same as in update, should we trigger DELETE ?
if (
isDefined(rootQueryVariables?.first) &&
isNonNullable(rootQueryVariables?.first) &&
cachedEdges?.length === rootQueryVariables.first
) {
return DELETE;

View File

@ -9,7 +9,7 @@ import { CachedObjectRecordQueryVariables } from '@/apollo/types/CachedObjectRec
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { getEdgeTypename } from '@/object-record/cache/utils/getEdgeTypename';
import { isRecordMatchingFilter } from '@/object-record/record-filter/utils/isRecordMatchingFilter';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { parseApolloStoreFieldName } from '~/utils/parseApolloStoreFieldName';
// TODO: add extensive unit tests for this function
@ -56,7 +56,7 @@ export const triggerUpdateRecordOptimisticEffect = ({
const rootQueryConnection = rootQueryCachedResponse;
const { fieldArguments: rootQueryVariables } =
const { fieldVariables: rootQueryVariables } =
parseApolloStoreFieldName<CachedObjectRecordQueryVariables>(
storeFieldName,
);
@ -71,7 +71,7 @@ export const triggerUpdateRecordOptimisticEffect = ({
const rootQueryOrderBy = rootQueryVariables?.orderBy;
const rootQueryLimit = rootQueryVariables?.first;
const shouldTryToMatchFilter = isDefined(rootQueryFilter);
const shouldTryToMatchFilter = isNonNullable(rootQueryFilter);
if (shouldTryToMatchFilter) {
const updatedRecordMatchesThisRootQueryFilter =
@ -101,7 +101,7 @@ export const triggerUpdateRecordOptimisticEffect = ({
if (updatedRecordShouldBeAddedToRootQueryEdges) {
const updatedRecordNodeReference = toReference(updatedRecord);
if (isDefined(updatedRecordNodeReference)) {
if (isNonNullable(updatedRecordNodeReference)) {
rootQueryNextEdges.push({
__typename: objectEdgeTypeName,
node: updatedRecordNodeReference,
@ -115,7 +115,8 @@ export const triggerUpdateRecordOptimisticEffect = ({
}
}
const rootQueryNextEdgesShouldBeSorted = isDefined(rootQueryOrderBy);
const rootQueryNextEdgesShouldBeSorted =
isNonNullable(rootQueryOrderBy);
if (
rootQueryNextEdgesShouldBeSorted &&
@ -128,7 +129,7 @@ export const triggerUpdateRecordOptimisticEffect = ({
});
}
const shouldLimitNextRootQueryEdges = isDefined(rootQueryLimit);
const shouldLimitNextRootQueryEdges = isNonNullable(rootQueryLimit);
// TODO: not sure that we should trigger a DELETE here, as it will trigger a network request
// Is it the responsibility of this optimistic effect function to delete a root query that will trigger a network request ?

View File

@ -12,7 +12,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const triggerUpdateRelationsOptimisticEffect = ({
cache,
@ -36,7 +36,7 @@ export const triggerUpdateRelationsOptimisticEffect = ({
}
const fieldDoesNotExist =
isDefined(updatedSourceRecord) &&
isNonNullable(updatedSourceRecord) &&
!(fieldMetadataItemOnSourceRecord.name in updatedSourceRecord);
if (fieldDoesNotExist) {
@ -87,7 +87,7 @@ export const triggerUpdateRelationsOptimisticEffect = ({
? currentFieldValueOnSourceRecord.edges.map(
({ node }) => node as CachedObjectRecord,
)
: [currentFieldValueOnSourceRecord].filter(isDefined);
: [currentFieldValueOnSourceRecord].filter(isNonNullable);
const updatedFieldValueOnSourceRecordIsARecordConnection =
isObjectRecordConnection(
@ -100,10 +100,11 @@ export const triggerUpdateRelationsOptimisticEffect = ({
? updatedFieldValueOnSourceRecord.edges.map(
({ node }) => node as CachedObjectRecord,
)
: [updatedFieldValueOnSourceRecord].filter(isDefined);
: [updatedFieldValueOnSourceRecord].filter(isNonNullable);
const shouldDetachSourceFromAllTargets =
isDefined(currentSourceRecord) && targetRecordsToDetachFrom.length > 0;
isNonNullable(currentSourceRecord) &&
targetRecordsToDetachFrom.length > 0;
if (shouldDetachSourceFromAllTargets) {
// TODO: see if we can de-hardcode this, put cascade delete in relation metadata item

View File

@ -14,7 +14,7 @@ import { createUploadLink } from 'apollo-upload-client';
import { renewToken } from '@/auth/services/AuthService';
import { AuthTokenPair } from '~/generated/graphql';
import { assertNotNull } from '~/utils/assert';
import { isNonNullable } from '~/utils/isNonNullable';
import { logDebug } from '~/utils/logDebug';
import { ApolloManager } from '../types/apolloManager.interface';
@ -139,7 +139,7 @@ export class ApolloFactory<TCacheShape> implements ApolloManager<TCacheShape> {
isDebugMode ? logger : null,
retryLink,
httpLink,
].filter(assertNotNull),
].filter(isNonNullable),
);
};

View File

@ -11,7 +11,7 @@ import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const useFavorites = () => {
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
@ -56,7 +56,7 @@ export const useFavorites = () => {
return favorites
.map((favorite) => {
for (const relationField of favoriteRelationFieldMetadataItems) {
if (isDefined(favorite[relationField.name])) {
if (isNonNullable(favorite[relationField.name])) {
const relationObject = favorite[relationField.name];
const relationObjectNameSingular =

View File

@ -1,11 +1,10 @@
import { getLogoUrlFromDomainName } from '~/utils';
import { assertNotNull } from '~/utils/assert';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const mapFavorites = (favorites: any) => {
return favorites
.map((favorite: any) => {
const recordInformation = isDefined(favorite?.person)
const recordInformation = isNonNullable(favorite?.person)
? {
id: favorite.person.id,
labelIdentifier:
@ -16,7 +15,7 @@ export const mapFavorites = (favorites: any) => {
avatarType: 'rounded',
link: `/object/person/${favorite.person.id}`,
}
: isDefined(favorite?.company)
: isNonNullable(favorite?.company)
? {
id: favorite.company.id,
labelIdentifier: favorite.company.name,
@ -33,6 +32,6 @@ export const mapFavorites = (favorites: any) => {
position: favorite?.position,
};
})
.filter(assertNotNull)
.filter(isNonNullable)
.sort((a: any, b: any) => a.position - b.position);
};

View File

@ -2,7 +2,7 @@ import { useRecoilValue } from 'recoil';
import { objectMetadataItemsByNameSingularMapSelector } from '@/object-metadata/states/objectMetadataItemsByNameSingularMapSelector';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const useFilterOutUnexistingObjectMetadataItems = () => {
const objectMetadataItemsByNameSingularMap = useRecoilValue(
@ -12,7 +12,7 @@ export const useFilterOutUnexistingObjectMetadataItems = () => {
const filterOutUnexistingObjectMetadataItems = (
objectMetadatItem: ObjectMetadataItem,
) =>
isDefined(
isNonNullable(
objectMetadataItemsByNameSingularMap.get(objectMetadatItem.nameSingular),
);

View File

@ -21,7 +21,7 @@ import { useGenerateFindManyRecordsQuery } from '@/object-record/hooks/useGenera
import { useGenerateFindOneRecordQuery } from '@/object-record/hooks/useGenerateFindOneRecordQuery';
import { useGenerateUpdateOneRecordMutation } from '@/object-record/hooks/useGenerateUpdateOneRecordMutation';
import { generateDeleteOneRecordMutation } from '@/object-record/utils/generateDeleteOneRecordMutation';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { ObjectMetadataItemIdentifier } from '../types/ObjectMetadataItemIdentifier';
@ -63,7 +63,7 @@ export const useObjectMetadataItem = (
objectMetadataItems = mockObjectMetadataItems;
}
if (!isDefined(objectMetadataItem)) {
if (!isNonNullable(objectMetadataItem)) {
throw new ObjectMetadataItemNotFoundError(
objectNameSingular,
objectMetadataItems,

View File

@ -5,7 +5,7 @@ import { ObjectMetadataItemNotFoundError } from '@/object-metadata/errors/Object
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { ObjectMetadataItemIdentifier } from '../types/ObjectMetadataItemIdentifier';
@ -34,7 +34,7 @@ export const useObjectMetadataItemOnly = ({
objectMetadataItems = mockObjectMetadataItems;
}
if (!isDefined(objectMetadataItem)) {
if (!isNonNullable(objectMetadataItem)) {
throw new ObjectMetadataItemNotFoundError(
objectNameSingular,
objectMetadataItems,

View File

@ -3,7 +3,7 @@ import { useRecoilValue } from 'recoil';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState.ts';
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const useObjectNamePluralFromSingular = ({
objectNameSingular,
@ -28,7 +28,7 @@ export const useObjectNamePluralFromSingular = ({
) ?? null;
}
if (!isDefined(objectMetadataItem)) {
if (!isNonNullable(objectMetadataItem)) {
throw new Error(
`Object metadata item not found for ${objectNameSingular} object`,
);

View File

@ -3,7 +3,7 @@ import { useRecoilValue } from 'recoil';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState.ts';
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const useObjectNameSingularFromPlural = ({
objectNamePlural,
@ -29,7 +29,7 @@ export const useObjectNameSingularFromPlural = ({
) ?? null;
}
if (!isDefined(objectMetadataItem)) {
if (!isNonNullable(objectMetadataItem)) {
throw new Error(
`Object metadata item not found for ${objectNamePlural} object`,
);

View File

@ -1,6 +1,6 @@
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const DEFAULT_LABEL_IDENTIFIER_FIELD_NAME = 'name';
@ -14,6 +14,6 @@ export const isLabelIdentifierField = ({
'labelIdentifierFieldMetadataId'
>;
}) =>
isDefined(objectMetadataItem.labelIdentifierFieldMetadataId)
isNonNullable(objectMetadataItem.labelIdentifierFieldMetadataId)
? fieldMetadataItem.id === objectMetadataItem.labelIdentifierFieldMetadataId
: fieldMetadataItem.name === DEFAULT_LABEL_IDENTIFIER_FIELD_NAME;

View File

@ -4,7 +4,7 @@ import {
FieldMetadataType,
RelationMetadataType,
} from '~/generated-metadata/graphql';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const parseFieldRelationType = (
field: FieldMetadataItem | undefined,
@ -30,14 +30,14 @@ export const parseFieldRelationType = (
};
if (
isDefined(field.fromRelationMetadata) &&
isNonNullable(field.fromRelationMetadata) &&
field.fromRelationMetadata.relationType in config
) {
return config[field.fromRelationMetadata.relationType].from;
}
if (
isDefined(field.toRelationMetadata) &&
isNonNullable(field.toRelationMetadata) &&
field.toRelationMetadata.relationType in config
) {
return config[field.toRelationMetadata.relationType].to;

View File

@ -6,7 +6,7 @@ import { useGenerateFindManyRecordsQuery } from '@/object-record/hooks/useGenera
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ObjectRecordQueryResult } from '@/object-record/types/ObjectRecordQueryResult';
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const useReadFindManyRecordsQueryInCache = ({
objectMetadataItem,
@ -38,7 +38,7 @@ export const useReadFindManyRecordsQueryInCache = ({
const existingRecordConnection =
existingRecordsQueryResult?.[objectMetadataItem.namePlural];
const existingObjectRecords = isDefined(existingRecordConnection)
const existingObjectRecords = isNonNullable(existingRecordConnection)
? getRecordsFromRecordConnection({
recordConnection: existingRecordConnection,
})

View File

@ -13,7 +13,7 @@ import {
import { useMapConnectionToRecords } from '@/object-record/hooks/useMapConnectionToRecords';
import { Person } from '@/people/types/Person';
import { getJestHookWrapper } from '~/testing/jest/getJestHookWrapper';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
const Wrapper = getJestHookWrapper({
apolloMocks: [],
@ -184,7 +184,7 @@ describe('useMapConnectionToRecords', () => {
expect(isNonEmptyArray(peopleResult)).toBe(true);
expect(firstPersonResult.id).toBe(firstPersonMock.id);
expect(isDefined(firstPersonsCompanyresult)).toBe(true);
expect(isNonNullable(firstPersonsCompanyresult)).toBe(true);
expect(firstPersonsCompanyresult.id).toEqual(firstPersonsCompanyMock.id);
});
});

View File

@ -4,7 +4,7 @@ import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { getDeleteManyRecordsMutationResponseField } from '@/object-record/hooks/useGenerateDeleteManyRecordMutation';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { capitalize } from '~/utils/string/capitalize';
type useDeleteOneRecordProps = {
@ -56,7 +56,7 @@ export const useDeleteManyRecords = ({
const cachedRecords = records
.map((record) => getRecordFromCache(record.id, cache))
.filter(isDefined);
.filter(isNonNullable);
triggerDeleteRecordsOptimisticEffect({
cache,

View File

@ -8,7 +8,7 @@ import { parseFieldRelationType } from '@/object-metadata/utils/parseFieldRelati
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
import { FieldMetadataType } from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const useMapConnectionToRecords = () => {
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
@ -26,7 +26,7 @@ export const useMapConnectionToRecords = () => {
depth: number;
}): ObjectRecord[] => {
if (
!isDefined(objectRecordConnection) ||
!isNonNullable(objectRecordConnection) ||
!isNonEmptyArray(objectMetadataItems)
) {
return [];
@ -79,7 +79,7 @@ export const useMapConnectionToRecords = () => {
if (
!relationFieldMetadataItem ||
!isDefined(relatedObjectMetadataSingularName)
!isNonNullable(relatedObjectMetadataSingularName)
) {
throw new Error(
`Could not find relation object metadata item for object name plural ${relationField.name} in mapConnectionToRecords`,
@ -90,7 +90,7 @@ export const useMapConnectionToRecords = () => {
relationField.name
] as ObjectRecordConnection | undefined | null;
if (!isDefined(relationConnection)) {
if (!isNonNullable(relationConnection)) {
continue;
}

View File

@ -1,6 +1,6 @@
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
import { InternalDatePicker } from '@/ui/input/components/internal/date/components/InternalDatePicker';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const ObjectFilterDropdownDateSearchInput = () => {
const {
@ -15,9 +15,9 @@ export const ObjectFilterDropdownDateSearchInput = () => {
selectFilter?.({
fieldMetadataId: filterDefinitionUsedInDropdown.fieldMetadataId,
value: isDefined(date) ? date.toISOString() : '',
value: isNonNullable(date) ? date.toISOString() : '',
operand: selectedOperandInDropdown,
displayValue: isDefined(date) ? date.toLocaleString() : '',
displayValue: isNonNullable(date) ? date.toLocaleString() : '',
definition: filterDefinitionUsedInDropdown,
});

View File

@ -6,7 +6,7 @@ import { useRecordBoardDeprecatedActionBarEntriesInternal } from '@/object-recor
import { useRecordBoardDeprecatedContextMenuEntriesInternal } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedContextMenuEntriesInternal';
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
import { useUpdateCompanyBoardColumnsInternal } from '@/object-record/record-board-deprecated/hooks/internal/useUpdateCompanyBoardColumnsInternal';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export type RecordBoardDeprecatedInternalEffectProps = {
onFieldsChange: (fields: any) => void;
@ -40,13 +40,13 @@ export const RecordBoardDeprecatedInternalEffect = () => {
}, [opportunities, setSavedOpportunities]);
useEffect(() => {
if (isDefined(fetchMoreOpportunities)) {
if (isNonNullable(fetchMoreOpportunities)) {
fetchMoreOpportunities();
}
}, [fetchMoreOpportunities]);
useEffect(() => {
if (isDefined(fetchMoreCompanies)) {
if (isNonNullable(fetchMoreCompanies)) {
fetchMoreCompanies();
}
}, [fetchMoreCompanies]);

View File

@ -5,7 +5,7 @@ import { recordBoardColumnsFamilyStateScopeMap } from '@/object-record/record-bo
import { RecordBoardColumnDefinition } from '@/object-record/record-board/types/RecordBoardColumnDefinition';
import { createFamilySelectorScopeMap } from '@/ui/utilities/recoil-scope/utils/createFamilySelectorScopeMap';
import { guardRecoilDefaultValue } from '@/ui/utilities/recoil-scope/utils/guardRecoilDefaultValue';
import { assertNotNull } from '~/utils/assert';
import { isNonNullable } from '~/utils/isNonNullable';
export const recordBoardColumnsFamilySelectorScopeMap =
createFamilySelectorScopeMap<RecordBoardColumnDefinition | undefined, string>(
@ -57,7 +57,7 @@ export const recordBoardColumnsFamilySelectorScopeMap =
}),
);
})
.filter(assertNotNull);
.filter(isNonNullable);
const lastColumn = [...columns].sort(
(a, b) => b.position - a.position,

View File

@ -2,7 +2,7 @@ import { Key } from 'ts-key-enum';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const useRegisterInputEvents = <T>({
inputRef,
@ -30,7 +30,7 @@ export const useRegisterInputEvents = <T>({
onClickOutside?.(event, inputValue);
},
enabled: isDefined(onClickOutside),
enabled: isNonNullable(onClickOutside),
});
useScopedHotkeys(

View File

@ -17,9 +17,9 @@ import { isFieldSelect } from '@/object-record/record-field/types/guards/isField
import { isFieldSelectValue } from '@/object-record/record-field/types/guards/isFieldSelectValue';
import { isFieldText } from '@/object-record/record-field/types/guards/isFieldText';
import { isFieldUuid } from '@/object-record/record-field/types/guards/isFieldUuid';
import { assertNotNull } from '~/utils/assert';
import { isNonNullable } from '~/utils/isNonNullable';
const isValueEmpty = (value: unknown) => !assertNotNull(value) || value === '';
const isValueEmpty = (value: unknown) => !isNonNullable(value) || value === '';
export const isFieldValueEmpty = ({
fieldDefinition,
@ -46,7 +46,7 @@ export const isFieldValueEmpty = ({
}
if (isFieldSelect(fieldDefinition)) {
return isFieldSelectValue(fieldValue) && !assertNotNull(fieldValue);
return isFieldSelectValue(fieldValue) && !isNonNullable(fieldValue);
}
if (isFieldCurrency(fieldDefinition)) {

View File

@ -22,8 +22,8 @@ import { isMatchingFloatFilter } from '@/object-record/record-filter/utils/isMat
import { isMatchingStringFilter } from '@/object-record/record-filter/utils/isMatchingStringFilter';
import { isMatchingUUIDFilter } from '@/object-record/record-filter/utils/isMatchingUUIDFilter';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isDefined } from '~/utils/isDefined';
import { isEmptyObject } from '~/utils/isEmptyObject';
import { isNonNullable } from '~/utils/isNonNullable';
const isAndFilter = (
filter: ObjectRecordQueryFilter,
@ -102,7 +102,7 @@ export const isRecordMatchingFilter = ({
if (isNotFilter(filter)) {
const filterValue = filter.not;
if (!isDefined(filterValue)) {
if (!isNonNullable(filterValue)) {
throw new Error('Unexpected value for "not" filter : ' + filterValue);
}
@ -117,7 +117,7 @@ export const isRecordMatchingFilter = ({
}
return Object.entries(filter).every(([filterKey, filterValue]) => {
if (!isDefined(filterValue)) {
if (!isNonNullable(filterValue)) {
throw new Error(
'Unexpected value for filter key "' + filterKey + '" : ' + filterValue,
);
@ -129,7 +129,7 @@ export const isRecordMatchingFilter = ({
(field) => field.name === filterKey,
);
if (!isDefined(objectMetadataField)) {
if (!isNonNullable(objectMetadataField)) {
throw new Error(
'Field metadata item "' +
filterKey +

View File

@ -11,7 +11,7 @@ import {
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { Field } from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { Filter } from '../../object-filter-dropdown/types/Filter';
@ -38,7 +38,7 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
);
}
if (!isDefined(rawUIFilter.value) || rawUIFilter.value === '') {
if (!isNonNullable(rawUIFilter.value) || rawUIFilter.value === '') {
return undefined;
}

View File

@ -30,7 +30,7 @@ import {
FileFolder,
useUploadImageMutation,
} from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
type RecordShowContainerProps = {
objectNameSingular: string;
@ -131,7 +131,7 @@ export const RecordShowContainer = ({
<RecoilScope CustomRecoilScopeContext={ShowPageRecoilScopeContext}>
<ShowPageContainer>
<ShowPageLeftContainer>
{!recordLoading && isDefined(recordFromStore) && (
{!recordLoading && isNonNullable(recordFromStore) && (
<>
<ShowPageSummaryCard
id={objectRecordId}

View File

@ -6,7 +6,7 @@ import { Activity } from '@/activities/types/Activity';
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { recordLoadingFamilyState } from '@/object-record/record-store/states/recordLoadingFamilyState';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const RecordShowContainer = ({
objectRecordId,
@ -38,7 +38,7 @@ export const RecordShowContainer = ({
const { makeActivityWithoutConnection } = useActivityConnectionUtils();
useEffect(() => {
if (!loading && isDefined(record)) {
if (!loading && isNonNullable(record)) {
const { activity: activityWithoutConnection } =
makeActivityWithoutConnection(record as any);

View File

@ -13,7 +13,7 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { assertNotNull } from '~/utils/assert';
import { isNonNullable } from '~/utils/isNonNullable';
import { EntityForSelect } from '../types/EntityForSelect';
import { RelationPickerHotkeyScope } from '../types/RelationPickerHotkeyScope';
@ -55,7 +55,7 @@ export const SingleEntitySelectMenuItems = ({
const entitiesInDropdown = [selectedEntity, ...entitiesToSelect].filter(
(entity): entity is EntityForSelect =>
assertNotNull(entity) && isNonEmptyString(entity.name),
isNonNullable(entity) && isNonEmptyString(entity.name),
);
useScopedHotkeys(

View File

@ -6,7 +6,7 @@ import {
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { useEntitySelectSearch } from '../hooks/useEntitySelectSearch';
@ -42,7 +42,7 @@ export const SingleEntitySelectMenuItemsWithSearch = ({
relationPickerScopeId,
});
const showCreateButton = isDefined(onCreate) && searchFilter !== '';
const showCreateButton = isNonNullable(onCreate) && searchFilter !== '';
const entities = useFilteredSearchEntityQuery({
filters: [

View File

@ -1,6 +1,6 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/object-record/constants/DefaultSearchRequestLimit';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { capitalize } from '~/utils/string/capitalize';
export const useLimitPerMetadataItem = ({
@ -15,7 +15,7 @@ export const useLimitPerMetadataItem = ({
.map(({ nameSingular }) => {
return [`limit${capitalize(nameSingular)}`, limit];
})
.filter(isDefined),
.filter(isNonNullable),
);
return {

View File

@ -5,7 +5,7 @@ import { objectMetadataItemsByNamePluralMapSelector } from '@/object-metadata/st
import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier';
import { ObjectRecordForSelect } from '@/object-record/relation-picker/hooks/useMultiObjectSearch';
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export type MultiObjectRecordQueryResult = {
[namePlural: string]: ObjectRecordConnection;
@ -30,7 +30,7 @@ export const useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArr
const objectMetadataItem =
objectMetadataItemsByNamePluralMap.get(namePlural);
if (!isDefined(objectMetadataItem)) return [];
if (!isNonNullable(objectMetadataItem)) return [];
return objectRecordConnection.edges.map(({ node }) => ({
objectMetadataItem,

View File

@ -13,7 +13,7 @@ import {
import { SelectedObjectRecordId } from '@/object-record/relation-picker/hooks/useMultiObjectSearch';
import { useOrderByFieldPerMetadataItem } from '@/object-record/relation-picker/hooks/useOrderByFieldPerMetadataItem';
import { useSearchFilterPerMetadataItem } from '@/object-record/relation-picker/hooks/useSearchFilterPerMetadataItem';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { capitalize } from '~/utils/string/capitalize';
export const useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery = ({
@ -71,7 +71,7 @@ export const useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery = ({
},
];
})
.filter(isDefined),
.filter(isNonNullable),
);
const { orderByFieldPerMetadataItem } = useOrderByFieldPerMetadataItem({
@ -99,7 +99,7 @@ export const useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery = ({
...orderByFieldPerMetadataItem,
...limitPerMetadataItem,
},
skip: !isDefined(multiSelectQueryForSelectedIds),
skip: !isNonNullable(multiSelectQueryForSelectedIds),
},
);

View File

@ -14,7 +14,7 @@ import { SelectedObjectRecordId } from '@/object-record/relation-picker/hooks/us
import { useOrderByFieldPerMetadataItem } from '@/object-record/relation-picker/hooks/useOrderByFieldPerMetadataItem';
import { useSearchFilterPerMetadataItem } from '@/object-record/relation-picker/hooks/useSearchFilterPerMetadataItem';
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { capitalize } from '~/utils/string/capitalize';
export const useMultiObjectSearchMatchesSearchFilterAndToSelectQuery = ({
@ -72,7 +72,7 @@ export const useMultiObjectSearchMatchesSearchFilterAndToSelectQuery = ({
makeAndFilterVariables(searchFilters),
];
})
.filter(isDefined),
.filter(isNonNullable),
);
const { orderByFieldPerMetadataItem } = useOrderByFieldPerMetadataItem({
@ -98,7 +98,7 @@ export const useMultiObjectSearchMatchesSearchFilterAndToSelectQuery = ({
...orderByFieldPerMetadataItem,
...limitPerMetadataItem,
},
skip: !isDefined(multiSelectQuery),
skip: !isNonNullable(multiSelectQuery),
});
const {

View File

@ -12,7 +12,7 @@ import {
} from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { SelectedObjectRecordId } from '@/object-record/relation-picker/hooks/useMultiObjectSearch';
import { useOrderByFieldPerMetadataItem } from '@/object-record/relation-picker/hooks/useOrderByFieldPerMetadataItem';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { capitalize } from '~/utils/string/capitalize';
export const EMPTY_QUERY = gql`
@ -56,7 +56,7 @@ export const useMultiObjectSearchSelectedItemsQuery = ({
},
];
})
.filter(isDefined),
.filter(isNonNullable),
);
const { orderByFieldPerMetadataItem } = useOrderByFieldPerMetadataItem({
@ -83,7 +83,7 @@ export const useMultiObjectSearchSelectedItemsQuery = ({
...orderByFieldPerMetadataItem,
...limitPerMetadataItem,
},
skip: !isDefined(multiSelectQueryForSelectedIds),
skip: !isNonNullable(multiSelectQueryForSelectedIds),
},
);

View File

@ -1,6 +1,6 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { getObjectOrderByField } from '@/object-metadata/utils/getObjectOrderByField';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { capitalize } from '~/utils/string/capitalize';
export const useOrderByFieldPerMetadataItem = ({
@ -20,7 +20,7 @@ export const useOrderByFieldPerMetadataItem = ({
},
];
})
.filter(isDefined),
.filter(isNonNullable),
);
return {

View File

@ -3,7 +3,7 @@ import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/get
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
import { makeOrFilterVariables } from '@/object-record/utils/makeOrFilterVariables';
import { FieldMetadataType } from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const useSearchFilterPerMetadataItem = ({
objectMetadataItems,
@ -64,7 +64,7 @@ export const useSearchFilterPerMetadataItem = ({
return [objectMetadataItem.nameSingular, searchFilter] as const;
})
.filter(isDefined),
.filter(isNonNullable),
);
return {

View File

@ -1,12 +1,15 @@
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const getChildRelationArray = ({
childRelation,
}: {
childRelation: any;
}) => {
if (isDefined(childRelation.edges) && Array.isArray(childRelation.edges)) {
if (
isNonNullable(childRelation.edges) &&
Array.isArray(childRelation.edges)
) {
return childRelation.edges.map((edge: ObjectRecordEdge) => edge.node);
} else {
return childRelation;

View File

@ -1,10 +1,10 @@
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const makeAndFilterVariables = (
filters: (ObjectRecordQueryFilter | undefined)[],
): ObjectRecordQueryFilter | undefined => {
const definedFilters = filters.filter(isDefined);
const definedFilters = filters.filter(isNonNullable);
if (!definedFilters.length) return undefined;

View File

@ -1,10 +1,10 @@
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const makeOrFilterVariables = (
filters: (ObjectRecordQueryFilter | undefined)[],
): ObjectRecordQueryFilter | undefined => {
const definedFilters = filters.filter(isDefined);
const definedFilters = filters.filter(isNonNullable);
if (!definedFilters.length) return undefined;

View File

@ -5,7 +5,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { isFieldRelationValue } from '@/object-record/record-field/types/guards/isFieldRelationValue';
import { sanitizeLink } from '@/object-record/utils/sanitizeLinkRecordInput';
import { FieldMetadataType } from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const sanitizeRecordInput = ({
objectMetadataItem,
@ -39,7 +39,7 @@ export const sanitizeRecordInput = ({
return [fieldName, fieldValue];
})
.filter(isDefined),
.filter(isNonNullable),
);
if (
objectMetadataItem.nameSingular !== CoreObjectNameSingular.Company ||

View File

@ -2,7 +2,7 @@ import { DateTime } from 'luxon';
import { OrderBy } from '@/object-metadata/types/OrderBy';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
const SORT_BEFORE = -1;
const SORT_AFTER = 1;
@ -14,11 +14,11 @@ export const sortObjectRecordByDateField =
const aDate = a[dateField];
const bDate = b[dateField];
if (!isDefined(aDate) && !isDefined(bDate)) {
if (!isNonNullable(aDate) && !isNonNullable(bDate)) {
return SORT_EQUAL;
}
if (!isDefined(aDate)) {
if (!isNonNullable(aDate)) {
if (sortDirection === 'AscNullsFirst') {
return SORT_BEFORE;
} else if (sortDirection === 'DescNullsFirst') {
@ -32,7 +32,7 @@ export const sortObjectRecordByDateField =
throw new Error(`Invalid sortDirection: ${sortDirection}`);
}
if (!isDefined(bDate)) {
if (!isNonNullable(bDate)) {
if (sortDirection === 'AscNullsFirst') {
return SORT_AFTER;
} else if (sortDirection === 'DescNullsFirst') {

View File

@ -9,7 +9,7 @@ import { EntityForSelect } from '@/object-record/relation-picker/types/EntityFor
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
import { makeOrFilterVariables } from '@/object-record/utils/makeOrFilterVariables';
import { assertNotNull } from '~/utils/assert';
import { isNonNullable } from '~/utils/isNonNullable';
type SearchFilter = { fieldNames: string[]; filter: string | number };
@ -104,13 +104,13 @@ export const useFilteredSearchEntityQuery = ({
return {
selectedEntities: selectedRecords
.map(mappingFunction)
.filter(assertNotNull),
.filter(isNonNullable),
filteredSelectedEntities: filteredSelectedRecords
.map(mappingFunction)
.filter(assertNotNull),
.filter(isNonNullable),
entitiesToSelect: recordsToSelect
.map(mappingFunction)
.filter(assertNotNull),
.filter(isNonNullable),
loading:
recordsToSelectLoading ||
filteredSelectedRecordsLoading ||

View File

@ -1,7 +1,7 @@
import styled from '@emotion/styled';
import { RawData } from '@/spreadsheet-import/types';
import { assertNotNull } from '~/utils/assert';
import { isNonNullable } from '~/utils/isNonNullable';
import { Column } from '../MatchColumnsStep';
@ -39,7 +39,7 @@ export const UserTableColumn = <T extends string>({
entries,
}: UserTableColumnProps<T>) => {
const { header } = column;
const entry = entries.find(assertNotNull);
const entry = entries.find(isNonNullable);
return (
<StyledContainer>

View File

@ -5,7 +5,7 @@ import { Key } from 'ts-key-enum';
import { FieldDoubleText } from '@/object-record/record-field/types/FieldDoubleText';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { StyledInput } from './TextInput';
@ -147,7 +147,7 @@ export const DoubleTextInput = ({
secondValue: secondInternalValue,
});
},
enabled: isDefined(onClickOutside),
enabled: isNonNullable(onClickOutside),
});
return (

View File

@ -10,7 +10,7 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
import { ClickOutsideMode } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { useRightDrawer } from '../hooks/useRightDrawer';
import { isRightDrawerExpandedState } from '../states/isRightDrawerExpandedState';
@ -84,7 +84,7 @@ export const RightDrawer = () => {
: theme.rightDrawerWidth
: '0';
if (!isDefined(rightDrawerPage)) {
if (!isNonNullable(rightDrawerPage)) {
return <></>;
}

View File

@ -1,6 +1,6 @@
import { useRecoilCallback } from 'recoil';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { DEFAULT_HOTKEYS_SCOPE_CUSTOM_SCOPES } from '../constants/DefaultHotkeysScopeCustomScopes';
import { currentHotkeyScopeState } from '../states/internal/currentHotkeyScopeState';
@ -30,7 +30,7 @@ export const useSetHotkeyScope = () =>
.valueOrThrow();
if (currentHotkeyScope.scope === hotkeyScopeToSet) {
if (!isDefined(customScopes)) {
if (!isNonNullable(customScopes)) {
if (
isCustomScopesEqual(
currentHotkeyScope?.customScopes,

View File

@ -8,7 +8,7 @@ import {
} from '@/ui/utilities/pointer-event/hooks/useListenClickOutsideV2';
import { ClickOutsideListenerCallback } from '@/ui/utilities/pointer-event/types/ClickOutsideListenerCallback';
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const useClickOutsideListener = (componentId: string) => {
// TODO: improve typing
@ -67,7 +67,7 @@ export const useClickOutsideListener = (componentId: string) => {
(callback) => callback.callbackId === callbackId,
);
if (!isDefined(existingCallbackWithSameId)) {
if (!isNonNullable(existingCallbackWithSameId)) {
const existingCallbacksWithNewCallback = existingCallbacks.concat({
callbackId,
callbackFunction,

View File

@ -1,6 +1,6 @@
import { useContext } from 'react';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
import { ScopeInternalContext } from '../types/ScopeInternalContext';
@ -9,7 +9,7 @@ export const useScopeInternalContextOrThrow = <T extends { scopeId: string }>(
) => {
const context = useContext(Context);
if (!isDefined(context)) {
if (!isNonNullable(context)) {
throw new Error(
`Using a scope context without a ScopeInternalContext.Provider wrapper for context : ${Context.displayName}.`,
);

View File

@ -20,7 +20,7 @@ import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MenuItemDraggable } from '@/ui/navigation/menu-item/components/MenuItemDraggable';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { groupArrayItemsBy } from '~/utils/array/groupArrayItemsBy';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
type ViewFieldsVisibilityDropdownSectionProps = {
fields: Omit<ColumnDefinition<FieldMetadata>, 'size'>[];
@ -69,7 +69,7 @@ export const ViewFieldsVisibilityDropdownSection = ({
Icon: field.isVisible ? IconMinus : IconPlus,
onClick: () => onVisibilityChange(field),
},
].filter(isDefined);
].filter(isNonNullable);
return iconButtons.length ? iconButtons : undefined;
};
@ -134,7 +134,7 @@ export const ViewFieldsVisibilityDropdownSection = ({
/>
)}
</DropdownMenuItemsContainer>
{isDefined(openToolTipIndex) &&
{isNonNullable(openToolTipIndex) &&
createPortal(
<AppTooltip
anchorSelect={`.${title}-${

View File

@ -20,7 +20,7 @@ import { MOBILE_VIEWPORT } from '@/ui/theme/constants/MobileViewport';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { VIEWS_DROPDOWN_ID } from '@/views/constants/ViewsDropdownId';
import { useViewBar } from '@/views/hooks/useViewBar';
import { assertNotNull } from '~/utils/assert';
import { isNonNullable } from '~/utils/isNonNullable';
import { useViewScopedStates } from '../hooks/internal/useViewScopedStates';
@ -161,7 +161,7 @@ export const ViewsDropdownButton = ({
handleDeleteViewButtonClick(event, view.id),
}
: null,
].filter(assertNotNull)}
].filter(isNonNullable)}
onClick={() => handleViewSelect(view.id)}
LeftIcon={IconList}
text={view.name}

View File

@ -14,7 +14,7 @@ import { useGenerateFindManyRecordsQuery } from '@/object-record/hooks/useGenera
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ViewFilter } from '@/views/types/ViewFilter';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { assertNotNull } from '~/utils/assert';
import { isNonNullable } from '~/utils/isNonNullable';
const filterQueryParamsSchema = z.object({
filter: z.record(
@ -142,7 +142,7 @@ export const useFiltersFromQueryParams = () => {
},
),
)
).filter(assertNotNull);
).filter(isNonNullable);
},
[
apolloClient,

View File

@ -7,8 +7,8 @@ import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-i
import { ViewField } from '@/views/types/ViewField';
import { ViewFilter } from '@/views/types/ViewFilter';
import { ViewSort } from '@/views/types/ViewSort';
import { assertNotNull } from '~/utils/assert';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { isNonNullable } from '~/utils/isNonNullable';
import { ViewScopeInternalContext } from '../scopes/scope-internal-context/ViewScopeInternalContext';
import { currentViewFieldsScopedFamilyState } from '../states/currentViewFieldsScopedFamilyState';
@ -116,9 +116,7 @@ export const useViewBar = (props?: UseViewProps) => {
return;
}
const queriedViewFields = viewFields
.map((viewField) => viewField)
.filter(assertNotNull);
const queriedViewFields = viewFields.filter(isNonNullable);
if (isPersistingView) {
return;
@ -173,7 +171,7 @@ export const useViewBar = (props?: UseViewProps) => {
definition: availableFilterDefinition,
};
})
.filter(assertNotNull);
.filter(isNonNullable);
if (!isDeeplyEqual(savedViewFilters, queriedViewFilters)) {
set(savedViewFiltersState, queriedViewFilters);
@ -220,7 +218,7 @@ export const useViewBar = (props?: UseViewProps) => {
definition: availableSortDefinition,
};
})
.filter(assertNotNull);
.filter(isNonNullable);
if (!isDeeplyEqual(savedViewSorts, queriedViewSorts)) {
set(savedViewSortsState, queriedViewSorts);

View File

@ -1,6 +1,6 @@
import { BoardFieldDefinition } from '@/object-record/record-board-deprecated/types/BoardFieldDefinition';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { assertNotNull } from '~/utils/assert';
import { isNonNullable } from '~/utils/isNonNullable';
import { ViewField } from '../types/ViewField';
@ -28,5 +28,5 @@ export const mapViewFieldsToBoardFieldDefinitions = (
}
: null;
})
.filter(assertNotNull);
.filter(isNonNullable);
};

View File

@ -2,7 +2,7 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
import { mapArrayToObject } from '~/utils/array/mapArrayToObject';
import { moveArrayItem } from '~/utils/array/moveArrayItem';
import { assertNotNull } from '~/utils/assert';
import { isNonNullable } from '~/utils/isNonNullable';
import { ViewField } from '../types/ViewField';
@ -48,7 +48,7 @@ export const mapViewFieldsToColumnDefinitions = ({
viewFieldId: viewField.id,
};
})
.filter(assertNotNull);
.filter(isNonNullable);
// No label identifier set for this object
if (!labelIdentifierFieldMetadataId) return columnDefinitionsFromViewFields;

View File

@ -16,7 +16,7 @@ import { ShowPageAddButton } from '@/ui/layout/show-page/components/ShowPageAddB
import { ShowPageMoreButton } from '@/ui/layout/show-page/components/ShowPageMoreButton';
import { PageTitle } from '@/ui/utilities/page-title/PageTitle';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isDefined } from '~/utils/isDefined';
import { isNonNullable } from '~/utils/isNonNullable';
export const RecordShowPage = () => {
const { objectNameSingular, objectRecordId } = useParams<{
@ -55,7 +55,7 @@ export const RecordShowPage = () => {
(favorite) => favorite.recordId === objectRecordId,
);
const isFavorite = isDefined(correspondingFavorite);
const isFavorite = isNonNullable(correspondingFavorite);
const handleFavoriteButtonClick = async () => {
if (!objectNameSingular || !record) return;

View File

@ -1,7 +1,7 @@
import { isObject, isString } from '@sniptt/guards';
import { GraphQLVariables } from 'msw';
import { isDefined } from '../../utils/isDefined';
import { isNonNullable } from '../../utils/isNonNullable';
type StringFilter = {
equals?: string;
@ -133,7 +133,7 @@ export const fetchOneFromData = <DataT extends { id: string }>(
data: Array<DataT>,
id: string,
): DataT | undefined => {
if (!isDefined(id)) {
if (!isNonNullable(id)) {
throw new Error(
`id is not defined in updateOneFromData, check that you provided where.id if needed.`,
);
@ -147,7 +147,7 @@ export const updateOneFromData = <DataT extends { id: string }>(
id: string | undefined,
payload: GraphQLVariables,
): DataT | undefined => {
if (!isDefined(id)) {
if (!isNonNullable(id)) {
throw new Error(
`id is not defined in updateOneFromData, check that you provided where.id if needed.`,
);

View File

@ -1,19 +0,0 @@
import { assertNotNull } from '~/utils/assert';
describe('assert', () => {
it('should return true for a NonNullable value', () => {
expect(assertNotNull(1)).toBeTruthy();
});
it('should return true for a NonNullable value', () => {
expect(assertNotNull('')).toBeTruthy();
});
it('should return false for a null value', () => {
expect(assertNotNull(null)).toBeFalsy();
});
it('should return false for an undefined value', () => {
expect(assertNotNull(undefined)).toBeFalsy();
});
});

View File

@ -1,19 +0,0 @@
import { isDefined } from '~/utils/isDefined';
describe('isDefined', () => {
it('should return true for a NonNullable value', () => {
expect(isDefined(1)).toBe(true);
});
it('should return true for a NonNullable value', () => {
expect(isDefined('')).toBe(true);
});
it('should return false for a null value', () => {
expect(isDefined(null)).toBe(false);
});
it('should return false for an undefined value', () => {
expect(isDefined(undefined)).toBe(false);
});
});

View File

@ -0,0 +1,15 @@
import { isNonNullable } from '~/utils/isNonNullable';
describe('isNonNullable', () => {
it('returns true if value is not undefined nor null', () => {
expect(isNonNullable('')).toBe(true);
});
it('returns false if value is null', () => {
expect(isNonNullable(null)).toBe(false);
});
it('returns false if value is undefined', () => {
expect(isNonNullable(undefined)).toBe(false);
});
});

View File

@ -0,0 +1,30 @@
import { parseApolloStoreFieldName } from '../parseApolloStoreFieldName';
describe('parseApolloStoreFieldName', () => {
it('returns an empty object if string is not a valid store field name', () => {
const result = parseApolloStoreFieldName('////');
expect(result).toEqual({});
});
it('returns the field name and parsed variables if they exist', () => {
const result = parseApolloStoreFieldName('fieldName({"key":"value"})');
expect(result).toEqual({
fieldName: 'fieldName',
fieldVariables: { key: 'value' },
});
});
it('returns only the field name if the variables cannot be parsed', () => {
const result = parseApolloStoreFieldName('fieldName(notJson)');
expect(result).toEqual({
fieldName: 'fieldName',
});
});
it('returns only the field name if there are no variables', () => {
const result = parseApolloStoreFieldName('fieldName');
expect(result).toEqual({
fieldName: 'fieldName',
});
});
});

View File

@ -0,0 +1,35 @@
import { sortAsc, sortDesc, sortNullsFirst, sortNullsLast } from '../sort';
describe('sort', () => {
describe('sortNullsFirst', () => {
it('should sort nulls first', () => {
expect(sortNullsFirst(null, 'a')).toBe(-1);
expect(sortNullsFirst('a', null)).toBe(1);
expect(sortNullsFirst('a', 'a')).toBe(0);
});
});
describe('sortNullsLast', () => {
it('should sort nulls last', () => {
expect(sortNullsLast(null, 'a')).toBe(1);
expect(sortNullsLast('a', null)).toBe(-1);
expect(sortNullsLast('a', 'a')).toBe(0);
});
});
describe('sortAsc', () => {
it('should sort in ascending order', () => {
expect(sortAsc('a', 'b')).toBe(-1);
expect(sortAsc('b', 'a')).toBe(1);
expect(sortAsc('a', 'a')).toBe(0);
});
});
describe('sortDesc', () => {
it('should sort in descending order', () => {
expect(sortDesc('a', 'b')).toBe(1);
expect(sortDesc('b', 'a')).toBe(-1);
expect(sortDesc('a', 'a')).toBe(0);
});
});
});

View File

@ -0,0 +1,25 @@
import { groupArrayItemsBy } from '../groupArrayItemsBy';
describe('groupArrayItemsBy', () => {
it('groups an array of objects by a computed key', () => {
// Given
const array = [
{ id: '1', type: 'fruit', value: 'apple' },
{ id: '2', type: 'fruit', value: 'banana' },
{ id: '3', type: 'vegetable', value: 'carrot' },
];
const computeGroupKey = ({ type }: (typeof array)[0]) => type;
// When
const result = groupArrayItemsBy(array, computeGroupKey);
// Then
expect(result).toEqual({
fruit: [
{ id: '1', type: 'fruit', value: 'apple' },
{ id: '2', type: 'fruit', value: 'banana' },
],
vegetable: [{ id: '3', type: 'vegetable', value: 'carrot' }],
});
});
});

View File

@ -0,0 +1,23 @@
import { mapArrayToObject } from '~/utils/array/mapArrayToObject';
describe('mapArrayToObject', () => {
it('maps an array of objects to an object with computed keys', () => {
// Given
const array = [
{ id: '1', value: 'one' },
{ id: '2', value: 'two' },
{ id: '3', value: 'three' },
];
const computeItemKey = ({ id }: { id: string }) => id;
// When
const result = mapArrayToObject(array, computeItemKey);
// Then
expect(result).toEqual({
'1': { id: '1', value: 'one' },
'2': { id: '2', value: 'two' },
'3': { id: '3', value: 'three' },
});
});
});

View File

@ -1,8 +1,30 @@
export const groupArrayItemsBy = <Item, Key extends string>(
array: Item[],
computeGroupKey: (item: Item) => Key,
/**
* Groups an array of items by a key computed from each item.
*
* @param array - The array to group.
* @param computeGroupKey - A function that computes the group key to which the item belongs.
*
* @returns An object with items grouped by a computed key.
*
* @example
* groupArrayItemsBy(
* [{ id: '1', type: 'fruit' }, { id: '2', type: 'vegetable' }, { id: '3', type: 'fruit' }],
* ({ type }) => type,
* )
* ⬇️
* {
* fruit: [{ id: '1', type: 'fruit' }, { id: '3', type: 'fruit' }],
* vegetable: [{ id: '2', type: 'vegetable' }],
* }
*/
export const groupArrayItemsBy = <
ArrayItem extends Record<string, unknown>,
Key extends string,
>(
array: ArrayItem[],
computeGroupKey: (item: ArrayItem) => Key,
) =>
array.reduce<Partial<Record<Key, Item[]>>>((result, item) => {
array.reduce<Partial<Record<Key, ArrayItem[]>>>((result, item) => {
const groupKey = computeGroupKey(item);
const previousGroup = result[groupKey] || [];

View File

@ -1,4 +1,27 @@
export const mapArrayToObject = <ArrayItem>(
/**
* Transforms an array of items into an object where the keys are computed from each item.
*
* @param array - The array to transform.
* @param computeItemKey - A function that computes a key from an item.
*
* @returns An object where the keys are computed from the items in the array.
*
* @example
* mapArrayToObject(
* [{ id: '1', type: 'fruit' }, { id: '2', type: 'vegetable' }, { id: '3', type: 'fruit' }],
* ({ id }) => id,
* )
* ⬇️
* {
* '1': { id: '1', type: 'fruit' },
* '2': { id: '2', type: 'vegetable' },
* '3': { id: '3', type: 'fruit' },
* }
*/
export const mapArrayToObject = <ArrayItem, Key extends string>(
array: ArrayItem[],
computeItemKey: (item: ArrayItem) => string,
) => Object.fromEntries(array.map((item) => [computeItemKey(item), item]));
computeItemKey: (item: ArrayItem) => Key,
) =>
Object.fromEntries(
array.map((item) => [computeItemKey(item), item]),
) as Record<Key, ArrayItem>;

View File

@ -1,5 +1,19 @@
export const moveArrayItem = <Item>(
array: Item[],
/**
* Moves an item in an array from one index to another.
*
* @param array - The array to move an item in.
* @param indices - The indices to move the item from and to.
* @param indices.fromIndex - The index to move the item from.
* @param indices.toIndex - The index to move the item to.
*
* @returns A new array with the item moved to the new index.
*
* @example
* moveArrayItem(['a', 'b', 'c'], { fromIndex: 0, toIndex: 2 })
* => ['b', 'c', 'a']
*/
export const moveArrayItem = <ArrayItem>(
array: ArrayItem[],
{ fromIndex, toIndex }: { fromIndex: number; toIndex: number },
) => {
if (!(fromIndex in array) || !(toIndex in array) || fromIndex === toIndex) {

View File

@ -1,2 +0,0 @@
export const assertNotNull = <T>(item: T): item is NonNullable<T> =>
item !== null && item !== undefined;

View File

@ -1,7 +1,7 @@
import { isDefined } from './isDefined';
import { isNonNullable } from './isNonNullable';
export const isDomain = (url: string | undefined | null) =>
isDefined(url) &&
isNonNullable(url) &&
/^((?!-))(xn--)?[a-z0-9][a-z0-9-_]{0,61}[a-z0-9]{0,1}\.(xn--)?([a-z0-9-]{1,61}|[a-z0-9-]{1,30}\.[a-z]{2,})$/.test(
url,
);

View File

@ -1,7 +1,7 @@
import { isDefined } from './isDefined';
import { isNonNullable } from './isNonNullable';
export const isURL = (url: string | undefined | null) =>
isDefined(url) &&
isNonNullable(url) &&
url.match(
/^(https?:\/\/)?(www.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-z]{2,63}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/i,
);

View File

@ -1,3 +0,0 @@
export const isDefined = <T>(
value: T | undefined | null,
): value is NonNullable<T> => value !== undefined && value !== null;

View File

@ -0,0 +1,4 @@
import { isNull, isUndefined } from '@sniptt/guards';
export const isNonNullable = <T>(value: T): value is NonNullable<T> =>
!isUndefined(value) && !isNull(value);

View File

@ -8,19 +8,22 @@ export const parseApolloStoreFieldName = <
>(
storeFieldName: string,
) => {
const matches = storeFieldName.match(/([a-zA-Z][a-zA-Z0-9 ]*)\((.*)\)/);
const matches = storeFieldName.match(/([a-zA-Z][a-zA-Z0-9 ]*)(\((.*)\))?/);
if (!matches?.[1]) return {};
const fieldName = matches?.[1];
const [, , stringifiedVariables] = matches;
const fieldName = matches[1] as string;
if (!fieldName) {
return {};
}
const stringifiedVariables = matches[3];
try {
const fieldArguments = stringifiedVariables
const fieldVariables = stringifiedVariables
? (JSON.parse(stringifiedVariables) as Variables)
: undefined;
return { fieldName, fieldArguments };
return { fieldName, fieldVariables };
} catch {
return { fieldName };
}

View File

@ -13,7 +13,7 @@ export const sortNullsLast = (
export const sortAsc = (
fieldValueA: string | number,
fieldValueB: string | number,
) => (fieldValueA < fieldValueB ? -1 : 1);
) => (fieldValueA === fieldValueB ? 0 : fieldValueA < fieldValueB ? -1 : 1);
export const sortDesc = (
fieldValueA: string | number,