Merge pull request #14527 from strapi/relations/fix-order-normalizeRelations

[Relations - Front] Reverse order of relations
This commit is contained in:
Gustav Hansen 2022-10-07 11:55:25 +02:00 committed by GitHub
commit 541b1695fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 191 additions and 36 deletions

View File

@ -7,7 +7,7 @@ import { useCMEditViewDataManager, NotAllowedInput, useQueryParams } from '@stra
import { RelationInput } from '../RelationInput'; import { RelationInput } from '../RelationInput';
import { useRelation } from '../../hooks/useRelation'; import { useRelation } from '../../hooks/useRelation';
import { connect, select, normalizeRelations } from './utils'; import { connect, select, normalizeRelations, normalizeSearchResults } from './utils';
import { PUBLICATION_STATES, RELATIONS_TO_DISPLAY, SEARCH_RESULTS_TO_DISPLAY } from './constants'; import { PUBLICATION_STATES, RELATIONS_TO_DISPLAY, SEARCH_RESULTS_TO_DISPLAY } from './constants';
import { getTrad } from '../../utils'; import { getTrad } from '../../utils';
@ -194,9 +194,8 @@ export const RelationInputDataManager = ({
}} }}
relations={normalizedRelations} relations={normalizedRelations}
required={required} required={required}
searchResults={normalizeRelations(search, { searchResults={normalizeSearchResults(search, {
mainFieldName: mainField.name, mainFieldName: mainField.name,
search: 'search',
})} })}
size={size} size={size}
/> />

View File

@ -1,3 +1,4 @@
export { default as connect } from './connect'; export { default as connect } from './connect';
export { default as select } from './select'; export { default as select } from './select';
export { normalizeRelations } from './normalizeRelations'; export { normalizeRelations } from './normalizeRelations';
export { normalizeSearchResults } from './normalizeSearchResults';

View File

