From a33f8180e5dc0b86eb53672c6868ceaf3d514167 Mon Sep 17 00:00:00 2001 From: Barbara Honhoff Date: Mon, 2 Dec 2024 11:07:44 +0100 Subject: [PATCH] Research Guide frontend: Do not use the removed identifier property and new top level layout. --- .../research-guide/filterGuides.test.ts | 109 ++++++++++++++++++ .../[locale]/research-guide/filterGuides.ts | 60 ++++++++++ .../src/app/[locale]/research-guide/page.tsx | 94 ++++++++------- 3 files changed, 220 insertions(+), 43 deletions(-) create mode 100644 apps/researcher/src/app/[locale]/research-guide/filterGuides.test.ts create mode 100644 apps/researcher/src/app/[locale]/research-guide/filterGuides.ts diff --git a/apps/researcher/src/app/[locale]/research-guide/filterGuides.test.ts b/apps/researcher/src/app/[locale]/research-guide/filterGuides.test.ts new file mode 100644 index 00000000..98583326 --- /dev/null +++ b/apps/researcher/src/app/[locale]/research-guide/filterGuides.test.ts @@ -0,0 +1,109 @@ +import {filterLevel3Guides, sortLevel1Guides} from './filterGuides'; + +describe('filterLevel3Guides', () => { + it('only shows each level 3 guide once', () => { + const topLevel = { + id: 'top', + seeAlso: [ + { + id: 'level1-1', + seeAlso: [ + { + id: 'level2-1', + seeAlso: [{id: 'level3-1'}, {id: 'level3-2'}, {id: 'level3-3'}], + }, + { + id: 'level2-2', + seeAlso: [{id: 'level3-1'}, {id: 'level3-4'}], + }, + ], + }, + { + id: 'level1-2', + seeAlso: [ + { + id: 'level2-3', + seeAlso: [{id: 'level3-2'}, {id: 'level3-5'}], + }, + ], + }, + ], + }; + + const filteredTopLevel = filterLevel3Guides(topLevel); + + expect(filteredTopLevel.seeAlso?.[0].seeAlso?.[0].seeAlso).toEqual([ + {id: 'level3-1'}, + {id: 'level3-2'}, + {id: 'level3-3'}, + ]); + expect(filteredTopLevel.seeAlso?.[0].seeAlso?.[1].seeAlso).toEqual([ + {id: 'level3-4'}, + ]); + expect(filteredTopLevel.seeAlso?.[1].seeAlso?.[0].seeAlso).toEqual([ + {id: 'level3-5'}, + ]); + }); + + it('filters out level 1 and 2 guides from the level 2 seeAlso', () => { + const topLevel = { + id: 'top', + seeAlso: [ + { + id: 'level1-1', + seeAlso: [ + { + id: 'level2-1', + seeAlso: [ + {id: 'level1-2'}, // Level 1 guide + {id: 'level2-2'}, // Level 2 guide + {id: 'level3-1'}, // Level 3 guide + ], + }, + ], + }, + { + id: 'level1-2', + seeAlso: [ + { + id: 'level2-2', + seeAlso: [ + {id: 'level3-2'}, // Level 3 guide + ], + }, + ], + }, + ], + }; + + const filteredTopLevel = filterLevel3Guides(topLevel); + + expect(filteredTopLevel.seeAlso?.[0].seeAlso?.[0].seeAlso).toEqual([ + {id: 'level3-1'}, + ]); + expect(filteredTopLevel.seeAlso?.[1].seeAlso?.[0].seeAlso).toEqual([ + {id: 'level3-2'}, + ]); + }); +}); + +describe('sortLevel1Guides', () => { + it('sorts level 1 guides by their names', () => { + const topLevel = { + id: 'top', + seeAlso: [ + {id: '2', name: 'B'}, + {id: '1', name: 'A'}, + {id: '3', name: 'C'}, + ], + }; + + const sortedLevel1Guides = sortLevel1Guides(topLevel); + + expect(sortedLevel1Guides).toEqual([ + {id: '1', name: 'A'}, + {id: '2', name: 'B'}, + {id: '3', name: 'C'}, + ]); + }); +}); diff --git a/apps/researcher/src/app/[locale]/research-guide/filterGuides.ts b/apps/researcher/src/app/[locale]/research-guide/filterGuides.ts new file mode 100644 index 00000000..2cfe08f4 --- /dev/null +++ b/apps/researcher/src/app/[locale]/research-guide/filterGuides.ts @@ -0,0 +1,60 @@ +type Guide = { + id: string; + name?: string; + seeAlso?: Guide[]; +}; + +/** + * Filters out level 1 and level 2 guides from the seeAlso arrays of level 2 guides + * and ensures each level 3 guide is only shown once. + * + * Assumptions: + * - topLevel.seeAlso contains level 1 guides. + * - Each level 1 guide's seeAlso contains level 2 guides. + * - Each level 2 guide's seeAlso may contain level 1, level 2, and level 3 guides. + * - Level 3 guides should only be shown once across all level 2 guides. + */ +export const filterLevel3Guides = (topLevel: Guide): Guide => { + const displayedLevel3Guides = new Set(); + + topLevel.seeAlso?.forEach(level1Guide => { + level1Guide.seeAlso?.forEach(level2Guide => { + const filteredSeeAlso = + level2Guide.seeAlso?.filter(guide => { + // Check if the guide is a level 1 guide + const isLevel1Guide = topLevel.seeAlso?.some( + l1 => l1.id === guide.id + ); + // Check if the guide is a level 2 guide + const isLevel2Guide = topLevel.seeAlso?.some( + l1 => l1.seeAlso?.some(l2 => l2.id === guide.id) + ); + + // If the guide is not a level 1 or level 2 guide and has not been displayed yet, it's a level 3 guide + if ( + !isLevel1Guide && + !isLevel2Guide && + !displayedLevel3Guides.has(guide.id) + ) { + displayedLevel3Guides.add(guide.id); + return true; + } + return false; + }) || []; + level2Guide.seeAlso = filteredSeeAlso; + }); + }); + + return topLevel; +}; + +/** + * Sorts the level 1 guides by their names. + */ +export const sortLevel1Guides = (topLevel: Guide): Guide[] => { + return ( + topLevel.seeAlso?.sort((a, b) => + (a.name || '').localeCompare(b.name || '') + ) || [] + ); +}; diff --git a/apps/researcher/src/app/[locale]/research-guide/page.tsx b/apps/researcher/src/app/[locale]/research-guide/page.tsx index 28b46bc4..33211c1c 100644 --- a/apps/researcher/src/app/[locale]/research-guide/page.tsx +++ b/apps/researcher/src/app/[locale]/research-guide/page.tsx @@ -5,6 +5,10 @@ import {Link} from '@/navigation'; import {ChevronRightIcon} from '@heroicons/react/24/solid'; import {getLocale, getTranslations} from 'next-intl/server'; import StringToMarkdown from './string-to-markdown'; +import { + filterLevel3Guides, + sortLevel1Guides, +} from '@/app/[locale]/research-guide/filterGuides'; export default async function Page() { const locale = (await getLocale()) as LocaleEnum; @@ -17,11 +21,11 @@ export default async function Page() { // There can be multiple top levels, but the current design only supports one. const topLevel = topLevels[0]; + const filteredTopLevel = filterLevel3Guides(topLevel); + const level1Guides = sortLevel1Guides(filteredTopLevel); - const level1Guides = - topLevel.seeAlso?.find(item => item.identifier === '1')?.seeAlso || []; - const level2Guides = - topLevel.seeAlso?.find(item => item.identifier === '2')?.seeAlso || []; + const firstLevel1Guide = level1Guides[0]; + const nextLevel1Guides = level1Guides.slice(1); return ( <> @@ -31,58 +35,62 @@ export default async function Page() {
{topLevel.text && }
-
-

- {t('level1Title')} -

-
- {level1Guides.map(item => ( - -
-
{item.name}
-
- + {firstLevel1Guide && ( +
+

+ {firstLevel1Guide.name} +

+
+ {firstLevel1Guide.seeAlso?.map(item => ( + +
+
{item.name}
+
+ +
-
- - ))} + + ))} +
-
-
-
+ )} + {nextLevel1Guides.map(level1Guide => ( +

- {t('level2Title')} + {level1Guide.name}

-
- {level2Guides.map(item => ( +
+ {level1Guide.seeAlso?.map(level2Guides => (
-
-
{item.name}
-
- -
+
{level2Guides.name}
+
+
- {item.seeAlso?.map(subItem => ( + {level2Guides.seeAlso?.map(linkedGuides => (
-
{subItem.name}
+
{linkedGuides.name}
@@ -93,7 +101,7 @@ export default async function Page() { ))}
-
+ ))} ); }