Skip to content

Commit

Permalink
Merge pull request #730 from colonial-heritage/research-guide-frontend
Browse files Browse the repository at this point in the history
Research guide frontend
  • Loading branch information
barbarah authored Dec 5, 2024
2 parents 995aeb1 + ab5ffcf commit d6fd092
Show file tree
Hide file tree
Showing 7 changed files with 364 additions and 58 deletions.
48 changes: 41 additions & 7 deletions apps/researcher/src/app/[locale]/research-guide/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {getLocale, getTranslations} from 'next-intl/server';
import {Link} from '@/navigation';
import {ChevronRightIcon, ChevronLeftIcon} from '@heroicons/react/24/solid';
import StringToMarkdown from '../string-to-markdown';
import {Event} from '@colonial-collections/api';

interface Props {
params: {id: string};
Expand All @@ -34,16 +35,15 @@ export default async function GuidePage({params}: Props) {
{t('backButton')}
</Link>
</div>
<div className="w-full px-4 sm:px-10 max-w-7xl mx-auto mt-16 relative">
<nav className="*:no-underline text-sm flex gap-4 2xl:fixed 2xl:flex-col 2xl:-translate-x-32 2xl:gap-2 2xl:pt-24">
<a href="#description">{t('navText')}</a>
<a href="#citations">{t('navCitations')}</a>
</nav>
</div>
<main className="w-full px-4 sm:px-10 max-w-7xl mx-auto mt-16 mb-40">
<h1 className="text-2xl md:text-4xl mb-2" tabIndex={0}>
{guide.name}
</h1>
{guide.alternateNames?.length && (
<div className="text-sm text-neutral-600 mb-6">
{t('nameVariations')}: {guide.alternateNames?.join(', ')}
</div>
)}
<div className="flex flex-col md:flex-row gap-6">
<div className="w-full md:w-2/3">
<div className="prose" id="#description">
Expand All @@ -63,7 +63,9 @@ export default async function GuidePage({params}: Props) {
{citation.description}
{' — '}
<span className="text-sm">
<a href={citation.url}>{citation.url}</a>
<a target="_blank" href={citation.url}>
{citation.url}
</a>
</span>
</div>
</li>
Expand Down Expand Up @@ -118,10 +120,42 @@ export default async function GuidePage({params}: Props) {
))}
</div>
)}
{guide.contentReferenceTimes &&
guide.contentReferenceTimes.length > 0 && (
<div className="bg-consortium-sand-50 rounded px-2 py-4">
<h3 tabIndex={0}>{t('contentReferenceTimes')}</h3>
{guide.contentReferenceTimes.map(time => (
<div key={time.id}>
<DateRange event={time} />
</div>
))}
</div>
)}
</div>
</div>
</div>
</main>
</div>
);
}

async function DateRange({event}: {event: Event}) {
const t = await getTranslations('ResearchGuide');

if (!event.date?.startDate && !event.date?.endDate) {
return t('noDateRange');
}

const startDateFormatted = event.date?.startDate
? event.date.startDate.getFullYear()
: t('noStartDate');
const endDateFormatted = event.date?.endDate
? event.date.endDate.getFullYear()
: t('noEndDate');

if (startDateFormatted === endDateFormatted) {
return startDateFormatted as string;
}

return `${startDateFormatted}${endDateFormatted}`;
}
197 changes: 197 additions & 0 deletions apps/researcher/src/app/[locale]/research-guide/filterGuides.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import {ResearchGuide} from '@colonial-collections/api';
import {filterLevel3Guides, sortResearchGuide} from './filterGuides';

