Skip to content

Commit ce58b90

Browse files
authored
fix: Add text resources to create code list dialog (#14646)
1 parent a5839e1 commit ce58b90

File tree

12 files changed

+190
-86
lines changed

12 files changed

+190
-86
lines changed

frontend/libs/studio-components/src/components/StudioTextResourceInput/StudioTextResourceInput.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type TextResourceInputPropsBase = {
2727
currentIdClass?: string;
2828
inputClass?: string;
2929
onChangeCurrentId: (id: string | null) => void;
30-
onChangeTextResource: (textResource: TextResource) => void;
30+
onChangeTextResource?: (textResource: TextResource) => void;
3131
textResources: TextResource[];
3232
texts: TextResourceInputTexts;
3333
toggleClass?: string;
@@ -62,7 +62,7 @@ export const StudioTextResourceInput = forwardRef<HTMLInputElement, StudioTextRe
6262
const handleTextResourceChange = (newTextResource: TextResource): void => {
6363
const newList = changeTextResourceInList(textResources, newTextResource);
6464
setTextResources(newList);
65-
onChangeTextResource(newTextResource);
65+
onChangeTextResource?.(newTextResource);
6666
};
6767

6868
const rootClass = cn(givenClass, classes.container);

frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeListPage.test.tsx

+75-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
} from '../../../../test-data/codeListDataList';
1414
import { ArrayUtils } from '@studio/pure-functions';
1515
import { label1ResourceNb, textResources } from '../../../../test-data/textResources';
16+
import type { TextResource } from '../../../../types/TextResource';
1617
import type { TextResourceWithLanguage } from '../../../../types/TextResourceWithLanguage';
1718

1819
const onDeleteCodeList = jest.fn();
@@ -182,6 +183,41 @@ describe('CodeListPage', () => {
182183
expect(onUpdateTextResource).toHaveBeenCalledTimes(newLabel.length);
183184
expect(onUpdateTextResource).toHaveBeenLastCalledWith(expectedObject);
184185
});
186+
187+
it('Renders with text resources in the input fields of the create dialog when given', async () => {
188+
const user = userEvent.setup();
189+
190+
renderCodeListPage({ textResources });
191+
const dialog = await openCreateDialog(user);
192+
await addCodeListItem(user, dialog);
193+
await openSearchModeForFirstLabel(user, dialog);
194+
await openFirstLabelCombobox(user, dialog);
195+
196+
expect(getTextResourceOption(label1ResourceNb)).toBeInTheDocument();
197+
});
198+
199+
it('Calls onUpdateTextResource with the new text resource and the default language when a text resource is changed in the create dialog', async () => {
200+
const user = userEvent.setup();
201+
const onUpdateTextResource = jest.fn();
202+
const newLabel = 'Ny ledetekst';
203+
204+
renderCodeListPage({ textResources, onUpdateTextResource });
205+
const dialog = await openCreateDialog(user);
206+
await addCodeListItem(user, dialog);
207+
await openSearchModeForFirstLabel(user, dialog);
208+
await openFirstLabelCombobox(user, dialog);
209+
await user.click(getTextResourceOption(label1ResourceNb));
210+
await openEditModeForFirstLabel(user, dialog);
211+
await user.type(getFirstLabelField(dialog), newLabel);
212+
213+
const expectedLanguage = 'nb';
214+
const expectedObject: TextResourceWithLanguage = {
215+
language: expectedLanguage,
216+
textResource: { ...label1ResourceNb, value: newLabel },
217+
};
218+
expect(onUpdateTextResource).toHaveBeenCalledTimes(newLabel.length);
219+
expect(onUpdateTextResource).toHaveBeenLastCalledWith(expectedObject);
220+
});
185221
});
186222

