Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,28 @@ describe('OrgContentLibraryPage', () => {
expect(updateOrgCodeLists).toHaveBeenCalledTimes(1);
expect(updateOrgCodeLists).toHaveBeenCalledWith(orgName, expectedPayload);
});

it('Publishes a code list when publish is triggered on the new code list page', async () => {
const publishCodeList = jest.fn();
renderOrgContentLibraryWithData({
featureFlags: [FeatureFlag.NewCodeLists],
queries: { publishCodeList },
});
const codeListToPublish = codeListsNewResponse.codeListWrappers[0];
const libraryCodeListData: LibraryCodeListData = {
name: codeListToPublish.title,
codes: codeListToPublish.codeList.codes,
};

retrievePagesConfig().codeLists.props.onPublish(libraryCodeListData);

await waitFor(expect(publishCodeList).toHaveBeenCalled);
expect(publishCodeList).toHaveBeenCalledTimes(1);
expect(publishCodeList).toHaveBeenCalledWith(
orgName,
expect.objectContaining({ title: codeListToPublish.title }),
);
});
});

function renderOrgContentLibraryWithData(providerData: ProviderData = {}): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import type { AxiosError } from 'axios';
import { useDeleteOrgCodeListMutation } from 'app-shared/hooks/mutations/useDeleteOrgCodeListMutation';
import {
backendCodeListsToLibraryCodeLists,
libraryCodeListDataToBackendCodeListData,
libraryCodeListsToUpdatePayload,
textResourcesWithLanguageToLibraryTextResources,
textResourceWithLanguageToMutationArgs,
Expand All @@ -49,6 +50,8 @@ import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse';
import { useOrgCodeListsNewQuery } from 'app-shared/hooks/queries/useOrgCodeListsNewQuery';
import type { CodeListsNewResponse } from 'app-shared/types/api/CodeListsNewResponse';
import { useOrgCodeListsMutation } from 'app-shared/hooks/mutations/useOrgCodeListsMutation';
import { usePublishCodeListMutation } from 'app-shared/hooks/mutations/usePublishCodeListMutation';
import type { PublishCodeListPayload } from 'app-shared/types/api/PublishCodeListPayload';

export function OrgContentLibraryPage(): ReactElement {
const selectedContext = useSelectedContext();
Expand Down Expand Up @@ -204,6 +207,7 @@ function usePagesFromFeatureFlags(orgName: string): Partial<PagesConfig> {
function useCodeListsProps(orgName: string): PagesConfig['codeLists']['props'] {
const { data } = useOrgCodeListsNewQuery(orgName);
const { mutate } = useOrgCodeListsMutation(orgName);
const { mutate: publish } = usePublishCodeListMutation(orgName);
const { t } = useTranslation();

const libraryCodeLists = backendCodeListsToLibraryCodeLists(data);
Expand All @@ -220,7 +224,15 @@ function useCodeListsProps(orgName: string): PagesConfig['codeLists']['props'] {
[data, mutate, t],
);

return { codeLists: libraryCodeLists, onSave: handleSave };
const handlePublish = useCallback(
(cld: CodeListData): void => {
const payload: PublishCodeListPayload = libraryCodeListDataToBackendCodeListData(cld);
publish(payload);
},
[publish],
);

return { codeLists: libraryCodeLists, onPublish: handlePublish, onSave: handleSave };
}

function ContextWithoutLibraryAccess(): ReactElement {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
backendCodeListsToLibraryCodeLists,
libraryCodeListDataToBackendCodeListData,
libraryCodeListsToUpdatePayload,
textResourcesWithLanguageToLibraryTextResources,
textResourceWithLanguageToMutationArgs,
Expand All @@ -16,6 +17,7 @@ import { codeListsNewResponse } from './test-data/codeListsNewResponse';
import { codeLists } from './test-data/codeLists';
import type { CodeListsNewResponse } from 'app-shared/types/api/CodeListsNewResponse';
import type { UpdateOrgCodeListsPayload } from 'app-shared/types/api/UpdateOrgCodeListsPayload';
import type { CodeListDataNew } from 'app-shared/types/CodeListDataNew';

describe('utils', () => {
describe('textResourceWithLanguageToMutationArgs', () => {
Expand Down Expand Up @@ -138,4 +140,19 @@ describe('utils', () => {
expect(result).toEqual(expectedResult);
});
});

describe('libraryCodeListDataToBackendCodeListData', () => {
it('Converts library code list data to backend code list data', () => {
const libraryCodeListData: CodeListData = {
name: 'animals',
codes: codeLists.animals,
};
const result = libraryCodeListDataToBackendCodeListData(libraryCodeListData);
const expectedResult: CodeListDataNew = {
title: 'animals',
codeList: { codes: codeLists.animals },
};
expect(result).toEqual(expectedResult);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,17 @@ export function libraryCodeListsToUpdatePayload(
}

function mapUpdatedLists(updatedCodeLists: LibraryCodeListData[]): DeletableCodeListData[] {
return updatedCodeLists.map(({ name, codes }) => ({
return updatedCodeLists.map(libraryCodeListDataToBackendCodeListData);
}

export function libraryCodeListDataToBackendCodeListData({
name,
codes,
}: LibraryCodeListData): Required<Pick<CodeListDataNew, 'title' | 'codeList'>> {
return {
title: name,
codeList: { codes },
}));
};
}

function extractDeletedLists(
Expand Down
1 change: 1 addition & 0 deletions src/Designer/frontend/language/src/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"app_content_library.code_lists.errors": "Kan ikke lagre kodelistene på grunn av følgende feil:",
"app_content_library.code_lists.name": "Navn",
"app_content_library.code_lists.page_name": "Kodelister (ny)",
"app_content_library.code_lists.publish": "Publiser",
"app_content_library.code_lists.unnamed": "Uten navn",
"app_content_library.code_lists_with_text_resources.add_new_code_list": "Legg til kodeliste",
"app_content_library.code_lists_with_text_resources.code_list_delete": "Slett kodeliste",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const mockPagesConfig: PagesConfig = {
codeLists: {
props: {
codeLists: [],
onPublish: () => {},
onSave: () => {},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
display: grid;
gap: var(--ds-size-6);
grid-template-areas:
'name-field delete-button'
'codes codes';
grid-template-columns: 1fr auto;
'name-field delete-button publish-button'
'codes codes codes';
grid-template-columns: 1fr auto auto;
}

.nameField {
Expand All @@ -17,6 +17,11 @@
justify-self: end;
}

.publishButton {
align-self: end;
grid-area: publish-button;
}

.codes {
grid-area: codes;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { userEvent } from '@testing-library/user-event';
const data = fruitsData;
const onUpdate = jest.fn();
const onDelete = jest.fn();
const defaultProps: CodeListDataEditorProps = { data, onUpdate, onDelete };
const onPublish = jest.fn();
const defaultProps: CodeListDataEditorProps = { data, onUpdate, onDelete, onPublish };

describe('CodeListDataEditor', () => {
beforeEach(jest.clearAllMocks);
Expand Down Expand Up @@ -68,6 +69,21 @@ describe('CodeListDataEditor', () => {
const placeholderText = textMock('app_content_library.code_lists.unnamed');
expect(screen.queryByText(placeholderText)).not.toBeInTheDocument();
});

it('Calls onPublish with the code list data when the publish button is clicked', async () => {
const user = userEvent.setup();
renderCodeListDataEditor();
const publishButtonName = textMock('app_content_library.code_lists.publish');
await user.click(screen.getByRole('button', { name: publishButtonName }));
expect(onPublish).toHaveBeenCalledTimes(1);
expect(onPublish).toHaveBeenCalledWith(data);
});

it('Disables the publish button when no name is given', () => {
renderCodeListDataEditor({ data: { ...data, name: '' } });
const publishButtonName = textMock('app_content_library.code_lists.publish');
expect(screen.getByRole('button', { name: publishButtonName })).toBeDisabled();
});
});

function renderCodeListDataEditor(props: Partial<CodeListDataEditorProps> = {}): RenderResult {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useCallback } from 'react';
import type { ChangeEventHandler, ReactElement, ReactNode } from 'react';
import { useCodeListEditorTexts } from '../useCodeListEditorTexts';
import {
StudioButton,
StudioCodeListEditor,
StudioDeleteButton,
StudioDetails,
Expand All @@ -15,13 +16,15 @@ import classes from './CodeListDataEditor.module.css';

export type CodeListDataEditorProps = Readonly<{
data: CodeListData;
onUpdate: (newData: CodeListData) => void;
onDelete: () => void;
onPublish: (data: CodeListData) => void;
onUpdate: (newData: CodeListData) => void;
}>;

export function CodeListDataEditor({
data,
onDelete,
onPublish,
onUpdate,
}: CodeListDataEditorProps): ReactElement {
const texts = useCodeListEditorTexts();
Expand All @@ -44,6 +47,10 @@ export function CodeListDataEditor({
[data, onUpdate],
);

const handlePublish = useCallback((): void => onPublish(data), [data, onPublish]);

const canPublish = !!data.name;

return (
<StudioDetails>
<StudioDetails.Summary>
Expand All @@ -59,6 +66,14 @@ export function CodeListDataEditor({
<StudioDeleteButton className={classes.deleteButton} onDelete={onDelete}>
{t('general.delete')}
</StudioDeleteButton>
<StudioButton
className={classes.publishButton}
disabled={!canPublish}
onClick={handlePublish}
variant='secondary'
>
{t('app_content_library.code_lists.publish')}
</StudioButton>
<StudioCodeListEditor
className={classes.codes}
codeList={data.codes}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@ import { textMock } from '@studio/testing/mocks/i18nMock';
import { codeLists, coloursData } from './test-data/codeLists';

// Test data:
const onPublish = jest.fn();
const onSave = jest.fn();
const defaultProps: CodeListsPageProps = { codeLists, onSave };
const defaultProps: CodeListsPageProps = { codeLists, onPublish, onSave };

describe('CodeListsPage', () => {
beforeEach(onSave.mockClear);
beforeEach(() => {
onPublish.mockClear();
onSave.mockClear();
});

it('Renders with the given code lists', () => {
renderCodeListPage();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ import { Errors } from './Errors';

export type CodeListsPageProps = {
codeLists: CodeListData[];
onPublish: (data: CodeListData) => void;
onSave: (data: CodeListData[]) => void;
};

export function CodeListsPage({ codeLists, onSave }: CodeListsPageProps): ReactElement {
export function CodeListsPage({ codeLists, onPublish, onSave }: CodeListsPageProps): ReactElement {
const { t } = useTranslation();
const [codeListMap, setCodeListMap] = useState<CodeListMap>(createCodeListMap(codeLists));
const [errors, setErrors] = useState<CodeListMapError[]>([]);
Expand Down Expand Up @@ -73,6 +74,7 @@ export function CodeListsPage({ codeLists, onSave }: CodeListsPageProps): ReactE
<ListOfCodeLists
codeListMap={codeListMap}
onDeleteCodeList={handleDeleteCodeList}
onPublish={onPublish}
onUpdateCodeListData={handleUpdateCodeListData}
/>
<Errors errors={errors} />
Expand All @@ -86,12 +88,14 @@ export function CodeListsPage({ codeLists, onSave }: CodeListsPageProps): ReactE
type ListOfCodeListsProps = Readonly<{
codeListMap: CodeListMap;
onDeleteCodeList: (key: string) => void;
onPublish: (data: CodeListData) => void;
onUpdateCodeListData: (key: string, newData: CodeListData) => void;
}>;

function ListOfCodeLists({
codeListMap,
onDeleteCodeList,
onPublish,
onUpdateCodeListData,
}: ListOfCodeListsProps): ReactElement {
const { t } = useTranslation();
Expand All @@ -107,6 +111,7 @@ function ListOfCodeLists({
data={data}
key={key}
onDelete={() => onDeleteCodeList(key)}
onPublish={onPublish}
onUpdate={(newData) => onUpdateCodeListData(key, newData)}
/>
))}
Expand Down
3 changes: 3 additions & 0 deletions src/Designer/frontend/packages/shared/src/api/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {
taskNavigationGroupPath,
orgCodeListUpdateIdPath,
orgCodeListsNewPath,
orgCodeListPublishPath,
} from 'app-shared/api/paths';
import type { AddLanguagePayload } from 'app-shared/types/api/AddLanguagePayload';
import type { AddRepoParams } from 'app-shared/types/api';
Expand Down Expand Up @@ -94,6 +95,7 @@ import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs';
import type { TaskNavigationGroup } from 'app-shared/types/api/dto/TaskNavigationGroup';
import type { ImportCodeListResponse } from 'app-shared/types/api/ImportCodeListResponse';
import type { UpdateOrgCodeListsPayload } from 'app-shared/types/api/UpdateOrgCodeListsPayload';
import type { PublishCodeListPayload } from 'app-shared/types/api/PublishCodeListPayload';

const headers = {
Accept: 'application/json',
Expand Down Expand Up @@ -128,6 +130,7 @@ export const deleteFormLayout = (org: string, app: string, layoutName: string, l
export const deleteLanguageCode = (org: string, app: string, language: string) => del(textResourcesPath(org, app, language));
export const generateModels = (org: string, app: string, modelPath: string, payload: JsonSchema) => put<void, JsonSchema>(dataModelPath(org, app, modelPath, false), payload);
export const logout = () => post(userLogoutPath());
export const publishCodeList = (org: string, payload: PublishCodeListPayload) => post<void, PublishCodeListPayload>(orgCodeListPublishPath(org), payload);
export const pushRepoChanges = (org: string, app: string) => post(repoPushPath(org, app));
export const resetRepoChanges = (org: string, app: string) => get(repoResetPath(org, app)); //Technically a mutation, but currently only implemented as a GET
export const saveDataModel = (org: string, app: string, modelPath: string, payload: JsonSchema) => put<void, JsonSchema>(dataModelPath(org, app, modelPath, true), payload);
Expand Down
1 change: 1 addition & 0 deletions src/Designer/frontend/packages/shared/src/api/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export const getImageFileNamesPath = (org, app) => `${apiBasePath}/${org}/${app}
export const orgCodeListsPath = (org) => `${apiBasePath}/${org}/code-lists`; // Get
export const orgCodeListsNewPath = (org) => `${apiBasePath}/${org}/code-lists/new`; // Get, Put
export const orgCodeListPath = (org, codeListId) => `${apiBasePath}/${org}/code-lists/${codeListId}`; // Post, Put, Delete
export const orgCodeListPublishPath = (org) => `${apiBasePath}/${org}/code-lists/new/publish`; // Post
export const orgCodeListUpdateIdPath = (org, codeListId) => `${apiBasePath}/${org}/code-lists/change-name/${codeListId}`;
export const orgCodeListUploadPath = (org) => `${apiBasePath}/${org}/code-lists/upload`; // Post
export const orgTextResourcesPath = (org, language) => `${apiBasePath}/${org}/text/language/${language}`; // Get, patch, post
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { renderHookWithProviders } from '../../mocks/renderHookWithProviders';
import { usePublishCodeListMutation } from '../../hooks/mutations/usePublishCodeListMutation';
import type { PublishCodeListPayload } from '../../types/api/PublishCodeListPayload';

// Test data:
const org = 'test-org';

describe('usePublishCodeListMutation', () => {
it('Calls publishCodeList with correct arguments and payload', async () => {
const publishCodeList = jest.fn();
const { result } = renderHookWithProviders(() => usePublishCodeListMutation(org), {
queries: { publishCodeList },
});
const payload: PublishCodeListPayload = {
title: 'Test Code List',
codeList: {
codes: [
{ value: '001', label: { nb: 'En' } },
{ value: '002', label: { nb: 'To' } },
],
},
};

await result.current.mutateAsync(payload);

expect(publishCodeList).toHaveBeenCalledTimes(1);
expect(publishCodeList).toHaveBeenCalledWith(org, payload);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useMutation } from '@tanstack/react-query';
import type { DefaultError, UseMutationResult } from '@tanstack/react-query';
import type { PublishCodeListPayload } from '../../types/api/PublishCodeListPayload';
import { useServicesContext } from '../../contexts/ServicesContext';

export function usePublishCodeListMutation(
orgName: string,
): UseMutationResult<void, DefaultError, PublishCodeListPayload> {
const { publishCodeList } = useServicesContext();
return useMutation<void, DefaultError, PublishCodeListPayload>({
mutationFn: (payload) => publishCodeList(orgName, payload),
});
}
Loading
Loading