Skip to content

Commit

Permalink
feat: use modal to view code list usage in library (#14343)
Browse files Browse the repository at this point in the history
  • Loading branch information
standeren authored Jan 13, 2025
1 parent 8133d37 commit aa46b72
Show file tree
Hide file tree
Showing 12 changed files with 477 additions and 70 deletions.
9 changes: 8 additions & 1 deletion frontend/language/src/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,16 @@
"api_errors.ResourceNotFound": "Studio prøver å finne en fil som ikke finnes.",
"api_errors.Unauthorized": "Handlingen du prøver å utføre krever rettigheter du ikke har. Du blir nå logget ut.",
"api_errors.UploadedImageNotValid": "Det opplastede bildet er ikke en gyldig filtype",
"app_content_library.code_lists.code_list_accordion_title": "Kodeliste: {{codeListTitle}}",
"app_content_library.code_lists.code_list_accordion_title": "Kodelistenavn: {{codeListTitle}}",
"app_content_library.code_lists.code_list_accordion_usage_sub_title_plural": "Kodelisten brukes i {{codeListUsagesCount}} komponenter.",
"app_content_library.code_lists.code_list_accordion_usage_sub_title_single": "Kodelisten brukes i {{codeListUsagesCount}} komponent.",
"app_content_library.code_lists.code_list_edit_id_label": "Navn på kodeliste",
"app_content_library.code_lists.code_list_edit_id_title": "Rediger navn på kodelisten {{codeListName}}",
"app_content_library.code_lists.code_list_show_usage": "Se hvor kodelisten er tatt i bruk",
"app_content_library.code_lists.code_list_show_usage_modal_title": "Oversikt over bruk",
"app_content_library.code_lists.code_list_usage_table_column_header_components": "Komponent",
"app_content_library.code_lists.code_list_usage_table_column_header_layout": "Side",
"app_content_library.code_lists.code_list_usage_table_column_header_layout_set": "Sidegruppe",
"app_content_library.code_lists.code_list_view_id_title": "Navn på kodeliste: {{codeListName}}",
"app_content_library.code_lists.code_lists_count_info_none": "Det finnes ingen kodelister i biblioteket.",
"app_content_library.code_lists.code_lists_count_info_plural": "Det finnes <bold>{{codeListsCount}}</bold> kodelister i biblioteket.",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.codeListTitle {
display: flex;
flex-direction: column;
}

.codeListUsages {
font: var(--fds-typography-paragraph-xsmall);
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import type { CodeListsProps } from './CodeLists';
import { getCodeListSourcesById, updateCodeListWithMetadata, CodeLists } from './CodeLists';
import { CodeLists } from './CodeLists';
import { updateCodeListWithMetadata } from './EditCodeList/EditCodeList';
import { textMock } from '@studio/testing/mocks/i18nMock';
import type { CodeListWithMetadata } from '../CodeListPage';
import type { RenderResult } from '@testing-library/react';
import type { UserEvent } from '@testing-library/user-event';
import userEvent from '@testing-library/user-event';
import type { CodeList as StudioComponentsCodeList } from '@studio/components';
import { codeListsDataMock } from '../../../../../../mocks/mockPagesConfig';
import type { CodeListIdSource, CodeListReference } from '../types/CodeListReference';

const codeListName = codeListsDataMock[0].title;
const onUpdateCodeListIdMock = jest.fn();
Expand All @@ -31,6 +31,112 @@ describe('CodeLists', () => {
expect(codeListAccordion).toHaveAttribute('aria-expanded', 'true');
});

it('renders the accordion header title without usage information if not in use', () => {
renderCodeLists();
const codeListAccordionHeaderSubTitleSingle = screen.queryByText(
textMock('app_content_library.code_lists.code_list_accordion_usage_sub_title_single', {
codeListUsagesCount: 0,
}),
);
const codeListAccordionHeaderSubTitlePlural = screen.queryByText(
textMock('app_content_library.code_lists.code_list_accordion_usage_sub_title_plural', {
codeListUsagesCount: 0,
}),
);
expect(codeListAccordionHeaderSubTitleSingle).not.toBeInTheDocument();
expect(codeListAccordionHeaderSubTitlePlural).not.toBeInTheDocument();
});

it('does not render a button to view code list usages if not in use', () => {
renderCodeLists();
const viewCodeListUsagesButton = screen.queryByRole('button', {
name: textMock('app_content_library.code_lists.code_list_show_usage'),
});
expect(viewCodeListUsagesButton).not.toBeInTheDocument();
});

it('renders the accordion header title with single usage information if used once', () => {
renderCodeLists({
codeListsUsages: [
{
codeListId: codeListName,
codeListIdSources: [
{ layoutSetId: 'layoutSetId', layoutName: 'layoutName', componentIds: ['componentId'] },
],
},
],
});
const codeListAccordionHeaderSubTitleSingle = screen.getByText(
textMock('app_content_library.code_lists.code_list_accordion_usage_sub_title_single', {
codeListUsagesCount: 1,
}),
);
expect(codeListAccordionHeaderSubTitleSingle).toBeInTheDocument();
});

it('renders the accordion header title with plural usage information if used multiple times', () => {
renderCodeLists({
codeListsUsages: [
{
codeListId: codeListName,
codeListIdSources: [
{
layoutSetId: 'layoutSetId',
layoutName: 'layoutName',
componentIds: ['componentId1', 'componentId2'],
},
{ layoutSetId: 'layoutSetId', layoutName: 'layoutName', componentIds: ['componentId'] },
],
},
],
});
const codeListAccordionHeaderSubTitlePlural = screen.getByText(
textMock('app_content_library.code_lists.code_list_accordion_usage_sub_title_plural', {
codeListUsagesCount: 3,
}),
);
expect(codeListAccordionHeaderSubTitlePlural).toBeInTheDocument();
});

it('renders button to view code list usages if code list is in use', () => {
renderCodeLists({
codeListsUsages: [
{
codeListId: codeListName,
codeListIdSources: [
{ layoutSetId: 'layoutSetId', layoutName: 'layoutName', componentIds: ['componentId'] },
],
},
],
});
const viewCodeListUsagesButton = screen.getByRole('button', {
name: textMock('app_content_library.code_lists.code_list_show_usage'),
});
expect(viewCodeListUsagesButton).toBeInTheDocument();
});

it('renders modal to see code list usages if clicking button to view code list usages', async () => {
const user = userEvent.setup();
renderCodeLists({
codeListsUsages: [
{
codeListId: codeListName,
codeListIdSources: [
{ layoutSetId: 'layoutSetId', layoutName: 'layoutName', componentIds: ['componentId'] },
],
},
],
});
const viewCodeListUsagesButton = screen.getByRole('button', {
name: textMock('app_content_library.code_lists.code_list_show_usage'),
});
await user.click(viewCodeListUsagesButton);
const codeListUsagesModalTitle = screen.getByText(
textMock('app_content_library.code_lists.code_list_show_usage_modal_title'),
);
expect(codeListUsagesModalTitle).toBeInTheDocument();
});

it('renders the code list editor', () => {
renderCodeLists();
const codeListEditor = screen.getByText(textMock('code_list_editor.legend'));
Expand Down Expand Up @@ -158,37 +264,3 @@ describe('updateCodeListWithMetadata', () => {
});
});
});