describe('filterLevel3Guides', () => {
it('filters out level 1 and level 2 guides from level 3 list', () => {
const topLevel: ResearchGuide = {
id: 'top',
seeAlso: [
{
id: 'level1-1',
seeAlso: [
{
id: 'level2-1',
seeAlso: [{id: 'level3-1'}, {id: 'level1-2'}, {id: 'level2-2'}],
},
],
},
{id: 'level1-2'},
],
};

const expected: ResearchGuide = {
id: 'top',
seeAlso: [
{
id: 'level1-1',
seeAlso: [
{
id: 'level2-1',
seeAlso: [{id: 'level3-1'}],
},
],
},
{id: 'level1-2'},
],
};

const result = filterLevel3Guides(topLevel);
expect(result).toEqual(expected);
});

it('handles cases with no level 3 guides', () => {
const topLevel: ResearchGuide = {
id: 'top',
seeAlso: [
{
id: 'level1-1',
seeAlso: [
{
id: 'level2-1',
seeAlso: [{id: 'level1-2'}, {id: 'level2-2'}],
},
],
},
{id: 'level1-2'},
],
};

const expected: ResearchGuide = {
id: 'top',
seeAlso: [
{
id: 'level1-1',
seeAlso: [
{
id: 'level2-1',
seeAlso: [],
},
],
},
{id: 'level1-2'},
],
};

const result = filterLevel3Guides(topLevel);
expect(result).toEqual(expected);
});

it('handles cases with no seeAlso arrays', () => {
const topLevel: ResearchGuide = {
id: 'top',
seeAlso: [],
};

const expected: ResearchGuide = {
id: 'top',
seeAlso: [],
};

const result = filterLevel3Guides(topLevel);
expect(result).toEqual(expected);
});
});

describe('sortResearchGuide', () => {
it('sorts guides by their names', () => {
const topLevel: ResearchGuide = {
id: 'top',
seeAlso: [
{id: '2', name: 'Beta'},
{id: '1', name: 'Alpha'},
{id: '3', name: 'Gamma'},
],
};

const expected: ResearchGuide = {
id: 'top',
seeAlso: [
{id: '1', name: 'Alpha'},
{id: '2', name: 'Beta'},
{id: '3', name: 'Gamma'},
],
};

const result = sortResearchGuide(topLevel);
expect(result).toEqual(expected);
});

it('handles guides with missing names', () => {
const topLevel: ResearchGuide = {
id: 'top',
seeAlso: [{id: '2', name: 'Beta'}, {id: '1'}, {id: '3', name: 'Gamma'}],
};

const expected: ResearchGuide = {
id: 'top',
seeAlso: [{id: '1'}, {id: '2', name: 'Beta'}, {id: '3', name: 'Gamma'}],
};

const result = sortResearchGuide(topLevel);
expect(result).toEqual(expected);
});

it('handles empty seeAlso arrays', () => {
const topLevel: ResearchGuide = {
id: 'top',
seeAlso: [],
};

const expected: ResearchGuide = {
id: 'top',
seeAlso: [],
};

const result = sortResearchGuide(topLevel);
expect(result).toEqual(expected);
});

it('sorts nested seeAlso arrays', () => {
const topLevel: ResearchGuide = {
id: 'top',
seeAlso: [
{
id: '1',
name: 'Alpha',
seeAlso: [
{id: '3', name: 'Gamma'},
{id: '2', name: 'Beta'},
],
},
{
id: '2',
name: 'Beta',
seeAlso: [
{id: '5', name: 'Epsilon'},
{id: '4', name: 'Delta'},
],
},
],
};

const expected: ResearchGuide = {
id: 'top',
seeAlso: [
{
id: '1',
name: 'Alpha',
seeAlso: [
{id: '2', name: 'Beta'},
{id: '3', name: 'Gamma'},
],
},
{
id: '2',
name: 'Beta',
seeAlso: [
{id: '4', name: 'Delta'},
{id: '5', name: 'Epsilon'},
],
},
],
};

const result = sortResearchGuide(topLevel);
expect(result).toEqual(expected);
});
});
49 changes: 49 additions & 0 deletions apps/researcher/src/app/[locale]/research-guide/filterGuides.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {ResearchGuide} from '@colonial-collections/api';

/**
* Filters out level 1 and level 2 guides from the seeAlso arrays of level 2 guides.
*
* 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.
*/
export function filterLevel3Guides(topLevel: ResearchGuide): ResearchGuide {
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, it's a level 3 guide
return !isLevel1Guide && !isLevel2Guide;
}) || [];
level2Guide.seeAlso = filteredSeeAlso;
});
});

return topLevel;
}

export function sortResearchGuide(topLevel: ResearchGuide): ResearchGuide {
const sortGuides = (guide: ResearchGuide): ResearchGuide => {
const sortedSeeAlso =
guide.seeAlso?.sort((a, b) =>
(a.name || '').localeCompare(b.name || '')
) || [];

return {
...guide,
seeAlso: sortedSeeAlso.map(sortGuides),
};
};

return sortGuides(topLevel);
}
Loading

0 comments on commit d6fd092

Please sign in to comment.