@ -2,7 +2,7 @@ import { getRelationLink } from './getRelationLink';
import { PUBLICATION_STATES } from '../constants'; import { PUBLICATION_STATES } from '../constants';
const normalizeRelation = (relation, { shouldAddLink, mainFieldName, targetModel }) => { export const normalizeRelation = (relation, { shouldAddLink, mainFieldName, targetModel }) => {
const nextRelation = { ...relation }; const nextRelation = { ...relation };
if (shouldAddLink) { if (shouldAddLink) {
@ -22,6 +22,14 @@ const normalizeRelation = (relation, { shouldAddLink, mainFieldName, targetModel
return nextRelation; return nextRelation;
}; };
/*
* Applies some transformations to existing and new relations in order to display them correctly
* relations: raw relations data coming from useRelations
* shouldAddLink: comes from generateRelationQueryInfos, if true we display a link to the relation (TO FIX: explanation)
* mainFieldName: name of the main field inside the relation (e.g. text field), if no displayable main field exists (e.g. date field) we use the id of the entry
* targetModel: the model on which the relation is based on, used to create an URL link
*/
export const normalizeRelations = ( export const normalizeRelations = (
relations, relations,
{ modifiedData = {}, shouldAddLink = false, mainFieldName, targetModel } { modifiedData = {}, shouldAddLink = false, mainFieldName, targetModel }
@ -31,7 +39,7 @@ export const normalizeRelations = (
data: { data: {
pages: pages:
[ [
...(relations?.data?.pages ?? []), ...(relations?.data?.pages.reverse() ?? []),
...(modifiedData?.connect ? [{ results: modifiedData.connect }] : []), ...(modifiedData?.connect ? [{ results: modifiedData.connect }] : []),
] ]
?.map((page) => ?.map((page) =>
@ -44,7 +52,6 @@ export const normalizeRelations = (
) )
.map((relation) => .map((relation) =>
normalizeRelation(relation, { normalizeRelation(relation, {
modifiedData,
shouldAddLink, shouldAddLink,
mainFieldName, mainFieldName,
targetModel, targetModel,

View File

@ -0,0 +1,12 @@
import { normalizeRelation } from './normalizeRelations';
export const normalizeSearchResults = (relations, { mainFieldName }) => {
return {
...relations,
data: {
pages: [...(relations?.data?.pages ?? [])]?.map((page) =>
page?.results.map((relation) => normalizeRelation(relation, { mainFieldName }))
),
},
};
};

View File

@ -6,8 +6,8 @@ const FIXTURE_RELATIONS = {
{ {
results: [ results: [
{ {
id: 1, id: 3,
name: 'Relation 1', name: 'Relation 3',
publishedAt: '2022-08-24T09:29:11.38', publishedAt: '2022-08-24T09:29:11.38',
}, },
@ -18,8 +18,8 @@ const FIXTURE_RELATIONS = {
}, },
{ {
id: 3, id: 1,
name: 'Relation 3', name: 'Relation 1',
}, },
], ],
}, },
@ -27,8 +27,8 @@ const FIXTURE_RELATIONS = {
}, },
}; };
describe('normalizeRelations', () => { describe('RelationInputDataManager || normalizeRelations', () => {
test('filters out deleted releations', () => { test('filters out deleted relations', () => {
expect( expect(
normalizeRelations(FIXTURE_RELATIONS, { normalizeRelations(FIXTURE_RELATIONS, {
modifiedData: { disconnect: [{ id: 1 }] }, modifiedData: { disconnect: [{ id: 1 }] },
@ -37,8 +37,8 @@ describe('normalizeRelations', () => {
data: { data: {
pages: [ pages: [
[ [
expect.objectContaining(FIXTURE_RELATIONS.data.pages[0].results[0]),
expect.objectContaining(FIXTURE_RELATIONS.data.pages[0].results[1]), expect.objectContaining(FIXTURE_RELATIONS.data.pages[0].results[1]),
expect.objectContaining(FIXTURE_RELATIONS.data.pages[0].results[2]),
], ],
], ],
}, },
@ -85,9 +85,9 @@ describe('normalizeRelations', () => {
data: { data: {
pages: [ pages: [
[ [
expect.objectContaining({ href: '/content-manager/collectionType/something/1' }),
expect.objectContaining({ href: '/content-manager/collectionType/something/2' }),
expect.objectContaining({ href: '/content-manager/collectionType/something/3' }), expect.objectContaining({ href: '/content-manager/collectionType/something/3' }),
expect.objectContaining({ href: '/content-manager/collectionType/something/2' }),
expect.objectContaining({ href: '/content-manager/collectionType/something/1' }),
], ],
], ],
}, },
@ -137,7 +137,7 @@ describe('normalizeRelations', () => {
}); });
}); });
test('allows to connect new relations, eventhough pages is empty', () => { test('allows to connect new relations, even though pages is empty', () => {
expect( expect(
normalizeRelations( normalizeRelations(
{ {
@ -161,4 +161,59 @@ describe('normalizeRelations', () => {
}, },
}); });
}); });
test('reverse order of relations pages', () => {
const fixtureExtended = {
pages: [
...FIXTURE_RELATIONS.data.pages,
{
results: [
{
id: 6,
name: 'Relation 6',
publishedAt: '2022-08-24T09:29:11.38',
},
{
id: 5,
name: 'Relation 5',
publishedAt: '',
},
{
id: 4,
name: 'Relation 4',
},
],
},
],
};
expect(
normalizeRelations(
{
data: fixtureExtended,
},
{
modifiedData: { connect: [{ id: 6 }] },
}
)
).toStrictEqual({
data: {
pages: [
[
expect.objectContaining({ id: 6 }),
expect.objectContaining({ id: 5 }),
expect.objectContaining({ id: 4 }),
],
[
expect.objectContaining({ id: 3 }),
expect.objectContaining({ id: 2 }),
expect.objectContaining({ id: 1 }),
],
[expect.objectContaining({ id: 6 })],
],
},
});
});
}); });

View File

@ -0,0 +1,68 @@
import { normalizeSearchResults } from '../normalizeSearchResults';
const FIXTURE_RELATIONS = {
data: {
pages: [
{
results: [
{
id: 3,
name: 'Relation 3',
publishedAt: '2022-08-24T09:29:11.38',
},
{
id: 2,
name: 'Relation 2',
publishedAt: '',
},
{
id: 1,
name: 'Relation 1',
},
],
},
],
},
};
describe('RelationInputDataManager || normalizeSearchResults', () => {
test('add publicationState attribute to each relation', () => {
expect(normalizeSearchResults(FIXTURE_RELATIONS, {})).toStrictEqual({
data: {
pages: [
[
expect.objectContaining({ publicationState: 'published' }),
expect.objectContaining({ publicationState: 'draft' }),
expect.objectContaining({ publicationState: false }),
],
],
},
});
});
test('add mainField attribute to each relation', () => {
expect(
normalizeSearchResults(FIXTURE_RELATIONS, {
mainFieldName: 'name',
})
).toStrictEqual({
data: {
pages: [
[
expect.objectContaining({
mainField: FIXTURE_RELATIONS.data.pages[0].results[0].name,
}),
expect.objectContaining({
mainField: FIXTURE_RELATIONS.data.pages[0].results[1].name,
}),
expect.objectContaining({
mainField: FIXTURE_RELATIONS.data.pages[0].results[2].name,
}),
],
],
},
});
});
});

View File

@ -8,9 +8,15 @@ import { useRelation } from '../useRelation';
jest.mock('../../../../core/utils', () => ({ jest.mock('../../../../core/utils', () => ({
...jest.requireActual('../../../../core/utils'), ...jest.requireActual('../../../../core/utils'),
axiosInstance: { axiosInstance: {
get: jest get: jest.fn().mockResolvedValue({
.fn() data: {
.mockResolvedValue({ data: { values: [], pagination: { page: 1, pageCount: 10 } } }), results: [
{ id: 2, name: 'newest', publishedAt: null },
{ id: 1, name: 'oldest', publishedAt: null },
],
pagination: { page: 1, pageCount: 10 },
},
}),
}, },
})); }));
@ -95,7 +101,7 @@ describe('useRelation', () => {
}); });
}); });
test('doesn not fetch relations if it was not enabled', async () => { test('does not fetch relations if it was not enabled', async () => {
await setup(undefined, { relation: { enabled: false } }); await setup(undefined, { relation: { enabled: false } });
expect(axiosInstance.get).not.toBeCalled(); expect(axiosInstance.get).not.toBeCalled();
@ -119,7 +125,7 @@ describe('useRelation', () => {
test('fetch relations next page, if there is one', async () => { test('fetch relations next page, if there is one', async () => {
axiosInstance.get = jest.fn().mockResolvedValue({ axiosInstance.get = jest.fn().mockResolvedValue({
data: { data: {
values: [], results: [],
pagination: { pagination: {
page: 1, page: 1,
pageCount: 3, pageCount: 3,
@ -155,7 +161,7 @@ describe('useRelation', () => {
test("does not fetch relations next page, if there isn't one", async () => { test("does not fetch relations next page, if there isn't one", async () => {
axiosInstance.get = jest.fn().mockResolvedValue({ axiosInstance.get = jest.fn().mockResolvedValue({
data: { data: {
values: [], results: [],
pagination: { pagination: {
page: 1, page: 1,
pageCount: 1, pageCount: 1,
@ -191,7 +197,7 @@ describe('useRelation', () => {
const spy = jest const spy = jest
.fn() .fn()
.mockResolvedValue({ data: { values: [], pagination: { page: 1, pageCount: 2 } } }); .mockResolvedValue({ data: { results: [], pagination: { page: 1, pageCount: 2 } } });
axiosInstance.get = spy; axiosInstance.get = spy;
act(() => { act(() => {
@ -237,7 +243,7 @@ describe('useRelation', () => {
const spy = jest const spy = jest
.fn() .fn()
.mockResolvedValue({ data: { values: [], pagination: { page: 1, pageCount: 2 } } }); .mockResolvedValue({ data: { results: [], pagination: { page: 1, pageCount: 2 } } });
axiosInstance.get = spy; axiosInstance.get = spy;
act(() => { act(() => {
@ -269,12 +275,12 @@ describe('useRelation', () => {
}); });
}); });
test("doesn not fetch search next page, if there isn't one", async () => { test("does not fetch search next page, if there isn't one", async () => {
const { result, waitForNextUpdate } = await setup(undefined); const { result, waitForNextUpdate } = await setup(undefined);
const spy = jest const spy = jest.fn().mockResolvedValue({
.fn() data: { results: [], pagination: { page: 1, pageCount: 1 } },
.mockResolvedValue({ data: { values: [], pagination: { page: 1, pageCount: 1 } } }); });
axiosInstance.get = spy; axiosInstance.get = spy;
act(() => { act(() => {

View File

@ -22,15 +22,19 @@ export const useRelation = (cacheKey, { relation, search }) => {
}; };
const fetchSearch = async ({ pageParam = 1 }) => { const fetchSearch = async ({ pageParam = 1 }) => {
const { data } = await axiosInstance.get(search.endpoint, { try {
params: { const { data } = await axiosInstance.get(search.endpoint, {
...(search.pageParams ?? {}), params: {
...searchParams, ...(search.pageParams ?? {}),
page: pageParam, ...searchParams,
}, page: pageParam,
}); },
});
return data; return data;
} catch (err) {
return null;
}
}; };
const relationsRes = useInfiniteQuery(['relation', cacheKey], fetchRelations, { const relationsRes = useInfiniteQuery(['relation', cacheKey], fetchRelations, {
@ -44,6 +48,9 @@ export const useRelation = (cacheKey, { relation, search }) => {
// eslint-disable-next-line consistent-return // eslint-disable-next-line consistent-return
return lastPage.pagination.page + 1; return lastPage.pagination.page + 1;
}, },
select: (data) => ({
pages: data.pages.map((page) => ({ ...page, results: [...(page.results ?? [])].reverse() })),
}),
}); });
const searchRes = useInfiniteQuery( const searchRes = useInfiniteQuery(