Skip to content

Commit

Permalink
Merge branch 'main' into check-for-code-list-usage
Browse files Browse the repository at this point in the history
  • Loading branch information
standeren authored Dec 18, 2024
2 parents 602bdf0 + eda8ca7 commit 71d2e2c
Show file tree
Hide file tree
Showing 13 changed files with 381 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ import type { ServicesContextProps } from 'app-shared/contexts/ServicesContext';

const uploadCodeListButtonTextMock = 'Upload Code List';
const updateCodeListButtonTextMock = 'Update Code List';
const updateCodeListIdButtonTextMock = 'Update Code List Id';
const codeListNameMock = 'codeListNameMock';
const newCodeListNameMock = 'newCodeListNameMock';
const codeListMock: CodeList = [{ value: '', label: '' }];
jest.mock(
'../../../libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage',
() => ({
CodeListPage: ({ onUpdateCodeList, onUploadCodeList }: any) => (
CodeListPage: ({ onUpdateCodeList, onUploadCodeList, onUpdateCodeListId }: any) => (
<div>
<button
onClick={() =>
Expand All @@ -36,6 +38,9 @@ jest.mock(
>
{updateCodeListButtonTextMock}
</button>
<button onClick={() => onUpdateCodeListId(codeListNameMock, newCodeListNameMock)}>
{updateCodeListIdButtonTextMock}
</button>
</div>
),
}),
Expand Down Expand Up @@ -115,6 +120,23 @@ describe('AppContentLibrary', () => {
codeListMock,
);
});

it('calls onUpdateOptionListId when onUpdateCodeListId is triggered', async () => {
const user = userEvent.setup();
renderAppContentLibrary(optionListsMock);
await goToLibraryPage(user, 'code_lists');
const updateCodeListIdButton = screen.getByRole('button', {
name: updateCodeListIdButtonTextMock,
});
await user.click(updateCodeListIdButton);
expect(queriesMock.updateOptionListId).toHaveBeenCalledTimes(1);
expect(queriesMock.updateOptionListId).toHaveBeenCalledWith(
org,
app,
codeListNameMock,
newCodeListNameMock,
);
});
});

const getLibraryPageTile = (libraryPage: string) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ import { useStudioEnvironmentParams } from 'app-shared/hooks/useStudioEnvironmen
import { convertOptionListsToCodeLists } from './utils/convertOptionListsToCodeLists';
import { StudioPageSpinner } from '@studio/components';
import { useTranslation } from 'react-i18next';
import { useAddOptionListMutation, useUpdateOptionListMutation } from 'app-shared/hooks/mutations';
import type { ApiError } from 'app-shared/types/api/ApiError';
import { toast } from 'react-toastify';
import type { AxiosError } from 'axios';
import { isErrorUnknown } from 'app-shared/utils/ApiErrorUtils';
import {
useAddOptionListMutation,
useUpdateOptionListMutation,
useUpdateOptionListIdMutation,
} from 'app-shared/hooks/mutations';

export function AppContentLibrary(): React.ReactElement {
const { org, app } = useStudioEnvironmentParams();
Expand All @@ -24,12 +28,17 @@ export function AppContentLibrary(): React.ReactElement {
hideDefaultError: (error: AxiosError<ApiError>) => isErrorUnknown(error),
});
const { mutate: updateOptionList } = useUpdateOptionListMutation(org, app);
const { mutate: updateOptionListId } = useUpdateOptionListIdMutation(org, app);

if (optionListsPending)
return <StudioPageSpinner spinnerTitle={t('general.loading')}></StudioPageSpinner>;

const codeLists = convertOptionListsToCodeLists(optionLists);

const handleUpdateCodeListId = (optionListId: string, newOptionListId: string) => {
updateOptionListId({ optionListId, newOptionListId });
};

const handleUpload = (file: File) => {
uploadOptionList(file, {
onSuccess: () => {
Expand All @@ -52,6 +61,7 @@ export function AppContentLibrary(): React.ReactElement {
codeList: {
props: {
codeLists: codeLists,
onUpdateCodeListId: handleUpdateCodeListId,
onUpdateCodeList: handleUpdate,
onUploadCodeList: handleUpload,
fetchDataError: optionListsError,
Expand Down
4 changes: 4 additions & 0 deletions frontend/language/src/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
"api_errors.ResourceNotFound": "Fant ikke en fil som appen din prøvde å få tak i.",
"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": "Kodelistenavn: {{codeListTitle}}",
"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_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.",
"app_content_library.code_lists.code_lists_count_info_single": "Det finnes <bold>1</bold> kodeliste i biblioteket.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const mockPagesConfig: PagesConfig = {
{ title: 'CodeList1', codeList: [] },
{ title: 'CodeList2', codeList: [] },
],
onUpdateCodeListId: () => {},
onUpdateCodeList: () => {},
onUploadCodeList: () => {},
fetchDataError: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,47 @@ import React from 'react';
import { render, screen } from '@testing-library/react';
import type { CodeListPageProps, CodeListWithMetadata } from './CodeListPage';
import { CodeListPage } from './CodeListPage';
import userEvent from '@testing-library/user-event';
import type { UserEvent } from '@testing-library/user-event';
import { textMock } from '@studio/testing/mocks/i18nMock';
import type { CodeList as StudioComponentCodeList } from '@studio/components';

const onUpdateCodeListIdMock = jest.fn();
const onUpdateCodeListMock = jest.fn();
const onUploadCodeListMock = jest.fn();
const codeListName = 'codeList';
const codeListMock: CodeListWithMetadata = {
const codeListMock: StudioComponentCodeList = [{ value: 'value', label: 'label' }];
const codeListWithMetadataMock: CodeListWithMetadata = {
title: codeListName,
codeList: [{ value: 'value', label: 'label' }],
codeList: codeListMock,
};
const uploadedCodeListName = 'uploadedCodeListName';

describe('CodeListPage', () => {
afterEach(() => {
defaultCodeListPageProps.codeLists = [codeListWithMetadataMock];
defaultCodeListPageProps.fetchDataError = false;
jest.clearAllMocks();
});

it('renders the codeList heading', () => {
renderCodeList();
renderCodeListPage();
const codeListHeading = screen.getByRole('heading', {
name: textMock('app_content_library.code_lists.page_name'),
});
expect(codeListHeading).toBeInTheDocument();
});

it('renders a code list counter message', () => {
renderCodeList();
renderCodeListPage();
const codeListCounterMessage = screen.getByText(
textMock('app_content_library.code_lists.code_lists_count_info_single'),
);
expect(codeListCounterMessage).toBeInTheDocument();
});

it('renders code list actions', () => {
renderCodeList();
renderCodeListPage();
const codeListSearchField = screen.getByRole('searchbox');
const codeListCreatButton = screen.getByRole('button', {
name: textMock('app_content_library.code_lists.create_new_code_list'),
Expand All @@ -43,37 +55,127 @@ describe('CodeListPage', () => {
expect(codeListUploadButton).toBeInTheDocument();
});

it('renders the code list as a clickable element', () => {
renderCodeList();
const codeListAccordion = screen.getByRole('button', { name: codeListName });
it('renders the code list accordion', () => {
renderCodeListPage();
const codeListAccordion = screen.getByTitle(
textMock('app_content_library.code_lists.code_list_accordion_title', {
codeListTitle: codeListName,
}),
);
expect(codeListAccordion).toBeInTheDocument();
});

it('render the code list accordion as default open when uploading a code list', async () => {
const user = userEvent.setup();
const { rerender } = renderCodeListPage();
const codeListAccordionClosed = screen.getByRole('button', {
name: codeListName,
expanded: false,
});
expect(codeListAccordionClosed).toHaveAttribute('aria-expanded', 'false');
await uploadCodeList(user, uploadedCodeListName);
defaultCodeListPageProps.codeLists.push({
title: uploadedCodeListName,
codeList: codeListMock,
});
rerender(
<CodeListPage
codeLists={defaultCodeListPageProps.codeLists}
onUpdateCodeListId={onUpdateCodeListIdMock}
onUpdateCodeList={onUpdateCodeListMock}
onUploadCodeList={onUploadCodeListMock}
fetchDataError={defaultCodeListPageProps.fetchDataError}
/>,
);
const codeListAccordionOpen = screen.getByRole('button', {
name: uploadedCodeListName,
expanded: true,
});
expect(codeListAccordionOpen).toHaveAttribute('aria-expanded', 'true');
});

it('renders error message if error fetching option lists occurred', () => {
renderCodeList({ fetchDataError: true });
renderCodeListPage({ fetchDataError: true });
const errorMessage = screen.getByText(textMock('app_content_library.code_lists.fetch_error'));
expect(errorMessage).toBeInTheDocument();
});

it('calls onUpdateCodeListId when Id is changed', async () => {
const user = userEvent.setup();
renderCodeListPage();
await changeCodeListId(user, codeListName);
expect(onUpdateCodeListIdMock).toHaveBeenCalledTimes(1);
expect(onUpdateCodeListIdMock).toHaveBeenCalledWith(codeListName, codeListName + '2');
});

it('calls onUpdateCodeList when code list is changed', async () => {
const user = userEvent.setup();
const newValueText = 'newValueText';
renderCodeListPage();
await changeCodeListContent(user, newValueText);
expect(onUpdateCodeListMock).toHaveBeenCalledTimes(1);
expect(onUpdateCodeListMock).toHaveBeenLastCalledWith({
...codeListWithMetadataMock,
codeList: [{ ...codeListWithMetadataMock.codeList[0], value: newValueText }],
});
});

it('calls onUploadCodeList when uploading a code list', async () => {
const user = userEvent.setup();
renderCodeListPage();
await uploadCodeList(user);
expect(onUploadCodeListMock).toHaveBeenCalledTimes(1);
expect(onUploadCodeListMock).toHaveBeenCalledWith(expect.any(Object));
});
});

const defaultCodeListProps: CodeListPageProps = {
codeLists: [codeListMock],
onUpdateCodeList: onUpdateCodeListMock,
onUploadCodeList: onUploadCodeListMock,
const changeCodeListId = async (user: UserEvent, codeListNameToChange: string) => {
const codeListIdToggleTextfield = screen.getByTitle(
textMock('app_content_library.code_lists.code_list_view_id_title', {
codeListName: codeListNameToChange,
}),
);
await user.click(codeListIdToggleTextfield);
const codeListIdInput = screen.getByTitle(
textMock('app_content_library.code_lists.code_list_edit_id_title', {
codeListName: codeListNameToChange,
}),
);
await user.type(codeListIdInput, '2');
await user.tab();
};

const changeCodeListContent = async (user: UserEvent, newValueText: string) => {
const codeListFirstItemValue = screen.getByLabelText(
textMock('code_list_editor.value_item', { number: 1 }),
);
await user.type(codeListFirstItemValue, newValueText);
await user.tab();
};

const uploadCodeList = async (user: UserEvent, fileName: string = uploadedCodeListName) => {
const fileUploaderButton = screen.getByLabelText(
textMock('app_content_library.code_lists.upload_code_list'),
);
const file = new File(['test'], `${fileName}.json`, { type: 'application/json' });
await user.upload(fileUploaderButton, file);
};

const defaultCodeListPageProps: Partial<CodeListPageProps> = {
codeLists: [codeListWithMetadataMock],
fetchDataError: false,
};

const renderCodeList = ({
const renderCodeListPage = ({
codeLists,
onUpdateCodeList,
onUploadCodeList,
fetchDataError,
}: Partial<CodeListPageProps> = defaultCodeListProps) => {
render(
}: Partial<CodeListPageProps> = defaultCodeListPageProps) => {
return render(
<CodeListPage
codeLists={codeLists}
onUpdateCodeList={onUpdateCodeList}
onUploadCodeList={onUploadCodeList}
onUpdateCodeListId={onUpdateCodeListIdMock}
onUpdateCodeList={onUpdateCodeListMock}
onUploadCodeList={onUploadCodeListMock}
fetchDataError={fetchDataError}
/>,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from 'react';
import React, { useState } from 'react';
import { StudioHeading, StudioPageError } from '@studio/components';
import type { CodeList as StudioComponentCodeList } from '@studio/components';
import { useTranslation } from 'react-i18next';
import { CodeListsActionsBar } from './CodeListsActionsBar';
import { CodeLists } from './CodeLists';
import { CodeListsCounterMessage } from './CodeListsCounterMessage';
import classes from './CodeListPage.module.css';
import { ArrayUtils } from '@studio/pure-functions';
import { ArrayUtils, FileNameUtils } from '@studio/pure-functions';

export type CodeListWithMetadata = {
codeList: StudioComponentCodeList;
Expand All @@ -15,33 +15,52 @@ export type CodeListWithMetadata = {

export type CodeListPageProps = {
codeLists: CodeListWithMetadata[];
onUpdateCodeListId: (codeListId: string, newCodeListId: string) => void;
onUpdateCodeList: (updatedCodeList: CodeListWithMetadata) => void;
onUploadCodeList: (uploadedCodeList: File) => void;
fetchDataError: boolean;
};
export function CodeListPage({
codeLists,
onUpdateCodeListId,
onUpdateCodeList,
onUploadCodeList,
fetchDataError,
}: CodeListPageProps): React.ReactElement {
const { t } = useTranslation();
const [codeListInEditMode, setCodeListInEditMode] = useState<string>(undefined);

if (fetchDataError)
return <StudioPageError message={t('app_content_library.code_lists.fetch_error')} />;

const codeListTitles = ArrayUtils.mapByKey<CodeListWithMetadata, 'title'>(codeLists, 'title');

const handleUploadCodeList = (uploadedCodeList: File) => {
setCodeListInEditMode(FileNameUtils.removeExtension(uploadedCodeList.name));
onUploadCodeList(uploadedCodeList);
};

const handleUpdateCodeListId = (codeListId: string, newCodeListId: string) => {
setCodeListInEditMode(newCodeListId);
onUpdateCodeListId(codeListId, newCodeListId);
};

return (
<div className={classes.codeListsContainer}>
<StudioHeading size='small'>{t('app_content_library.code_lists.page_name')}</StudioHeading>
<CodeListsCounterMessage codeListsCount={codeLists.length} />
<CodeListsActionsBar
onUploadCodeList={onUploadCodeList}
onUploadCodeList={handleUploadCodeList}
onUpdateCodeList={onUpdateCodeList}
codeListNames={codeListTitles}
/>
<CodeLists
codeLists={codeLists}
onUpdateCodeListId={handleUpdateCodeListId}
onUpdateCodeList={onUpdateCodeList}
codeListInEditMode={codeListInEditMode}
codeListNames={codeListTitles}
/>
<CodeLists codeLists={codeLists} onUpdateCodeList={onUpdateCodeList} />
</div>
);
}
Loading

0 comments on commit 71d2e2c

Please sign in to comment.