const codeListId1: string = 'codeListId1';
const codeListId2: string = 'codeListId2';
const componentIds: string[] = ['componentId1', 'componentId2'];
const codeListIdSources1: CodeListIdSource[] = [
{ layoutSetId: 'layoutSetId', layoutName: 'layoutName', componentIds },
];
const codeListIdSources2: CodeListIdSource[] = [...codeListIdSources1];

describe('getCodeListSourcesById', () => {
it('returns an array of CodeListSources if given Id is present in codeListsUsages array', () => {
const codeListUsages: CodeListReference[] = [
{ codeListId: codeListId1, codeListIdSources: codeListIdSources1 },
{ codeListId: codeListId2, codeListIdSources: codeListIdSources2 },
];
const codeListSources = getCodeListSourcesById(codeListUsages, codeListId1);

expect(codeListSources).toBe(codeListIdSources1);
expect(codeListSources).not.toBe(codeListIdSources2);
});

it('returns an empty array if given Id is not present in codeListsUsages array', () => {
const codeListUsages: CodeListReference[] = [
{ codeListId: codeListId2, codeListIdSources: codeListIdSources2 },
];
const codeListSources = getCodeListSourcesById(codeListUsages, codeListId1);
expect(codeListSources).toEqual([]);
});

it('returns an empty array if codeListsUsages array is empty', () => {
const codeListSources = getCodeListSourcesById([], codeListId1);
expect(codeListSources).toEqual([]);
});
});
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React from 'react';
import type { CodeListData, CodeListWithMetadata } from '../CodeListPage';
import { Accordion } from '@digdir/designsystemet-react';
import { StudioAlert, type CodeList as StudioComponentsCodeList } from '@studio/components';
import { StudioAlert } from '@studio/components';
import { EditCodeList } from './EditCodeList/EditCodeList';
import { useTranslation } from 'react-i18next';
import type { CodeListIdSource, CodeListReference } from '../types/CodeListReference';
import classes from './CodeLists.module.css';
import { getCodeListSourcesById, getCodeListUsageCount } from '../utils';