187223
const uploadCodeList = async (user: UserEvent, fileName: string): Promise<void> => {
@@ -198,8 +234,12 @@ const openAndGetFirstLabelField = async (
198234
): Promise<HTMLElement> => {
199235
await user.click(getCodeListHeading(codeListTitle));
200236
const accordion = getCodeListAccordion(codeListTitle);
237+
return getFirstLabelField(accordion);
238+
};
239+
240+
const getFirstLabelField = (area: HTMLElement): HTMLElement => {
201241
const labelFieldLabel = textMock('code_list_editor.text_resource.label.value', { number: 1 });
202-
return within(accordion).getByRole('textbox', { name: labelFieldLabel });
242+
return within(area).getByRole('textbox', { name: labelFieldLabel });
203243
};
204244

205245
const getCodeListAccordion = (codeListTitle: string): HTMLElement =>
@@ -215,3 +255,37 @@ const queryCodeListHeading = (codeListTitle: string): HTMLElement =>
215255

216256
const renderCodeListPage = (props: Partial<CodeListPageProps> = {}): RenderResult =>
217257
render(<CodeListPage {...defaultCodeListPageProps} {...props} />);
258+
259+
const openCreateDialog = async (user: UserEvent): Promise<HTMLElement> => {
260+
const createButtonLabel = textMock('app_content_library.code_lists.create_new_code_list');
261+
await user.click(screen.getByRole('button', { name: createButtonLabel }));
262+
return screen.getByRole('dialog');
263+
};
264+
265+
const addCodeListItem = async (user: UserEvent, area: HTMLElement): Promise<void> => {
266+
const addButtonLabel = textMock('code_list_editor.add_option');
267+
await user.click(within(area).getByRole('button', { name: addButtonLabel }));
268+
};
269+
270+
const openSearchModeForFirstLabel = async (user: UserEvent, area: HTMLElement): Promise<void> => {
271+
const radioLabel = textMock('code_list_editor.text_resource.label.search_mode', { number: 1 });
272+
const radio = within(area).getByRole('radio', { name: radioLabel });
273+
await user.click(radio);
274+
};
275+
276+
const openEditModeForFirstLabel = async (user: UserEvent, area: HTMLElement): Promise<void> => {
277+
const radioLabel = textMock('code_list_editor.text_resource.label.edit_mode', { number: 1 });
278+
const radio = await within(area).findByRole('radio', { name: radioLabel });
279+
await user.click(radio);
280+
};
281+
282+
const openFirstLabelCombobox = async (user: UserEvent, area: HTMLElement): Promise<void> => {
283+
const comboboxLabel = textMock('code_list_editor.text_resource.label.select', { number: 1 });
284+
const combobox = within(area).getByRole('combobox', { name: comboboxLabel });
285+
await user.click(combobox);
286+
};
287+
288+
const getTextResourceOption = (textResource: TextResource): HTMLElement =>
289+
screen.getByRole('option', { name: retrieveOptionName(textResource) });
290+
291+
const retrieveOptionName = ({ value, id }: TextResource): string => `${value} ${id}`;

frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeListPage.tsx

+26-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1-
import React, { useMemo, useState } from 'react';
1+
import React, { useCallback, useMemo, useState } from 'react';
2+
import type { CodeList, TextResource } from '@studio/components';
23
import { StudioHeading } from '@studio/components';
3-
import type { CodeList } from '@studio/components';
44
import { useTranslation } from 'react-i18next';
55
import { CodeListsActionsBar } from './CodeListsActionsBar';
66
import { CodeLists } from './CodeLists';
77
import { CodeListsCounterMessage } from './CodeListsCounterMessage';
88
import classes from './CodeListPage.module.css';
99
import { ArrayUtils, FileNameUtils } from '@studio/pure-functions';
1010
import type { CodeListReference } from './types/CodeListReference';
11-
import { filterCodeLists } from './utils';
11+
import {
12+
filterCodeLists,
13+
getTextResourcesForLanguage,
14+
createTextResourceWithLanguage,
15+
} from './utils';
1216
import type { TextResourceWithLanguage } from '../../../../types/TextResourceWithLanguage';
1317
import type { TextResources } from '../../../../types/TextResources';
1418

@@ -53,6 +57,19 @@ export function CodeListPage({
5357
[codeListsData, searchString],
5458
);
5559

60+
const textResourcesForLanguage = useMemo(
61+
() => getTextResourcesForLanguage(language, textResources),
62+
[textResources],
63+
);
64+
65+
const handleChangeTextResource = useCallback(
66+
(textResource: TextResource) => {
67+
const updatedTextResource = createTextResourceWithLanguage(language, textResource);
68+
onUpdateTextResource?.(updatedTextResource);
69+
},
70+
[onUpdateTextResource],
71+
);
72+
5673
const codeListTitles = ArrayUtils.mapByKey<CodeListData, 'title'>(codeListsData, 'title');
5774

5875
const handleUploadCodeList = (uploadedCodeList: File) => {
@@ -70,22 +87,26 @@ export function CodeListPage({
7087
<StudioHeading size='small'>{t('app_content_library.code_lists.page_name')}</StudioHeading>
7188
<CodeListsCounterMessage codeListsCount={codeListsData.length} />
7289
<CodeListsActionsBar
90+
onChangeTextResource={handleChangeTextResource}
7391
onUploadCodeList={handleUploadCodeList}
7492
onUpdateCodeList={onUpdateCodeList}
7593
codeListNames={codeListTitles}
7694
onSetSearchString={setSearchString}
95+
textResources={textResourcesForLanguage}
7796
/>
7897
<CodeLists
7998
codeListsData={filteredCodeLists}
99+
onChangeTextResource={handleChangeTextResource}
80100
onDeleteCodeList={onDeleteCodeList}
81101
onUpdateCodeListId={handleUpdateCodeListId}
82102
onUpdateCodeList={onUpdateCodeList}
83-
onUpdateTextResource={onUpdateTextResource}
84103
codeListInEditMode={codeListInEditMode}
85104
codeListNames={codeListTitles}
86105
codeListsUsages={codeListsUsages}
87-
textResources={textResources}
106+
textResources={textResourcesForLanguage}
88107
/>
89108
</div>
90109
);
91110
}
111+
112+
const language: string = 'nb'; // Todo: Let the user choose the language: https://github.com/Altinn/altinn-studio/issues/14572

frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/CodeLists.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,18 @@ import { Trans, useTranslation } from 'react-i18next';
77
import type { CodeListIdSource, CodeListReference } from '../types/CodeListReference';
88
import classes from './CodeLists.module.css';
99
import { getCodeListSourcesById, getCodeListUsageCount } from '../utils';
10-
import type { TextResourceWithLanguage } from '../../../../../types/TextResourceWithLanguage';
11-
import type { TextResources } from '../../../../../types/TextResources';
10+
import type { TextResource } from '@studio/components';
1211

1312
export type CodeListsProps = {
1413
codeListsData: CodeListData[];
14+
onChangeTextResource?: (textResource: TextResource) => void;
1515
onDeleteCodeList: (codeListId: string) => void;
1616
onUpdateCodeListId: (codeListId: string, newCodeListId: string) => void;
1717
onUpdateCodeList: (updatedCodeList: CodeListWithMetadata) => void;
18-
onUpdateTextResource?: (textResource: TextResourceWithLanguage) => void;
1918
codeListInEditMode: string | undefined;
2019
codeListNames: string[];
2120
codeListsUsages: CodeListReference[];
22-
textResources?: TextResources;
21+
textResources?: TextResource[];
2322
};
2423

2524
export function CodeLists({

frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/EditCodeList/EditCodeList.tsx

+7-26
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import type { CodeList, CodeListEditorTexts } from '@studio/components';
1+
import type { CodeList, CodeListEditorTexts, TextResource } from '@studio/components';
22
import {
33
StudioDeleteButton,
44
StudioModal,
55
StudioDisplayTile,
66
StudioCodeListEditor,
77
StudioToggleableTextfield,
88
} from '@studio/components';
9-
import React, { useCallback, useMemo } from 'react';
9+
import React from 'react';
1010
import { useTranslation } from 'react-i18next';
1111
import type { CodeListWithMetadata } from '../../CodeListPage';
1212
import { useCodeListEditorTexts } from '../../hooks/useCodeListEditorTexts';
@@ -16,43 +16,32 @@ import { useInputCodeListNameErrorMessage } from '../../hooks/useInputCodeListNa
1616
import classes from './EditCodeList.module.css';
1717
import type { CodeListIdSource } from '../../types/CodeListReference';
1818
import { CodeListUsages } from './CodeListUsages/CodeListUsages';
19-
import type { TextResources } from '../../../../../../types/TextResources';
20-
import type { TextResource } from '../../../../../../types/TextResource';
21-
import type { TextResourceWithLanguage } from '../../../../../../types/TextResourceWithLanguage';
22-
import { createTextResourceWithLanguage, getTextResourcesForLanguage } from './utils';
2319

2420
export type EditCodeListProps = {
2521
codeList: CodeList;
2622
codeListTitle: string;
23+
onChangeTextResource?: (textResource: TextResource) => void;
2724
onDeleteCodeList: (codeListId: string) => void;
2825
onUpdateCodeListId: (codeListId: string, newCodeListId: string) => void;
2926
onUpdateCodeList: (updatedCodeList: CodeListWithMetadata) => void;
30-
onUpdateTextResource?: (textResource: TextResourceWithLanguage) => void;
3127
codeListNames: string[];
3228
codeListSources: CodeListIdSource[];
33-
textResources?: TextResources;
29+
textResources?: TextResource[];
3430
};
3531

36-
const language: string = 'nb'; // Todo: Let the user choose the language: https://github.com/Altinn/altinn-studio/issues/14572
37-
3832
export function EditCodeList({
3933
codeList,
4034
codeListTitle,
35+
onChangeTextResource,
4136
onDeleteCodeList,
4237
onUpdateCodeListId,
4338
onUpdateCodeList,
44-
onUpdateTextResource,
4539
codeListNames,
4640
codeListSources,
4741
textResources,
4842
}: EditCodeListProps): React.ReactElement {
4943
const editorTexts: CodeListEditorTexts = useCodeListEditorTexts();
5044

51-
const textResourcesForLanguage = useMemo(
52-
() => getTextResourcesForLanguage(language, textResources),
53-
[textResources],
54-
);
55-
5645
const handleCodeListChange = (updatedCodeList: CodeList): void => {
5746
const updatedCodeListWithMetadata = updateCodeListWithMetadata(
5847
{ title: codeListTitle, codeList: codeList },
@@ -63,14 +52,6 @@ export function EditCodeList({
6352

6453
const handleDeleteCodeList = (): void => onDeleteCodeList(codeListTitle);
6554

66-
const handleChangeTextResource = useCallback(
67-
(textResource: TextResource) => {
68-
const updatedTextResource = createTextResourceWithLanguage(language, textResource);
69-
onUpdateTextResource?.(updatedTextResource);
70-
},
71-
[onUpdateTextResource],
72-
);
73-
7455
const codeListHasUsages = codeListSources.length > 0;
7556
const isCodeListEditable = codeListSources.length === 0;
7657

@@ -86,9 +67,9 @@ export function EditCodeList({
8667
codeList={codeList}
8768
onAddOrDeleteItem={handleCodeListChange}
8869
onBlurAny={handleCodeListChange}
89-
onChangeTextResource={handleChangeTextResource}
70+
onChangeTextResource={onChangeTextResource}
9071
texts={editorTexts}
91-
textResources={textResourcesForLanguage}
72+
textResources={textResources}
9273
/>
9374
<CodeListButtons
9475
codeListHasUsages={codeListHasUsages}

frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/EditCodeList/utils.test.ts

-32
This file was deleted.

frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/EditCodeList/utils.ts

-13
This file was deleted.

frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeListsActionsBar/CodeListsActionsBar.tsx

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from 'react';
2+
import type { TextResource } from '@studio/components';
23
import { StudioFileUploader, StudioSearch } from '@studio/components';
34
import type { ChangeEvent } from 'react';
45
import classes from './CodeListsActionsBar.module.css';
@@ -10,17 +11,21 @@ import { useUploadCodeListNameErrorMessage } from '../hooks/useUploadCodeListNam
1011
import { toast } from 'react-toastify';
1112

1213
export type CodeListsActionsBarProps = {
14+
onChangeTextResource?: (textResource: TextResource) => void;
1315
onUploadCodeList: (updatedCodeList: File) => void;
1416
onUpdateCodeList: (updatedCodeList: CodeListWithMetadata) => void;
1517
codeListNames: string[];
1618
onSetSearchString: (searchString: string) => void;
19+
textResources?: TextResource[];
1720
};
1821

1922
export function CodeListsActionsBar({
23+
onChangeTextResource,
2024
onUploadCodeList,
2125
onUpdateCodeList,
2226
codeListNames,
2327
onSetSearchString,
28+
textResources,
2429
}: CodeListsActionsBarProps) {
2530
const { t } = useTranslation();
2631
const getInvalidUploadFileNameErrorMessage = useUploadCodeListNameErrorMessage();
@@ -49,7 +54,12 @@ export function CodeListsActionsBar({
4954
clearButtonLabel={t('app_content_library.code_lists.clear_search_button_label')}
5055
onClear={handleClearSearch}
5156
/>
52-
<CreateNewCodeListModal onUpdateCodeList={onUpdateCodeList} codeListNames={codeListNames} />
57+
<CreateNewCodeListModal
58+
codeListNames={codeListNames}
59+
onChangeTextResource={onChangeTextResource}
60+
onUpdateCodeList={onUpdateCodeList}
61+
textResources={textResources}
62+
/>
5363
<StudioFileUploader
5464
accept='.json'
5565
size='small'

0 commit comments

Comments
 (0)