mirror of
https://github.com/twentyhq/twenty.git
synced 2024-09-17 19:17:09 +03:00
Recursively turn relation connection into records (#3334)
* Use new ObjectRecordConnection * Use new records without connection in GraphQLView * Added playwright for storybook tests * Fixed lint * Fixed test and tsc * Fixed storybook tests * wip tests * Added useMapConnectionToRecords unit test --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
parent
299bed511f
commit
e2bdf0ce45
@ -241,6 +241,7 @@
|
|||||||
"msw": "^2.0.11",
|
"msw": "^2.0.11",
|
||||||
"msw-storybook-addon": "2.0.0--canary.122.b3ed3b1.0",
|
"msw-storybook-addon": "2.0.0--canary.122.b3ed3b1.0",
|
||||||
"nx": "^17.2.8",
|
"nx": "^17.2.8",
|
||||||
|
"playwright": "^1.40.1",
|
||||||
"prettier": "^3.1.1",
|
"prettier": "^3.1.1",
|
||||||
"raw-loader": "^4.0.2",
|
"raw-loader": "^4.0.2",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
|
@ -9,14 +9,16 @@
|
|||||||
"start:clean": "yarn start --force",
|
"start:clean": "yarn start --force",
|
||||||
"build": "tsc && vite build && yarn build:inject-runtime-env",
|
"build": "tsc && vite build && yarn build:inject-runtime-env",
|
||||||
"build:inject-runtime-env": "sh ./scripts/inject-runtime-env.sh",
|
"build:inject-runtime-env": "sh ./scripts/inject-runtime-env.sh",
|
||||||
|
"tsc:spec": "tsc --project tsconfig.spec.json --noEmit",
|
||||||
"tsc": "tsc --project tsconfig.app.json --watch",
|
"tsc": "tsc --project tsconfig.app.json --watch",
|
||||||
"tsc:ci": "tsc --project tsconfig.app.json",
|
"tsc:ci": "tsc --project tsconfig.app.json --noEmit && tsc --project tsconfig.spec.json --noEmit && tsc --project tsconfig.node.json --noEmit",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"lint": "eslint . --report-unused-disable-directives --max-warnings 0 --config .eslintrc.cjs",
|
"lint": "eslint . --report-unused-disable-directives --max-warnings 0 --config .eslintrc.cjs",
|
||||||
"lint:ci": "yarn lint --config .eslintrc-ci.cjs",
|
"lint:ci": "yarn lint --config .eslintrc-ci.cjs",
|
||||||
"fmt:fix": "prettier --cache --write \"src/**/*.ts\" \"src/**/*.tsx\"",
|
"fmt:fix": "prettier --cache --write \"src/**/*.ts\" \"src/**/*.tsx\"",
|
||||||
"fmt": "prettier --check \"src/**/*.ts\" \"src/**/*.tsx\"",
|
"fmt": "prettier --check \"src/**/*.ts\" \"src/**/*.tsx\"",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
|
"test-watch": "jest --coverage=false --watch",
|
||||||
"tsup": "tsup",
|
"tsup": "tsup",
|
||||||
"coverage": "jest --coverage",
|
"coverage": "jest --coverage",
|
||||||
"storybook:modules:dev": "STORYBOOK_SCOPE=modules yarn storybook:dev",
|
"storybook:modules:dev": "STORYBOOK_SCOPE=modules yarn storybook:dev",
|
||||||
@ -29,6 +31,7 @@
|
|||||||
"storybook:docs:build": "STORYBOOK_SCOPE=ui-docs yarn storybook:build",
|
"storybook:docs:build": "STORYBOOK_SCOPE=ui-docs yarn storybook:build",
|
||||||
"storybook:test": "test-storybook",
|
"storybook:test": "test-storybook",
|
||||||
"storybook:test-slow": "test-storybook --maxWorkers=3",
|
"storybook:test-slow": "test-storybook --maxWorkers=3",
|
||||||
|
"storybook:test-single-worker": "test-storybook --maxWorkers=1",
|
||||||
"storybook:coverage": "yarn storybook:test-slow --coverage && npx nyc report --reporter=lcov -t coverage/storybook --report-dir coverage/storybook --check-coverage",
|
"storybook:coverage": "yarn storybook:test-slow --coverage && npx nyc report --reporter=lcov -t coverage/storybook --report-dir coverage/storybook --check-coverage",
|
||||||
"storybook:modules:coverage": "STORYBOOK_SCOPE=modules yarn storybook:coverage",
|
"storybook:modules:coverage": "STORYBOOK_SCOPE=modules yarn storybook:coverage",
|
||||||
"storybook:pages:coverage": "STORYBOOK_SCOPE=pages yarn storybook:coverage",
|
"storybook:pages:coverage": "STORYBOOK_SCOPE=pages yarn storybook:coverage",
|
||||||
|
@ -4,7 +4,7 @@ import { produce } from 'immer';
|
|||||||
import { OptimisticEffectDefinition } from '@/apollo/optimistic-effect/types/OptimisticEffectDefinition';
|
import { OptimisticEffectDefinition } from '@/apollo/optimistic-effect/types/OptimisticEffectDefinition';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { isRecordMatchingFilter } from '@/object-record/record-filter/utils/isRecordMatchingFilter';
|
import { isRecordMatchingFilter } from '@/object-record/record-filter/utils/isRecordMatchingFilter';
|
||||||
import { PaginatedRecordTypeResults } from '@/object-record/types/PaginatedRecordTypeResults';
|
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
import { capitalize } from '~/utils/string/capitalize';
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
@ -21,9 +21,9 @@ export const getRecordOptimisticEffectDefinition = ({
|
|||||||
deletedRecordIds,
|
deletedRecordIds,
|
||||||
variables,
|
variables,
|
||||||
}) => {
|
}) => {
|
||||||
const newRecordPaginatedCacheField = produce<
|
const newRecordPaginatedCacheField = produce<ObjectRecordConnection<any>>(
|
||||||
PaginatedRecordTypeResults<any>
|
currentData as ObjectRecordConnection<any>,
|
||||||
>(currentData as PaginatedRecordTypeResults<any>, (draft) => {
|
(draft) => {
|
||||||
const existingDataIsEmpty = !draft || !draft.edges || !draft.edges[0];
|
const existingDataIsEmpty = !draft || !draft.edges || !draft.edges[0];
|
||||||
|
|
||||||
if (isNonEmptyArray(createdRecords)) {
|
if (isNonEmptyArray(createdRecords)) {
|
||||||
@ -55,7 +55,9 @@ export const getRecordOptimisticEffectDefinition = ({
|
|||||||
draft.edges.unshift({
|
draft.edges.unshift({
|
||||||
node: createdRecord,
|
node: createdRecord,
|
||||||
cursor: '',
|
cursor: '',
|
||||||
__typename: `${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
__typename: `${capitalize(
|
||||||
|
objectMetadataItem.nameSingular,
|
||||||
|
)}Edge`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,7 +103,8 @@ export const getRecordOptimisticEffectDefinition = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return newRecordPaginatedCacheField;
|
return newRecordPaginatedCacheField;
|
||||||
},
|
},
|
||||||
|
@ -0,0 +1,752 @@
|
|||||||
|
import { Company } from '@/companies/types/Company';
|
||||||
|
import { Favorite } from '@/favorites/types/Favorite';
|
||||||
|
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
||||||
|
import { Person } from '@/people/types/Person';
|
||||||
|
|
||||||
|
export const emptyConnectionMock: ObjectRecordConnection = {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: '',
|
||||||
|
endCursor: '',
|
||||||
|
},
|
||||||
|
__typename: 'ObjectRecordConnection',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const companiesConnectionWithPeopleConnectionWithFavoritesConnectionMock: ObjectRecordConnection<
|
||||||
|
Partial<Company> &
|
||||||
|
Pick<Company, 'id'> & {
|
||||||
|
people: ObjectRecordConnection<
|
||||||
|
Pick<Person, 'id' | 'name'> & {
|
||||||
|
favorites: ObjectRecordConnection<
|
||||||
|
Pick<Favorite, 'id' | 'personId' | 'companyId' | 'position'>
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
> = {
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: 'WyJmZTI1NmIzOS0zZWMzLTRmZTMtODk5Ny1iNzZhYTBiZmE0MDgiXQ==',
|
||||||
|
hasNextPage: true,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: 'WyIwNGIyZTlmNS0wNzEzLTQwYTUtODIxNi04MjgwMjQwMWQzM2UiXQ==',
|
||||||
|
},
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
cursor: 'WyIwNGIyZTlmNS0wNzEzLTQwYTUtODIxNi04MjgwMjQwMWQzM2UiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '04b2e9f5-0713-40a5-8216-82802401d33e',
|
||||||
|
name: 'Qonto',
|
||||||
|
people: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyIwZDk0MDk5Ny1jMjFlLTRlYzItODczYi1kZTQyNjRkODkwMjUiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '0d940997-c21e-4ec2-873b-de4264d89025',
|
||||||
|
name: 'Google',
|
||||||
|
people: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
cursor:
|
||||||
|
'WyIyNDBkYTJlYy0yZDQwLTRlNDktOGRmNC05YzZhMDQ5MTkwZGYiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '240da2ec-2d40-4e49-8df4-9c6a049190df',
|
||||||
|
name: {
|
||||||
|
firstName: 'Bertrand',
|
||||||
|
lastName: 'Voulzy',
|
||||||
|
},
|
||||||
|
favorites: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
cursor:
|
||||||
|
'WyJjODVhODY3Yy01YThmLTQ4NjEtOGVkMi05NmMzOTAyNDg0MjMiXQ==',
|
||||||
|
node: {
|
||||||
|
id: 'c85a867c-5a8f-4861-8ed2-96c390248423',
|
||||||
|
personId: '240da2ec-2d40-4e49-8df4-9c6a049190df',
|
||||||
|
companyId: null,
|
||||||
|
position: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor:
|
||||||
|
'WyJjODVhODY3Yy01YThmLTQ4NjEtOGVkMi05NmMzOTAyNDg0MjMiXQ==',
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor:
|
||||||
|
'WyJjODVhODY3Yy01YThmLTQ4NjEtOGVkMi05NmMzOTAyNDg0MjMiXQ==',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor:
|
||||||
|
'WyIyNDBkYTJlYy0yZDQwLTRlNDktOGRmNC05YzZhMDQ5MTkwZWYiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '240da2ec-2d40-4e49-8df4-9c6a049190ef',
|
||||||
|
name: {
|
||||||
|
firstName: 'Madison',
|
||||||
|
lastName: 'Perez',
|
||||||
|
},
|
||||||
|
favorites: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor:
|
||||||
|
'WyI1Njk1NTQyMi01ZDU0LTQxYjctYmEzNi1mMGQyMGUxNDE3YWUiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '56955422-5d54-41b7-ba36-f0d20e1417ae',
|
||||||
|
name: {
|
||||||
|
firstName: 'Avery',
|
||||||
|
lastName: 'Carter',
|
||||||
|
},
|
||||||
|
favorites: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor:
|
||||||
|
'WyI3NTUwMzVkYi02MjNkLTQxZmUtOTJlNy1kZDQ1YjdjNTY4ZTEiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '755035db-623d-41fe-92e7-dd45b7c568e1',
|
||||||
|
name: {
|
||||||
|
firstName: 'Ethan',
|
||||||
|
lastName: 'Mitchell',
|
||||||
|
},
|
||||||
|
favorites: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor:
|
||||||
|
'WyJhMmU3OGE1Zi0zMzhiLTQ2ZGYtODgxMS1mYTA4YzdkMTlkMzUiXQ==',
|
||||||
|
node: {
|
||||||
|
id: 'a2e78a5f-338b-46df-8811-fa08c7d19d35',
|
||||||
|
name: {
|
||||||
|
firstName: 'Elizabeth',
|
||||||
|
lastName: 'Baker',
|
||||||
|
},
|
||||||
|
favorites: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor:
|
||||||
|
'WyJjYTFmNWJmMy02NGFkLTRiMGUtYmJmZC1lOWZkNzk1YjcwMTYiXQ==',
|
||||||
|
node: {
|
||||||
|
id: 'ca1f5bf3-64ad-4b0e-bbfd-e9fd795b7016',
|
||||||
|
name: {
|
||||||
|
firstName: 'Christopher',
|
||||||
|
lastName: 'Nelson',
|
||||||
|
},
|
||||||
|
favorites: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor:
|
||||||
|
'WyJjYTFmNWJmMy02NGFkLTRiMGUtYmJmZC1lOWZkNzk1YjcwMTYiXQ==',
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor:
|
||||||
|
'WyIyNDBkYTJlYy0yZDQwLTRlNDktOGRmNC05YzZhMDQ5MTkwZGYiXQ==',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyIxMTg5OTVmMy01ZDgxLTQ2ZDYtYmY4My1mN2ZkMzNlYTYxMDIiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '118995f3-5d81-46d6-bf83-f7fd33ea6102',
|
||||||
|
name: 'Facebook',
|
||||||
|
people: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
cursor:
|
||||||
|
'WyI5M2M3MmQyZS1mNTE3LTQyZmQtODBhZS0xNDE3M2IzYjcwYWUiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '93c72d2e-f517-42fd-80ae-14173b3b70ae',
|
||||||
|
name: {
|
||||||
|
firstName: 'Christopher',
|
||||||
|
lastName: 'Gonzalez',
|
||||||
|
},
|
||||||
|
favorites: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor:
|
||||||
|
'WyJlZWVhY2FjZi1lZWUxLTQ2OTAtYWQyYy04NjE5ZTViNTZhMmUiXQ==',
|
||||||
|
node: {
|
||||||
|
id: 'eeeacacf-eee1-4690-ad2c-8619e5b56a2e',
|
||||||
|
name: {
|
||||||
|
firstName: 'Ashley',
|
||||||
|
lastName: 'Parker',
|
||||||
|
},
|
||||||
|
favorites: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor:
|
||||||
|
'WyJlZWVhY2FjZi1lZWUxLTQ2OTAtYWQyYy04NjE5ZTViNTZhMmUiXQ==',
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor:
|
||||||
|
'WyI5M2M3MmQyZS1mNTE3LTQyZmQtODBhZS0xNDE3M2IzYjcwYWUiXQ==',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyIxZDNhMWM2ZS03MDdlLTQ0ZGMtYTFkMi0zMDAzMGJmMWE5NDQiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '1d3a1c6e-707e-44dc-a1d2-30030bf1a944',
|
||||||
|
name: 'Netflix',
|
||||||
|
people: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyI0NjBiNmZiMS1lZDg5LTQxM2EtYjMxYS05NjI5ODZlNjdiYjQiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '460b6fb1-ed89-413a-b31a-962986e67bb4',
|
||||||
|
name: 'Microsoft',
|
||||||
|
people: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
cursor:
|
||||||
|
'WyIxZDE1MTg1Mi00OTBmLTQ0NjYtODM5MS03MzNjZmQ2NmEwYzgiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '1d151852-490f-4466-8391-733cfd66a0c8',
|
||||||
|
name: {
|
||||||
|
firstName: 'Isabella',
|
||||||
|
lastName: 'Scott',
|
||||||
|
},
|
||||||
|
favorites: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor:
|
||||||
|
'WyI5ODQwNmUyNi04MGYxLTRkZmYtYjU3MC1hNzQ5NDI1MjhkZTMiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '98406e26-80f1-4dff-b570-a74942528de3',
|
||||||
|
name: {
|
||||||
|
firstName: 'Matthew',
|
||||||
|
lastName: 'Green',
|
||||||
|
},
|
||||||
|
favorites: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor:
|
||||||
|
'WyI5YjMyNGE4OC02Nzg0LTQ0NDktYWZkZi1kYzYyY2I4NzAyZjIiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '9b324a88-6784-4449-afdf-dc62cb8702f2',
|
||||||
|
name: {
|
||||||
|
firstName: 'Nicholas',
|
||||||
|
lastName: 'Wright',
|
||||||
|
},
|
||||||
|
favorites: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor:
|
||||||
|
'WyI5YjMyNGE4OC02Nzg0LTQ0NDktYWZkZi1kYzYyY2I4NzAyZjIiXQ==',
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor:
|
||||||
|
'WyIxZDE1MTg1Mi00OTBmLTQ0NjYtODM5MS03MzNjZmQ2NmEwYzgiXQ==',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyI3YTkzZDFlNS0zZjc0LTQ5MmQtYTEwMS0yYTcwZjUwYTE2NDUiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '7a93d1e5-3f74-492d-a101-2a70f50a1645',
|
||||||
|
name: 'Libeo',
|
||||||
|
people: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyI4OWJiODI1Yy0xNzFlLTRiY2MtOWNmNy00MzQ0OGQ2ZmIyNzgiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
|
||||||
|
name: 'Airbnb',
|
||||||
|
people: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyI5ZDE2MmRlNi1jZmJmLTQxNTYtYTc5MC1lMzk4NTRkY2Q0ZWIiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '9d162de6-cfbf-4156-a790-e39854dcd4eb',
|
||||||
|
name: 'Claap',
|
||||||
|
people: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyJhNjc0ZmE2Yy0xNDU1LTRjNTctYWZhZi1kZDVkYzA4NjM2MWQiXQ==',
|
||||||
|
node: {
|
||||||
|
id: 'a674fa6c-1455-4c57-afaf-dd5dc086361d',
|
||||||
|
name: 'Algolia',
|
||||||
|
people: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
cursor:
|
||||||
|
'WyIyNDBkYTJlYy0yZDQwLTRlNDktOGRmNC05YzZhMDQ5MTkxZGYiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '240da2ec-2d40-4e49-8df4-9c6a049191df',
|
||||||
|
name: {
|
||||||
|
firstName: 'Lorie',
|
||||||
|
lastName: 'Vladim',
|
||||||
|
},
|
||||||
|
favorites: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor:
|
||||||
|
'WyIyNDBkYTJlYy0yZDQwLTRlNDktOGRmNC05YzZhMDQ5MTkxZGYiXQ==',
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor:
|
||||||
|
'WyIyNDBkYTJlYy0yZDQwLTRlNDktOGRmNC05YzZhMDQ5MTkxZGYiXQ==',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyJhN2JjNjhkNS1mNzllLTQwZGQtYmQwNi1jMzZlNmFiYjQ2NzgiXQ==',
|
||||||
|
node: {
|
||||||
|
id: 'a7bc68d5-f79e-40dd-bd06-c36e6abb4678',
|
||||||
|
name: 'Samsung',
|
||||||
|
people: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
cursor:
|
||||||
|
'WyIyNDBkYTJlYy0yZDQwLTRlNDktOGRmNC05YzZhMDQ5MTkxZGUiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '240da2ec-2d40-4e49-8df4-9c6a049191de',
|
||||||
|
name: {
|
||||||
|
firstName: 'Louis',
|
||||||
|
lastName: 'Duss',
|
||||||
|
},
|
||||||
|
favorites: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor:
|
||||||
|
'WyIyNDBkYTJlYy0yZDQwLTRlNDktOGRmNC05YzZhMDQ5MTkxZGUiXQ==',
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor:
|
||||||
|
'WyIyNDBkYTJlYy0yZDQwLTRlNDktOGRmNC05YzZhMDQ5MTkxZGUiXQ==',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyJhYWZmY2ZiZC1mODZiLTQxOWYtYjc5NC0wMjMxOWFiZTg2MzciXQ==',
|
||||||
|
node: {
|
||||||
|
id: 'aaffcfbd-f86b-419f-b794-02319abe8637',
|
||||||
|
name: 'Hasura',
|
||||||
|
people: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyJmMzNkYzI0Mi01NTE4LTQ1NTMtOTQzMy00MmQ4ZWI4MjgzNGIiXQ==',
|
||||||
|
node: {
|
||||||
|
id: 'f33dc242-5518-4553-9433-42d8eb82834b',
|
||||||
|
name: 'Wework',
|
||||||
|
people: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyJmZTI1NmIzOS0zZWMzLTRmZTMtODk5Ny1iNzZhYTBiZmE0MDgiXQ==',
|
||||||
|
node: {
|
||||||
|
id: 'fe256b39-3ec3-4fe3-8997-b76aa0bfa408',
|
||||||
|
name: 'Linkedin',
|
||||||
|
people: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
cursor:
|
||||||
|
'WyIwYWEwMGJlYi1hYzczLTQ3OTctODI0ZS04N2ExZjVhZWE5ZTAiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '0aa00beb-ac73-4797-824e-87a1f5aea9e0',
|
||||||
|
name: {
|
||||||
|
firstName: 'Sylvie',
|
||||||
|
lastName: 'Palmer',
|
||||||
|
},
|
||||||
|
favorites: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
cursor:
|
||||||
|
'WyIzN2I5NzE0MC0yNmI5LTQ5OGMtODM3Yi00ZjNkZTQ5OWFkODMiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '37b97140-26b9-498c-837b-4f3de499ad83',
|
||||||
|
personId: '0aa00beb-ac73-4797-824e-87a1f5aea9e0',
|
||||||
|
companyId: null,
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor:
|
||||||
|
'WyIzN2I5NzE0MC0yNmI5LTQ5OGMtODM3Yi00ZjNkZTQ5OWFkODMiXQ==',
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor:
|
||||||
|
'WyIzN2I5NzE0MC0yNmI5LTQ5OGMtODM3Yi00ZjNkZTQ5OWFkODMiXQ==',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor:
|
||||||
|
'WyI4NjA4MzE0MS0xYzBlLTQ5NGMtYTFiNi04NWIxYzZmZWZhYTUiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '86083141-1c0e-494c-a1b6-85b1c6fefaa5',
|
||||||
|
name: {
|
||||||
|
firstName: 'Christoph',
|
||||||
|
lastName: 'Callisto',
|
||||||
|
},
|
||||||
|
favorites: {
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: null,
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor:
|
||||||
|
'WyI4NjA4MzE0MS0xYzBlLTQ5NGMtYTFiNi04NWIxYzZmZWZhYTUiXQ==',
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor:
|
||||||
|
'WyIwYWEwMGJlYi1hYzczLTQ3OTctODI0ZS04N2ExZjVhZWE5ZTAiXQ==',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const peopleWithTheirUniqueCompanies: ObjectRecordConnection<
|
||||||
|
Pick<Person, 'id'> & { company: Pick<Company, 'id' | 'name'> }
|
||||||
|
> = {
|
||||||
|
pageInfo: {
|
||||||
|
endCursor: 'WyJlZWVhY2FjZi1lZWUxLTQ2OTAtYWQyYy04NjE5ZTViNTZhMmUiXQ==',
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: 'WyIwYWEwMGJlYi1hYzczLTQ3OTctODI0ZS04N2ExZjVhZWE5ZTAiXQ==',
|
||||||
|
},
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
cursor: 'WyIwYWEwMGJlYi1hYzczLTQ3OTctODI0ZS04N2ExZjVhZWE5ZTAiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '0aa00beb-ac73-4797-824e-87a1f5aea9e0',
|
||||||
|
company: {
|
||||||
|
id: 'fe256b39-3ec3-4fe3-8997-b76aa0bfa408',
|
||||||
|
name: 'Linkedin',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyIxZDE1MTg1Mi00OTBmLTQ0NjYtODM5MS03MzNjZmQ2NmEwYzgiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '1d151852-490f-4466-8391-733cfd66a0c8',
|
||||||
|
company: {
|
||||||
|
id: '460b6fb1-ed89-413a-b31a-962986e67bb4',
|
||||||
|
name: 'Microsoft',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyIyNDBkYTJlYy0yZDQwLTRlNDktOGRmNC05YzZhMDQ5MTkwZGYiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '240da2ec-2d40-4e49-8df4-9c6a049190df',
|
||||||
|
company: {
|
||||||
|
id: '0d940997-c21e-4ec2-873b-de4264d89025',
|
||||||
|
name: 'Google',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyIyNDBkYTJlYy0yZDQwLTRlNDktOGRmNC05YzZhMDQ5MTkwZWYiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '240da2ec-2d40-4e49-8df4-9c6a049190ef',
|
||||||
|
company: {
|
||||||
|
id: '0d940997-c21e-4ec2-873b-de4264d89025',
|
||||||
|
name: 'Google',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyIyNDBkYTJlYy0yZDQwLTRlNDktOGRmNC05YzZhMDQ5MTkxZGUiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '240da2ec-2d40-4e49-8df4-9c6a049191de',
|
||||||
|
company: {
|
||||||
|
id: 'a7bc68d5-f79e-40dd-bd06-c36e6abb4678',
|
||||||
|
name: 'Samsung',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyIyNDBkYTJlYy0yZDQwLTRlNDktOGRmNC05YzZhMDQ5MTkxZGYiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '240da2ec-2d40-4e49-8df4-9c6a049191df',
|
||||||
|
company: {
|
||||||
|
id: 'a674fa6c-1455-4c57-afaf-dd5dc086361d',
|
||||||
|
name: 'Algolia',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyI1Njk1NTQyMi01ZDU0LTQxYjctYmEzNi1mMGQyMGUxNDE3YWUiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '56955422-5d54-41b7-ba36-f0d20e1417ae',
|
||||||
|
company: {
|
||||||
|
id: '0d940997-c21e-4ec2-873b-de4264d89025',
|
||||||
|
name: 'Google',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyI3NTUwMzVkYi02MjNkLTQxZmUtOTJlNy1kZDQ1YjdjNTY4ZTEiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '755035db-623d-41fe-92e7-dd45b7c568e1',
|
||||||
|
company: {
|
||||||
|
id: '0d940997-c21e-4ec2-873b-de4264d89025',
|
||||||
|
name: 'Google',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyI4NjA4MzE0MS0xYzBlLTQ5NGMtYTFiNi04NWIxYzZmZWZhYTUiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '86083141-1c0e-494c-a1b6-85b1c6fefaa5',
|
||||||
|
company: {
|
||||||
|
id: 'fe256b39-3ec3-4fe3-8997-b76aa0bfa408',
|
||||||
|
name: 'Linkedin',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyI5M2M3MmQyZS1mNTE3LTQyZmQtODBhZS0xNDE3M2IzYjcwYWUiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '93c72d2e-f517-42fd-80ae-14173b3b70ae',
|
||||||
|
company: {
|
||||||
|
id: '118995f3-5d81-46d6-bf83-f7fd33ea6102',
|
||||||
|
name: 'Facebook',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyI5ODQwNmUyNi04MGYxLTRkZmYtYjU3MC1hNzQ5NDI1MjhkZTMiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '98406e26-80f1-4dff-b570-a74942528de3',
|
||||||
|
company: {
|
||||||
|
id: '460b6fb1-ed89-413a-b31a-962986e67bb4',
|
||||||
|
name: 'Microsoft',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyI5YjMyNGE4OC02Nzg0LTQ0NDktYWZkZi1kYzYyY2I4NzAyZjIiXQ==',
|
||||||
|
node: {
|
||||||
|
id: '9b324a88-6784-4449-afdf-dc62cb8702f2',
|
||||||
|
company: {
|
||||||
|
id: '460b6fb1-ed89-413a-b31a-962986e67bb4',
|
||||||
|
name: 'Microsoft',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyJhMmU3OGE1Zi0zMzhiLTQ2ZGYtODgxMS1mYTA4YzdkMTlkMzUiXQ==',
|
||||||
|
node: {
|
||||||
|
id: 'a2e78a5f-338b-46df-8811-fa08c7d19d35',
|
||||||
|
company: {
|
||||||
|
id: '0d940997-c21e-4ec2-873b-de4264d89025',
|
||||||
|
name: 'Google',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyJjYTFmNWJmMy02NGFkLTRiMGUtYmJmZC1lOWZkNzk1YjcwMTYiXQ==',
|
||||||
|
node: {
|
||||||
|
id: 'ca1f5bf3-64ad-4b0e-bbfd-e9fd795b7016',
|
||||||
|
company: {
|
||||||
|
id: '0d940997-c21e-4ec2-873b-de4264d89025',
|
||||||
|
name: 'Google',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cursor: 'WyJlZWVhY2FjZi1lZWUxLTQ2OTAtYWQyYy04NjE5ZTViNTZhMmUiXQ==',
|
||||||
|
node: {
|
||||||
|
id: 'eeeacacf-eee1-4690-ad2c-8619e5b56a2e',
|
||||||
|
company: {
|
||||||
|
id: '118995f3-5d81-46d6-bf83-f7fd33ea6102',
|
||||||
|
name: 'Facebook',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
@ -0,0 +1,190 @@
|
|||||||
|
import { isNonEmptyArray } from '@sniptt/guards';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
|
||||||
|
import { Company } from '@/companies/types/Company';
|
||||||
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||||
|
import {
|
||||||
|
companiesConnectionWithPeopleConnectionWithFavoritesConnectionMock,
|
||||||
|
emptyConnectionMock,
|
||||||
|
peopleWithTheirUniqueCompanies,
|
||||||
|
} from '@/object-record/hooks/__mocks__/useMapConnectionToRecords';
|
||||||
|
import { useMapConnectionToRecords } from '@/object-record/hooks/useMapConnectionToRecords';
|
||||||
|
import { Person } from '@/people/types/Person';
|
||||||
|
import { getJestHookWrapper } from '~/testing/jest/getJestHookWrapper';
|
||||||
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
|
const Wrapper = getJestHookWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
onInitializeRecoilSnapshot: (snapshot) => {
|
||||||
|
snapshot.set(objectMetadataItemsState, getObjectMetadataItemsMock());
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useMapConnectionToRecords', () => {
|
||||||
|
it('Empty edges - should return an empty array if no edge', async () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const mapConnectionToRecords = useMapConnectionToRecords();
|
||||||
|
|
||||||
|
const records = mapConnectionToRecords({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.Company,
|
||||||
|
objectRecordConnection: emptyConnectionMock,
|
||||||
|
depth: 5,
|
||||||
|
});
|
||||||
|
|
||||||
|
return records;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper: Wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(Array.isArray(result.current)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('No relation fields - should return an array of company records', async () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const mapConnectionToRecords = useMapConnectionToRecords();
|
||||||
|
|
||||||
|
const records = mapConnectionToRecords({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.Company,
|
||||||
|
objectRecordConnection:
|
||||||
|
companiesConnectionWithPeopleConnectionWithFavoritesConnectionMock,
|
||||||
|
depth: 5,
|
||||||
|
});
|
||||||
|
|
||||||
|
return records;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper: Wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(Array.isArray(result.current)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('n+1 relation fields - should return an array of company records with their people records', async () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const mapConnectionToRecords = useMapConnectionToRecords();
|
||||||
|
|
||||||
|
const records = mapConnectionToRecords({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.Company,
|
||||||
|
objectRecordConnection:
|
||||||
|
companiesConnectionWithPeopleConnectionWithFavoritesConnectionMock,
|
||||||
|
depth: 5,
|
||||||
|
});
|
||||||
|
|
||||||
|
return records;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper: Wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const secondCompanyMock =
|
||||||
|
companiesConnectionWithPeopleConnectionWithFavoritesConnectionMock
|
||||||
|
.edges[1];
|
||||||
|
|
||||||
|
const secondCompanyPeopleMock = secondCompanyMock.node.people.edges.map(
|
||||||
|
(edge) => edge.node,
|
||||||
|
);
|
||||||
|
|
||||||
|
const companiesResult = result.current;
|
||||||
|
const secondCompanyResult = result.current[1];
|
||||||
|
const secondCompanyPeopleResult = secondCompanyResult.people;
|
||||||
|
|
||||||
|
expect(isNonEmptyArray(companiesResult)).toBe(true);
|
||||||
|
expect(secondCompanyResult.id).toBe(secondCompanyMock.node.id);
|
||||||
|
expect(isNonEmptyArray(secondCompanyPeopleResult)).toBe(true);
|
||||||
|
expect(secondCompanyPeopleResult[0].id).toEqual(
|
||||||
|
secondCompanyPeopleMock[0].id,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('n+2 relation fields - should return an array of company records with their people records with their favorites records', async () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const mapConnectionToRecords = useMapConnectionToRecords();
|
||||||
|
|
||||||
|
const records = mapConnectionToRecords({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.Company,
|
||||||
|
objectRecordConnection:
|
||||||
|
companiesConnectionWithPeopleConnectionWithFavoritesConnectionMock,
|
||||||
|
depth: 5,
|
||||||
|
});
|
||||||
|
|
||||||
|
return records;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper: Wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const secondCompanyMock =
|
||||||
|
companiesConnectionWithPeopleConnectionWithFavoritesConnectionMock
|
||||||
|
.edges[1];
|
||||||
|
|
||||||
|
const secondCompanyPeopleMock = secondCompanyMock.node.people;
|
||||||
|
|
||||||
|
const secondCompanyFirstPersonMock = secondCompanyPeopleMock.edges[0].node;
|
||||||
|
|
||||||
|
const secondCompanyFirstPersonFavoritesMock =
|
||||||
|
secondCompanyFirstPersonMock.favorites;
|
||||||
|
|
||||||
|
const companiesResult = result.current;
|
||||||
|
const secondCompanyResult = companiesResult[1];
|
||||||
|
const secondCompanyPeopleResult = secondCompanyResult.people;
|
||||||
|
const secondCompanyFirstPersonResult = secondCompanyPeopleResult[0];
|
||||||
|
const secondCompanyFirstPersonFavoritesResult =
|
||||||
|
secondCompanyFirstPersonResult.favorites;
|
||||||
|
|
||||||
|
expect(isNonEmptyArray(companiesResult)).toBe(true);
|
||||||
|
expect(secondCompanyResult.id).toBe(secondCompanyMock.node.id);
|
||||||
|
expect(isNonEmptyArray(secondCompanyPeopleResult)).toBe(true);
|
||||||
|
expect(secondCompanyFirstPersonResult.id).toEqual(
|
||||||
|
secondCompanyFirstPersonMock.id,
|
||||||
|
);
|
||||||
|
expect(isNonEmptyArray(secondCompanyFirstPersonFavoritesResult)).toBe(true);
|
||||||
|
expect(secondCompanyFirstPersonFavoritesResult[0].id).toEqual(
|
||||||
|
secondCompanyFirstPersonFavoritesMock.edges[0].node.id,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("n+1 relation field TO_ONE_OBJECT - should return an array of people records with their company, mapConnectionToRecords shouldn't try to parse TO_ONE_OBJECT", async () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const mapConnectionToRecords = useMapConnectionToRecords();
|
||||||
|
|
||||||
|
const records = mapConnectionToRecords({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.Person,
|
||||||
|
objectRecordConnection: peopleWithTheirUniqueCompanies,
|
||||||
|
depth: 5,
|
||||||
|
});
|
||||||
|
|
||||||
|
return records as (Person & { company: Company })[];
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper: Wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const firstPersonMock = peopleWithTheirUniqueCompanies.edges[0].node;
|
||||||
|
|
||||||
|
const firstPersonsCompanyMock = firstPersonMock.company;
|
||||||
|
|
||||||
|
const peopleResult = result.current;
|
||||||
|
|
||||||
|
const firstPersonResult = result.current[0];
|
||||||
|
const firstPersonsCompanyresult = firstPersonResult.company;
|
||||||
|
|
||||||
|
expect(isNonEmptyArray(peopleResult)).toBe(true);
|
||||||
|
expect(firstPersonResult.id).toBe(firstPersonMock.id);
|
||||||
|
|
||||||
|
expect(isDefined(firstPersonsCompanyresult)).toBe(true);
|
||||||
|
expect(firstPersonsCompanyresult.id).toEqual(firstPersonsCompanyMock.id);
|
||||||
|
});
|
||||||
|
});
|
@ -9,7 +9,11 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
|
|||||||
import { useRecordOptimisticEffect } from '@/object-metadata/hooks/useRecordOptimisticEffect';
|
import { useRecordOptimisticEffect } from '@/object-metadata/hooks/useRecordOptimisticEffect';
|
||||||
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
||||||
import { OrderByField } from '@/object-metadata/types/OrderByField';
|
import { OrderByField } from '@/object-metadata/types/OrderByField';
|
||||||
|
import { useMapConnectionToRecords } from '@/object-record/hooks/useMapConnectionToRecords';
|
||||||
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
|
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
||||||
|
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
|
||||||
import { filterUniqueRecordEdgesByCursor } from '@/object-record/utils/filterUniqueRecordEdgesByCursor';
|
import { filterUniqueRecordEdgesByCursor } from '@/object-record/utils/filterUniqueRecordEdgesByCursor';
|
||||||
import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/search/hooks/useFilteredSearchEntityQuery';
|
import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/search/hooks/useFilteredSearchEntityQuery';
|
||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
@ -19,28 +23,24 @@ import { capitalize } from '~/utils/string/capitalize';
|
|||||||
import { cursorFamilyState } from '../states/cursorFamilyState';
|
import { cursorFamilyState } from '../states/cursorFamilyState';
|
||||||
import { hasNextPageFamilyState } from '../states/hasNextPageFamilyState';
|
import { hasNextPageFamilyState } from '../states/hasNextPageFamilyState';
|
||||||
import { isFetchingMoreRecordsFamilyState } from '../states/isFetchingMoreRecordsFamilyState';
|
import { isFetchingMoreRecordsFamilyState } from '../states/isFetchingMoreRecordsFamilyState';
|
||||||
import { PaginatedRecordType } from '../types/PaginatedRecordType';
|
import { ObjectRecordQueryResult } from '../types/ObjectRecordQueryResult';
|
||||||
import {
|
|
||||||
PaginatedRecordTypeEdge,
|
|
||||||
PaginatedRecordTypeResults,
|
|
||||||
} from '../types/PaginatedRecordTypeResults';
|
|
||||||
import { mapPaginatedRecordsToRecords } from '../utils/mapPaginatedRecordsToRecords';
|
import { mapPaginatedRecordsToRecords } from '../utils/mapPaginatedRecordsToRecords';
|
||||||
|
|
||||||
export const useFindManyRecords = <
|
export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
|
||||||
RecordType extends { id: string } & Record<string, any>,
|
|
||||||
>({
|
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
filter,
|
filter,
|
||||||
orderBy,
|
orderBy,
|
||||||
limit = DEFAULT_SEARCH_REQUEST_LIMIT,
|
limit = DEFAULT_SEARCH_REQUEST_LIMIT,
|
||||||
onCompleted,
|
onCompleted,
|
||||||
skip,
|
skip,
|
||||||
|
useRecordsWithoutConnection = false,
|
||||||
}: ObjectMetadataItemIdentifier & {
|
}: ObjectMetadataItemIdentifier & {
|
||||||
filter?: ObjectRecordQueryFilter;
|
filter?: ObjectRecordQueryFilter;
|
||||||
orderBy?: OrderByField;
|
orderBy?: OrderByField;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
onCompleted?: (data: PaginatedRecordTypeResults<RecordType>) => void;
|
onCompleted?: (data: ObjectRecordConnection<T>) => void;
|
||||||
skip?: boolean;
|
skip?: boolean;
|
||||||
|
useRecordsWithoutConnection?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const findManyQueryStateIdentifier =
|
const findManyQueryStateIdentifier =
|
||||||
objectNameSingular +
|
objectNameSingular +
|
||||||
@ -75,7 +75,7 @@ export const useFindManyRecords = <
|
|||||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||||
|
|
||||||
const { data, loading, error, fetchMore } = useQuery<
|
const { data, loading, error, fetchMore } = useQuery<
|
||||||
PaginatedRecordType<RecordType>
|
ObjectRecordQueryResult<T>
|
||||||
>(findManyRecordsQuery, {
|
>(findManyRecordsQuery, {
|
||||||
skip: skip || !objectMetadataItem || !currentWorkspace,
|
skip: skip || !objectMetadataItem || !currentWorkspace,
|
||||||
variables: {
|
variables: {
|
||||||
@ -88,10 +88,10 @@ export const useFindManyRecords = <
|
|||||||
|
|
||||||
if (data?.[objectMetadataItem.namePlural]) {
|
if (data?.[objectMetadataItem.namePlural]) {
|
||||||
setLastCursor(
|
setLastCursor(
|
||||||
data?.[objectMetadataItem.namePlural]?.pageInfo.endCursor,
|
data?.[objectMetadataItem.namePlural]?.pageInfo.endCursor ?? '',
|
||||||
);
|
);
|
||||||
setHasNextPage(
|
setHasNextPage(
|
||||||
data?.[objectMetadataItem.namePlural]?.pageInfo.hasNextPage,
|
data?.[objectMetadataItem.namePlural]?.pageInfo.hasNextPage ?? false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -125,7 +125,7 @@ export const useFindManyRecords = <
|
|||||||
const nextEdges =
|
const nextEdges =
|
||||||
fetchMoreResult?.[objectMetadataItem.namePlural]?.edges;
|
fetchMoreResult?.[objectMetadataItem.namePlural]?.edges;
|
||||||
|
|
||||||
let newEdges: PaginatedRecordTypeEdge<RecordType>[] = [];
|
let newEdges: ObjectRecordEdge<T>[] = [];
|
||||||
|
|
||||||
if (isNonEmptyArray(previousEdges) && isNonEmptyArray(nextEdges)) {
|
if (isNonEmptyArray(previousEdges) && isNonEmptyArray(nextEdges)) {
|
||||||
newEdges = filterUniqueRecordEdgesByCursor([
|
newEdges = filterUniqueRecordEdgesByCursor([
|
||||||
@ -138,11 +138,11 @@ export const useFindManyRecords = <
|
|||||||
if (data?.[objectMetadataItem.namePlural]) {
|
if (data?.[objectMetadataItem.namePlural]) {
|
||||||
setLastCursor(
|
setLastCursor(
|
||||||
fetchMoreResult?.[objectMetadataItem.namePlural]?.pageInfo
|
fetchMoreResult?.[objectMetadataItem.namePlural]?.pageInfo
|
||||||
.endCursor,
|
.endCursor ?? '',
|
||||||
);
|
);
|
||||||
setHasNextPage(
|
setHasNextPage(
|
||||||
fetchMoreResult?.[objectMetadataItem.namePlural]?.pageInfo
|
fetchMoreResult?.[objectMetadataItem.namePlural]?.pageInfo
|
||||||
.hasNextPage,
|
.hasNextPage ?? false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +164,7 @@ export const useFindManyRecords = <
|
|||||||
pageInfo:
|
pageInfo:
|
||||||
fetchMoreResult?.[objectMetadataItem.namePlural].pageInfo,
|
fetchMoreResult?.[objectMetadataItem.namePlural].pageInfo,
|
||||||
},
|
},
|
||||||
} as PaginatedRecordType<RecordType>);
|
} as ObjectRecordQueryResult<T>);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -198,18 +198,39 @@ export const useFindManyRecords = <
|
|||||||
enqueueSnackBar,
|
enqueueSnackBar,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// TODO: remove this and use only mapConnectionToRecords when we've finished the refactor
|
||||||
const records = useMemo(
|
const records = useMemo(
|
||||||
() =>
|
() =>
|
||||||
mapPaginatedRecordsToRecords({
|
mapPaginatedRecordsToRecords({
|
||||||
pagedRecords: data,
|
pagedRecords: data,
|
||||||
objectNamePlural: objectMetadataItem.namePlural,
|
objectNamePlural: objectMetadataItem.namePlural,
|
||||||
}),
|
}) as T[],
|
||||||
[data, objectMetadataItem],
|
[data, objectMetadataItem],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const mapConnectionToRecords = useMapConnectionToRecords();
|
||||||
|
|
||||||
|
const recordsWithoutConnection = useMemo(
|
||||||
|
() =>
|
||||||
|
useRecordsWithoutConnection
|
||||||
|
? (mapConnectionToRecords({
|
||||||
|
objectRecordConnection: data?.[objectMetadataItem.namePlural],
|
||||||
|
objectNameSingular,
|
||||||
|
depth: 5,
|
||||||
|
}) as T[])
|
||||||
|
: [],
|
||||||
|
[
|
||||||
|
data,
|
||||||
|
objectNameSingular,
|
||||||
|
objectMetadataItem.namePlural,
|
||||||
|
mapConnectionToRecords,
|
||||||
|
useRecordsWithoutConnection,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
records: records as RecordType[],
|
records: useRecordsWithoutConnection ? recordsWithoutConnection : records,
|
||||||
loading,
|
loading,
|
||||||
error,
|
error,
|
||||||
fetchMoreRecords,
|
fetchMoreRecords,
|
||||||
|
@ -0,0 +1,113 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
import { isNonEmptyArray } from '@sniptt/guards';
|
||||||
|
import { produce } from 'immer';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
|
import { parseFieldRelationType } from '@/object-metadata/utils/parseFieldRelationType';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
||||||
|
import { FieldMetadataType } from '~/generated/graphql';
|
||||||
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
|
export const useMapConnectionToRecords = () => {
|
||||||
|
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||||
|
|
||||||
|
const mapConnectionToRecords = useCallback(
|
||||||
|
<T extends ObjectRecord>({
|
||||||
|
objectRecordConnection,
|
||||||
|
objectNameSingular,
|
||||||
|
objectNamePlural,
|
||||||
|
depth,
|
||||||
|
}: {
|
||||||
|
objectRecordConnection: ObjectRecordConnection<T> | undefined | null;
|
||||||
|
objectNameSingular?: string;
|
||||||
|
objectNamePlural?: string;
|
||||||
|
depth: number;
|
||||||
|
}): ObjectRecord[] => {
|
||||||
|
if (
|
||||||
|
!isDefined(objectRecordConnection) ||
|
||||||
|
!isNonEmptyArray(objectMetadataItems)
|
||||||
|
) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentLevelObjectMetadataItem = objectMetadataItems.find(
|
||||||
|
(objectMetadataItem) =>
|
||||||
|
objectMetadataItem.nameSingular === objectNameSingular ||
|
||||||
|
objectMetadataItem.namePlural === objectNamePlural,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!currentLevelObjectMetadataItem) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not find object metadata item for object name singular "${objectNameSingular}" in mapConnectionToRecords`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const relationFields = currentLevelObjectMetadataItem.fields.filter(
|
||||||
|
(field) => field.type === FieldMetadataType.Relation,
|
||||||
|
);
|
||||||
|
|
||||||
|
const objectRecords = [
|
||||||
|
...(objectRecordConnection.edges?.map((edge) => edge.node) ?? []),
|
||||||
|
];
|
||||||
|
|
||||||
|
return produce(objectRecords, (objectRecordsDraft) => {
|
||||||
|
for (const objectRecordDraft of objectRecordsDraft) {
|
||||||
|
for (const relationField of relationFields) {
|
||||||
|
const relationType = parseFieldRelationType(relationField);
|
||||||
|
|
||||||
|
if (
|
||||||
|
relationType === 'TO_ONE_OBJECT' ||
|
||||||
|
relationType === 'FROM_ONE_OBJECT'
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const relatedObjectMetadataSingularName =
|
||||||
|
relationField.toRelationMetadata?.fromObjectMetadata
|
||||||
|
.nameSingular ??
|
||||||
|
relationField.fromRelationMetadata?.toObjectMetadata
|
||||||
|
.nameSingular ??
|
||||||
|
null;
|
||||||
|
|
||||||
|
const relationFieldMetadataItem = objectMetadataItems.find(
|
||||||
|
(objectMetadataItem) =>
|
||||||
|
objectMetadataItem.nameSingular ===
|
||||||
|
relatedObjectMetadataSingularName,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!relationFieldMetadataItem ||
|
||||||
|
!isDefined(relatedObjectMetadataSingularName)
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not find relation object metadata item for object name plural ${relationField.name} in mapConnectionToRecords`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const relationConnection = objectRecordDraft?.[
|
||||||
|
relationField.name
|
||||||
|
] as ObjectRecordConnection | undefined | null;
|
||||||
|
|
||||||
|
if (!isDefined(relationConnection)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const relationConnectionMappedToRecords = mapConnectionToRecords({
|
||||||
|
objectRecordConnection: relationConnection,
|
||||||
|
objectNameSingular: relatedObjectMetadataSingularName,
|
||||||
|
depth: depth - 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
(objectRecordDraft as any)[relationField.name] =
|
||||||
|
relationConnectionMappedToRecords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) as ObjectRecord[];
|
||||||
|
},
|
||||||
|
[objectMetadataItems],
|
||||||
|
);
|
||||||
|
|
||||||
|
return mapConnectionToRecords;
|
||||||
|
};
|
@ -7,7 +7,7 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
|
|||||||
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||||
import { useRecordBoardScopedStates } from '@/object-record/record-board/hooks/internal/useRecordBoardScopedStates';
|
import { useRecordBoardScopedStates } from '@/object-record/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
import { turnObjectDropdownFilterIntoQueryFilter } from '@/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter';
|
import { turnObjectDropdownFilterIntoQueryFilter } from '@/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter';
|
||||||
import { PaginatedRecordTypeResults } from '@/object-record/types/PaginatedRecordTypeResults';
|
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
||||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||||
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ export const useObjectRecordBoard = () => {
|
|||||||
objectNameSingular: CoreObjectNameSingular.PipelineStep,
|
objectNameSingular: CoreObjectNameSingular.PipelineStep,
|
||||||
filter: {},
|
filter: {},
|
||||||
onCompleted: useCallback(
|
onCompleted: useCallback(
|
||||||
(data: PaginatedRecordTypeResults<PipelineStep>) => {
|
(data: ObjectRecordConnection<PipelineStep>) => {
|
||||||
setSavedPipelineSteps(data.edges.map((edge) => edge.node));
|
setSavedPipelineSteps(data.edges.map((edge) => edge.node));
|
||||||
},
|
},
|
||||||
[setSavedPipelineSteps],
|
[setSavedPipelineSteps],
|
||||||
@ -89,7 +89,7 @@ export const useObjectRecordBoard = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
onCompleted: useCallback(
|
onCompleted: useCallback(
|
||||||
(data: PaginatedRecordTypeResults<Company>) => {
|
(data: ObjectRecordConnection<Company>) => {
|
||||||
setSavedCompanies(data.edges.map((edge) => edge.node));
|
setSavedCompanies(data.edges.map((edge) => edge.node));
|
||||||
},
|
},
|
||||||
[setSavedCompanies],
|
[setSavedCompanies],
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
|
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
|
||||||
|
import { Nullable } from '~/types/Nullable';
|
||||||
|
|
||||||
export type ObjectRecordConnection = {
|
export type ObjectRecordConnection<T extends ObjectRecord = ObjectRecord> = {
|
||||||
edges: ObjectRecordEdge[];
|
__typename?: string;
|
||||||
|
edges: ObjectRecordEdge<T>[];
|
||||||
pageInfo: {
|
pageInfo: {
|
||||||
hasNextPage?: boolean;
|
hasNextPage?: boolean;
|
||||||
hasPreviousPage?: boolean;
|
hasPreviousPage?: boolean;
|
||||||
startCursor?: string;
|
startCursor?: Nullable<string>;
|
||||||
endCursor?: string;
|
endCursor?: Nullable<string>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
|
||||||
export type ObjectRecordEdge = {
|
export type ObjectRecordEdge<T extends ObjectRecord> = {
|
||||||
node: ObjectRecord;
|
__typename?: string;
|
||||||
|
node: T;
|
||||||
cursor: string;
|
cursor: string;
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
||||||
|
|
||||||
|
export type ObjectRecordQueryResult<T extends ObjectRecord> = {
|
||||||
|
[objectNamePlural: string]: ObjectRecordConnection<T>;
|
||||||
|
};
|
@ -1,5 +0,0 @@
|
|||||||
import { PaginatedRecordTypeResults } from './PaginatedRecordTypeResults';
|
|
||||||
|
|
||||||
export type PaginatedRecordType<RecordType extends { id: string }> = {
|
|
||||||
[objectNamePlural: string]: PaginatedRecordTypeResults<RecordType>;
|
|
||||||
};
|
|
@ -1,19 +0,0 @@
|
|||||||
export type PaginatedRecordTypeEdge<
|
|
||||||
RecordType extends { id: string } & Record<string, any>,
|
|
||||||
> = {
|
|
||||||
node: RecordType;
|
|
||||||
cursor: string;
|
|
||||||
__typename?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PaginatedRecordTypeResults<
|
|
||||||
RecordType extends { id: string } & Record<string, any>,
|
|
||||||
> = {
|
|
||||||
__typename?: string;
|
|
||||||
edges: PaginatedRecordTypeEdge<RecordType>[];
|
|
||||||
pageInfo: {
|
|
||||||
hasNextPage: boolean;
|
|
||||||
startCursor: string;
|
|
||||||
endCursor: string;
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,9 +1,9 @@
|
|||||||
import { PaginatedRecordTypeEdge } from '@/object-record/types/PaginatedRecordTypeResults';
|
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
|
||||||
|
|
||||||
export const filterUniqueRecordEdgesByCursor = <
|
export const filterUniqueRecordEdgesByCursor = <
|
||||||
RecordType extends { id: string },
|
RecordType extends { id: string },
|
||||||
>(
|
>(
|
||||||
arrayToFilter: PaginatedRecordTypeEdge<RecordType>[],
|
arrayToFilter: ObjectRecordEdge<RecordType>[],
|
||||||
) => {
|
) => {
|
||||||
const seenCursors = new Set();
|
const seenCursors = new Set();
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ export const ViewBarEffect = () => {
|
|||||||
type: { eq: viewType },
|
type: { eq: viewType },
|
||||||
objectMetadataId: { eq: viewObjectMetadataId },
|
objectMetadataId: { eq: viewObjectMetadataId },
|
||||||
},
|
},
|
||||||
|
useRecordsWithoutConnection: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -284,33 +284,11 @@ describe('useViewBar', () => {
|
|||||||
|
|
||||||
viewBar.setAvailableSortDefinitions([sortDefinition]);
|
viewBar.setAvailableSortDefinitions([sortDefinition]);
|
||||||
|
|
||||||
viewBar.loadViewSorts(
|
viewBar.loadViewSorts([viewSort], mockedUuid);
|
||||||
{
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
node: viewSort,
|
|
||||||
cursor: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
|
|
||||||
},
|
|
||||||
mockedUuid,
|
|
||||||
);
|
|
||||||
|
|
||||||
viewBar.setAvailableFilterDefinitions([filterDefinition]);
|
viewBar.setAvailableFilterDefinitions([filterDefinition]);
|
||||||
|
|
||||||
viewBar.loadViewFilters(
|
viewBar.loadViewFilters([viewFilter], mockedUuid);
|
||||||
{
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
node: viewFilter,
|
|
||||||
cursor: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
|
|
||||||
},
|
|
||||||
mockedUuid,
|
|
||||||
);
|
|
||||||
|
|
||||||
return { viewBar };
|
return { viewBar };
|
||||||
}, renderHookConfig);
|
}, renderHookConfig);
|
||||||
|
@ -159,18 +159,7 @@ describe('useViewBar > viewFields', () => {
|
|||||||
await act(async () => {
|
await act(async () => {
|
||||||
result.current.viewBar.setAvailableFieldDefinitions([fieldDefinition]);
|
result.current.viewBar.setAvailableFieldDefinitions([fieldDefinition]);
|
||||||
|
|
||||||
await result.current.viewBar.loadViewFields(
|
await result.current.viewBar.loadViewFields([viewField], currentViewId);
|
||||||
{
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
node: viewField,
|
|
||||||
cursor: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
|
|
||||||
},
|
|
||||||
currentViewId,
|
|
||||||
);
|
|
||||||
result.current.viewBar.setCurrentViewId(currentViewId);
|
result.current.viewBar.setCurrentViewId(currentViewId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -69,18 +69,7 @@ describe('useViewBar > viewFilters', () => {
|
|||||||
await act(async () => {
|
await act(async () => {
|
||||||
result.current.viewBar.setAvailableFilterDefinitions([filterDefinition]);
|
result.current.viewBar.setAvailableFilterDefinitions([filterDefinition]);
|
||||||
|
|
||||||
await result.current.viewBar.loadViewFilters(
|
await result.current.viewBar.loadViewFilters([viewFilter], currentViewId);
|
||||||
{
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
node: viewFilter,
|
|
||||||
cursor: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
|
|
||||||
},
|
|
||||||
currentViewId,
|
|
||||||
);
|
|
||||||
result.current.viewBar.setCurrentViewId(currentViewId);
|
result.current.viewBar.setCurrentViewId(currentViewId);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -93,18 +82,7 @@ describe('useViewBar > viewFilters', () => {
|
|||||||
|
|
||||||
viewBar.setAvailableFilterDefinitions([filterDefinition]);
|
viewBar.setAvailableFilterDefinitions([filterDefinition]);
|
||||||
|
|
||||||
viewBar.loadViewFilters(
|
viewBar.loadViewFilters([viewFilter], currentViewId);
|
||||||
{
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
node: viewFilter,
|
|
||||||
cursor: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
|
|
||||||
},
|
|
||||||
currentViewId,
|
|
||||||
);
|
|
||||||
viewBar.setCurrentViewId(currentViewId);
|
viewBar.setCurrentViewId(currentViewId);
|
||||||
|
|
||||||
const { currentViewFiltersState } = useViewScopedStates({
|
const { currentViewFiltersState } = useViewScopedStates({
|
||||||
@ -174,18 +152,7 @@ describe('useViewBar > viewFilters', () => {
|
|||||||
|
|
||||||
viewBar.setAvailableFilterDefinitions([filterDefinition]);
|
viewBar.setAvailableFilterDefinitions([filterDefinition]);
|
||||||
|
|
||||||
viewBar.loadViewFilters(
|
viewBar.loadViewFilters([viewFilter], currentViewId);
|
||||||
{
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
node: viewFilter,
|
|
||||||
cursor: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
|
|
||||||
},
|
|
||||||
currentViewId,
|
|
||||||
);
|
|
||||||
viewBar.setCurrentViewId(currentViewId);
|
viewBar.setCurrentViewId(currentViewId);
|
||||||
|
|
||||||
const { currentViewFiltersState } = useViewScopedStates({
|
const { currentViewFiltersState } = useViewScopedStates({
|
||||||
|
@ -65,18 +65,7 @@ describe('View Sorts', () => {
|
|||||||
await act(async () => {
|
await act(async () => {
|
||||||
result.current.viewBar.setAvailableSortDefinitions([sortDefinition]);
|
result.current.viewBar.setAvailableSortDefinitions([sortDefinition]);
|
||||||
|
|
||||||
await result.current.viewBar.loadViewSorts(
|
await result.current.viewBar.loadViewSorts([viewSort], currentViewId);
|
||||||
{
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
node: viewSort,
|
|
||||||
cursor: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
|
|
||||||
},
|
|
||||||
currentViewId,
|
|
||||||
);
|
|
||||||
result.current.viewBar.setCurrentViewId(currentViewId);
|
result.current.viewBar.setCurrentViewId(currentViewId);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -89,18 +78,7 @@ describe('View Sorts', () => {
|
|||||||
|
|
||||||
viewBar.setAvailableSortDefinitions([sortDefinition]);
|
viewBar.setAvailableSortDefinitions([sortDefinition]);
|
||||||
|
|
||||||
viewBar.loadViewSorts(
|
viewBar.loadViewSorts([viewSort], currentViewId);
|
||||||
{
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
node: viewSort,
|
|
||||||
cursor: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
|
|
||||||
},
|
|
||||||
currentViewId,
|
|
||||||
);
|
|
||||||
viewBar.setCurrentViewId(currentViewId);
|
viewBar.setCurrentViewId(currentViewId);
|
||||||
|
|
||||||
const { currentViewSortsState } = useViewScopedStates({
|
const { currentViewSortsState } = useViewScopedStates({
|
||||||
@ -161,18 +139,7 @@ describe('View Sorts', () => {
|
|||||||
|
|
||||||
viewBar.setAvailableSortDefinitions([sortDefinition]);
|
viewBar.setAvailableSortDefinitions([sortDefinition]);
|
||||||
|
|
||||||
viewBar.loadViewSorts(
|
viewBar.loadViewSorts([viewSort], currentViewId);
|
||||||
{
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
node: viewSort,
|
|
||||||
cursor: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
|
|
||||||
},
|
|
||||||
currentViewId,
|
|
||||||
);
|
|
||||||
viewBar.setCurrentViewId(currentViewId);
|
viewBar.setCurrentViewId(currentViewId);
|
||||||
|
|
||||||
const { currentViewSortsState } = useViewScopedStates({
|
const { currentViewSortsState } = useViewScopedStates({
|
||||||
|
@ -3,7 +3,6 @@ import { useSearchParams } from 'react-router-dom';
|
|||||||
import { useRecoilCallback, useRecoilState, useSetRecoilState } from 'recoil';
|
import { useRecoilCallback, useRecoilState, useSetRecoilState } from 'recoil';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
import { PaginatedRecordTypeResults } from '@/object-record/types/PaginatedRecordTypeResults';
|
|
||||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||||
import { ViewField } from '@/views/types/ViewField';
|
import { ViewField } from '@/views/types/ViewField';
|
||||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||||
@ -93,10 +92,7 @@ export const useViewBar = (props?: UseViewProps) => {
|
|||||||
|
|
||||||
const loadViewFields = useRecoilCallback(
|
const loadViewFields = useRecoilCallback(
|
||||||
({ snapshot, set }) =>
|
({ snapshot, set }) =>
|
||||||
async (
|
async (viewFields: ViewField[], currentViewId: string) => {
|
||||||
data: PaginatedRecordTypeResults<ViewField>,
|
|
||||||
currentViewId: string,
|
|
||||||
) => {
|
|
||||||
const {
|
const {
|
||||||
availableFieldDefinitions,
|
availableFieldDefinitions,
|
||||||
onViewFieldsChange,
|
onViewFieldsChange,
|
||||||
@ -119,8 +115,8 @@ export const useViewBar = (props?: UseViewProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const queriedViewFields = data.edges
|
const queriedViewFields = viewFields
|
||||||
.map((viewField) => viewField.node)
|
.map((viewField) => viewField)
|
||||||
.filter(assertNotNull);
|
.filter(assertNotNull);
|
||||||
|
|
||||||
if (isPersistingView) {
|
if (isPersistingView) {
|
||||||
@ -138,10 +134,7 @@ export const useViewBar = (props?: UseViewProps) => {
|
|||||||
|
|
||||||
const loadViewFilters = useRecoilCallback(
|
const loadViewFilters = useRecoilCallback(
|
||||||
({ snapshot, set }) =>
|
({ snapshot, set }) =>
|
||||||
async (
|
async (viewFilters: ViewFilter[], currentViewId: string) => {
|
||||||
data: PaginatedRecordTypeResults<Required<ViewFilter>>,
|
|
||||||
currentViewId: string,
|
|
||||||
) => {
|
|
||||||
const {
|
const {
|
||||||
availableFilterDefinitions,
|
availableFilterDefinitions,
|
||||||
savedViewFilters,
|
savedViewFilters,
|
||||||
@ -163,18 +156,18 @@ export const useViewBar = (props?: UseViewProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const queriedViewFilters = data.edges
|
const queriedViewFilters = viewFilters
|
||||||
.map(({ node }) => {
|
.map((viewFilter) => {
|
||||||
const availableFilterDefinition = availableFilterDefinitions.find(
|
const availableFilterDefinition = availableFilterDefinitions.find(
|
||||||
(filterDefinition) =>
|
(filterDefinition) =>
|
||||||
filterDefinition.fieldMetadataId === node.fieldMetadataId,
|
filterDefinition.fieldMetadataId === viewFilter.fieldMetadataId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!availableFilterDefinition) return null;
|
if (!availableFilterDefinition) return null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...node,
|
...viewFilter,
|
||||||
displayValue: node.displayValue ?? node.value,
|
displayValue: viewFilter.displayValue ?? viewFilter.value,
|
||||||
definition: availableFilterDefinition,
|
definition: availableFilterDefinition,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
@ -191,10 +184,7 @@ export const useViewBar = (props?: UseViewProps) => {
|
|||||||
|
|
||||||
const loadViewSorts = useRecoilCallback(
|
const loadViewSorts = useRecoilCallback(
|
||||||
({ snapshot, set }) =>
|
({ snapshot, set }) =>
|
||||||
async (
|
async (viewSorts: Required<ViewSort>[], currentViewId: string) => {
|
||||||
data: PaginatedRecordTypeResults<Required<ViewSort>>,
|
|
||||||
currentViewId: string,
|
|
||||||
) => {
|
|
||||||
const { availableSortDefinitions, savedViewSorts, onViewSortsChange } =
|
const { availableSortDefinitions, savedViewSorts, onViewSortsChange } =
|
||||||
getViewScopedStateValuesFromSnapshot({
|
getViewScopedStateValuesFromSnapshot({
|
||||||
snapshot,
|
snapshot,
|
||||||
@ -213,18 +203,18 @@ export const useViewBar = (props?: UseViewProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const queriedViewSorts = data.edges
|
const queriedViewSorts = viewSorts
|
||||||
.map(({ node }) => {
|
.map((viewSort) => {
|
||||||
const availableSortDefinition = availableSortDefinitions.find(
|
const availableSortDefinition = availableSortDefinitions.find(
|
||||||
(sort) => sort.fieldMetadataId === node.fieldMetadataId,
|
(sort) => sort.fieldMetadataId === viewSort.fieldMetadataId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!availableSortDefinition) return null;
|
if (!availableSortDefinition) return null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: node.id,
|
id: viewSort.id,
|
||||||
fieldMetadataId: node.fieldMetadataId,
|
fieldMetadataId: viewSort.fieldMetadataId,
|
||||||
direction: node.direction,
|
direction: viewSort.direction,
|
||||||
definition: availableSortDefinition,
|
definition: availableSortDefinition,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { PaginatedRecordTypeResults } from '@/object-record/types/PaginatedRecordTypeResults';
|
|
||||||
import { ViewField } from '@/views/types/ViewField';
|
import { ViewField } from '@/views/types/ViewField';
|
||||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||||
import { ViewSort } from '@/views/types/ViewSort';
|
import { ViewSort } from '@/views/types/ViewSort';
|
||||||
@ -7,7 +6,7 @@ export type GraphQLView = {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
objectMetadataId: string;
|
objectMetadataId: string;
|
||||||
viewFields: PaginatedRecordTypeResults<ViewField>;
|
viewFields: ViewField[];
|
||||||
viewFilters: PaginatedRecordTypeResults<ViewFilter>;
|
viewFilters: ViewFilter[];
|
||||||
viewSorts: PaginatedRecordTypeResults<ViewSort>;
|
viewSorts: ViewSort[];
|
||||||
};
|
};
|
||||||
|
@ -7,7 +7,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
|
|||||||
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
|
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||||
import { PaginatedRecordTypeResults } from '@/object-record/types/PaginatedRecordTypeResults';
|
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
||||||
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
||||||
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
|
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
|
||||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||||
@ -93,7 +93,7 @@ export const SettingsObjectNewFieldStep2 = () => {
|
|||||||
type: { eq: ViewType.Table },
|
type: { eq: ViewType.Table },
|
||||||
objectMetadataId: { eq: activeObjectMetadataItem?.id },
|
objectMetadataId: { eq: activeObjectMetadataItem?.id },
|
||||||
},
|
},
|
||||||
onCompleted: async (data: PaginatedRecordTypeResults<View>) => {
|
onCompleted: async (data: ObjectRecordConnection<View>) => {
|
||||||
const views = data.edges;
|
const views = data.edges;
|
||||||
|
|
||||||
if (!views) return;
|
if (!views) return;
|
||||||
@ -109,7 +109,7 @@ export const SettingsObjectNewFieldStep2 = () => {
|
|||||||
type: { eq: ViewType.Table },
|
type: { eq: ViewType.Table },
|
||||||
objectMetadataId: { eq: formValues.relation?.objectMetadataId },
|
objectMetadataId: { eq: formValues.relation?.objectMetadataId },
|
||||||
},
|
},
|
||||||
onCompleted: async (data: PaginatedRecordTypeResults<View>) => {
|
onCompleted: async (data: ObjectRecordConnection<View>) => {
|
||||||
const views = data.edges;
|
const views = data.edges;
|
||||||
|
|
||||||
if (!views) return;
|
if (!views) return;
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { MockedProvider, MockedResponse } from '@apollo/client/testing';
|
||||||
|
import { MutableSnapshot, RecoilRoot } from 'recoil';
|
||||||
|
|
||||||
|
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
|
||||||
|
|
||||||
|
export const getJestHookWrapper = ({
|
||||||
|
apolloMocks,
|
||||||
|
onInitializeRecoilSnapshot,
|
||||||
|
}: {
|
||||||
|
apolloMocks:
|
||||||
|
| readonly MockedResponse<Record<string, any>, Record<string, any>>[]
|
||||||
|
| undefined;
|
||||||
|
onInitializeRecoilSnapshot?: (snapshot: MutableSnapshot) => void;
|
||||||
|
}) => {
|
||||||
|
return ({ children }: { children: ReactNode }) => (
|
||||||
|
<RecoilRoot initializeState={onInitializeRecoilSnapshot}>
|
||||||
|
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
|
||||||
|
<MockedProvider mocks={apolloMocks} addTypename={false}>
|
||||||
|
{children}
|
||||||
|
</MockedProvider>
|
||||||
|
</SnackBarProviderScope>
|
||||||
|
</RecoilRoot>
|
||||||
|
);
|
||||||
|
};
|
@ -35183,7 +35183,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"playwright@npm:^1.14.0":
|
"playwright@npm:^1.14.0, playwright@npm:^1.40.1":
|
||||||
version: 1.40.1
|
version: 1.40.1
|
||||||
resolution: "playwright@npm:1.40.1"
|
resolution: "playwright@npm:1.40.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -41675,6 +41675,7 @@ __metadata:
|
|||||||
patch-package: "npm:^8.0.0"
|
patch-package: "npm:^8.0.0"
|
||||||
pg: "npm:^8.11.3"
|
pg: "npm:^8.11.3"
|
||||||
pg-boss: "npm:^9.0.3"
|
pg-boss: "npm:^9.0.3"
|
||||||
|
playwright: "npm:^1.40.1"
|
||||||
prettier: "npm:^3.1.1"
|
prettier: "npm:^3.1.1"
|
||||||
prism-react-renderer: "npm:^2.1.0"
|
prism-react-renderer: "npm:^2.1.0"
|
||||||
raw-loader: "npm:^4.0.2"
|
raw-loader: "npm:^4.0.2"
|
||||||
|
Loading…
Reference in New Issue
Block a user