export type CodeListsProps = {
codeListsData: CodeListData[];
Expand All @@ -22,7 +24,7 @@ export function CodeLists({
codeListInEditMode,
codeListNames,
codeListsUsages,
}: CodeListsProps) {
}: CodeListsProps): React.ReactElement[] {
return codeListsData.map((codeListData) => {
const codeListSources = getCodeListSourcesById(codeListsUsages, codeListData.title);
return (
Expand All @@ -39,16 +41,6 @@ export function CodeLists({
});
}

export const getCodeListSourcesById = (
codeListsUsages: CodeListReference[],
codeListTitle: string,
): CodeListIdSource[] => {
const codeListUsages: CodeListReference | undefined = codeListsUsages.find(
(codeListUsage) => codeListUsage.codeListId === codeListTitle,
);
return codeListUsages?.codeListIdSources ?? [];
};

type CodeListProps = Omit<CodeListsProps, 'codeListsData' | 'codeListsUsages'> & {
codeListData: CodeListData;
codeListSources: CodeListIdSource[];
Expand All @@ -61,19 +53,14 @@ function CodeList({
codeListInEditMode,
codeListNames,
codeListSources,
}: CodeListProps) {
const { t } = useTranslation();

}: CodeListProps): React.ReactElement {
return (
<Accordion border>
<Accordion.Item defaultOpen={codeListInEditMode === codeListData.title}>
<Accordion.Header
title={t('app_content_library.code_lists.code_list_accordion_title', {
codeListTitle: codeListData.title,
})}
>
{codeListData.title}
</Accordion.Header>
<CodeListAccordionHeader
codeListTitle={codeListData.title}
codeListUsagesCount={getCodeListUsageCount(codeListSources)}
/>
<CodeListAccordionContent
codeListData={codeListData}
onUpdateCodeListId={onUpdateCodeListId}
Expand All @@ -86,6 +73,49 @@ function CodeList({
);
}

type CodeListAccordionHeaderProps = {
codeListTitle: string;
codeListUsagesCount: number;
};

function CodeListAccordionHeader({
codeListTitle,
codeListUsagesCount,
}: CodeListAccordionHeaderProps): React.ReactElement {
const { t } = useTranslation();

let codeListUsagesCountTextKey: string =
'app_content_library.code_lists.code_list_accordion_usage_sub_title_plural';

switch (codeListUsagesCount) {
case 0: {
codeListUsagesCountTextKey = null;
break;
}
case 1: {
codeListUsagesCountTextKey =
'app_content_library.code_lists.code_list_accordion_usage_sub_title_single';
break;
}
}

return (
<Accordion.Header
title={t('app_content_library.code_lists.code_list_accordion_title', {
codeListTitle: codeListTitle,
})}
className={classes.codeListTitle}
>
{codeListTitle}
{codeListUsagesCountTextKey && (
<div className={classes.codeListUsages}>
{t(codeListUsagesCountTextKey, { codeListUsagesCount })}
</div>
)}
</Accordion.Header>
);
}

type CodeListAccordionContentProps = Omit<CodeListProps, 'codeListInEditMode'>;

function CodeListAccordionContent({
Expand All @@ -110,15 +140,9 @@ function CodeListAccordionContent({
onUpdateCodeListId={onUpdateCodeListId}
onUpdateCodeList={onUpdateCodeList}
codeListNames={codeListNames}
codeListSources={codeListSources}
/>
)}
</Accordion.Content>
);
}

export const updateCodeListWithMetadata = (
currentCodeListWithMetadata: CodeListWithMetadata,
updatedCodeList: StudioComponentsCodeList,
): CodeListWithMetadata => {
return { ...currentCodeListWithMetadata, codeList: updatedCodeList };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.table {
width: 100%;
}
Loading

0 comments on commit aa46b72

Please sign in to comment.