Skip to content

Commit

Permalink
Updates from review
Browse files Browse the repository at this point in the history
  • Loading branch information
Konrad-Simso committed Nov 4, 2024
1 parent 7092614 commit 9e70770
Show file tree
Hide file tree
Showing 18 changed files with 228 additions and 327 deletions.
2 changes: 1 addition & 1 deletion frontend/language/src/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -1462,7 +1462,7 @@
"ux_editor.modal_properties_code_list_helper": "Velg kodeliste",
"ux_editor.modal_properties_code_list_id": "Kodeliste-ID",
"ux_editor.modal_properties_code_list_item_description": "Beskrivelse for alternativ {{number}}",
"ux_editor.modal_properties_code_list_item_helpText": "Hjelpetekst for alternativ {{number}}",
"ux_editor.modal_properties_code_list_item_help_text": "Hjelpetekst for alternativ {{number}}",
"ux_editor.modal_properties_code_list_item_label": "Ledetekst for alternativ {{number}}",
"ux_editor.modal_properties_code_list_item_value": "Verdi for alternativ {{number}}",
"ux_editor.modal_properties_code_list_open_editor": "Åpne redigeringsverktøy",
Expand Down
2 changes: 1 addition & 1 deletion frontend/packages/shared/src/api/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export const updateAppMetadata = (org: string, app: string, payload: Application
export const updateAppConfig = (org: string, app: string, payload: AppConfig) => post(serviceConfigPath(org, app), payload);
export const uploadDataModel = (org: string, app: string, form: FormData) => post<void, FormData>(dataModelsUploadPath(org, app), form, { headers: { 'Content-Type': 'multipart/form-data' } });
export const uploadOptionList = (org: string, app: string, payload: FormData) => post<void, FormData>(optionListUploadPath(org, app), payload, { headers: { 'Content-Type': 'multipart/form-data' } });
export const updateOptionList = (org: string, app: string, optionsListId: string, payload: Option[]) => put(optionListUpdatePath(org, app, optionsListId), payload);
export const updateOptionList = (org: string, app: string, optionsListId: string, payload: Option[]) => put<void, Option[]>(optionListUpdatePath(org, app, optionsListId), payload);
export const upsertTextResources = (org: string, app: string, language: string, payload: ITextResourcesObjectFormat) => put<ITextResourcesObjectFormat>(textResourcesPath(org, app, language), payload);

// Resourceadm
Expand Down
9 changes: 0 additions & 9 deletions frontend/packages/shared/src/mocks/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import type { Organization } from 'app-shared/types/Organization';
import type { KubernetesDeployment } from 'app-shared/types/api/KubernetesDeployment';
import type { DeploymentsResponse } from 'app-shared/types/api/DeploymentsResponse';
import type { AppRelease } from 'app-shared/types/AppRelease';
import type { Option } from 'app-shared/types/Option';

export const build: Build = {
id: '',
Expand Down Expand Up @@ -245,11 +244,3 @@ export const searchRepositoryResponse: SearchRepositoryResponse = {
totalCount: 0,
totalPages: 0,
};

export const optionListResponse: Map<string, Option[]> = new Map([
[
'key1',
[{ value: 'test', label: 'test label', description: 'description', helpText: 'help text' }],
],
['key2', [{ value: 'test2', label: 'test2 label' }]],
]);
3 changes: 1 addition & 2 deletions frontend/packages/shared/src/mocks/queriesMock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ import {
createRepoCommitPayload,
dataModelMetadataResponse,
layoutSets,
optionListResponse,
orgList,
policy,
repoStatus,
Expand Down Expand Up @@ -106,7 +105,7 @@ export const queriesMock: ServicesContextProps = {
getOptionListIds: jest.fn().mockImplementation(() => Promise.resolve<string[]>([])),
getOptionLists: jest
.fn()
.mockImplementation(() => Promise.resolve<Map<string, Option[]>>(optionListResponse)),
.mockImplementation(() => Promise.resolve<Map<string, Option[]>>(new Map<string, Option[]>())),
getOrgList: jest.fn().mockImplementation(() => Promise.resolve<OrgList>(orgList)),
getOrganizations: jest.fn().mockImplementation(() => Promise.resolve<Organization[]>([])),
getRepoMetadata: jest.fn().mockImplementation(() => Promise.resolve<Repository>(repository)),
Expand Down
10 changes: 4 additions & 6 deletions frontend/packages/shared/src/types/Option.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
export type Option<T extends string | boolean | number = string | boolean | number> = {
label: string;
value: T;
description?: string;
helpText?: string;
};
import type { CodeListItem } from '@studio/components';

export type Option<T extends string | boolean | number = string | boolean | number> =
CodeListItem<T>;
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { screen, waitForElementToBeRemoved } from '@testing-library/react';
import { ComponentType } from 'app-shared/types/ComponentType';
import type { FormComponent } from '../../../../../types/FormComponent';
import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
import {
type ServicesContextProps,
ServicesContextProvider,
} from 'app-shared/contexts/ServicesContext';
import { CodeListTableEditor } from './CodeListTableEditor';
import { CodeListEditor } from './CodeListEditor';
import { textMock } from '@studio/testing/mocks/i18nMock';
import { PreviewContext, type PreviewContextProps } from 'app-development/contexts/PreviewContext';
import { queriesMock } from 'app-shared/mocks/queriesMock';
import userEvent, { type UserEvent } from '@testing-library/user-event';
import { componentMocks } from '@altinn/ux-editor/testing/componentMocks';
import type { Option } from 'app-shared/types/Option';
import { renderWithProviders } from '@altinn/ux-editor/testing/mocks';

// Test data:
const mockComponent: FormComponent<ComponentType.Dropdown> = {
id: 'c24d0812-0c34-4582-8f31-ff4ce9795e96',
type: ComponentType.Dropdown,
itemType: 'COMPONENT',
dataModelBindings: { simpleBinding: 'some-path' },
optionsId: 'test',
const mockComponent: FormComponent<ComponentType.Dropdown> = componentMocks[ComponentType.Dropdown];
mockComponent.optionsId = 'test';

const optionsList = new Map([
[
'text',
[{ value: 'test', label: 'label text', description: 'description', helpText: 'help text' }],
],
['number', [{ value: 2, label: 'label number' }]],
['boolean', [{ value: true, label: 'label boolean' }]],
]);
const queriesMock = {
getOptionLists: jest
.fn()
.mockImplementation(() => Promise.resolve<Map<string, Option[]>>(optionsList)),
};

const defaultPreviewContextProps: PreviewContextProps = {
shouldReloadPreview: false,
doReloadPreview: jest.fn(),
previewHasLoaded: jest.fn(),
};

const queryClientMock = createQueryClientMock();

describe('CodeListTableEditor', () => {
afterEach(() => {
queryClientMock.clear();
Expand All @@ -38,7 +38,7 @@ describe('CodeListTableEditor', () => {
await renderCodeListTableEditor();

expect(
await screen.findByRole('button', {
screen.getByRole('button', {
name: textMock('ux_editor.modal_properties_code_list_open_editor'),
}),
).toBeInTheDocument();
Expand Down Expand Up @@ -76,32 +76,25 @@ describe('CodeListTableEditor', () => {
});

const openModal = async (user: UserEvent) => {
const btnOpen = await screen.findByRole('button', {
const btnOpen = screen.getByRole('button', {
name: textMock('ux_editor.modal_properties_code_list_open_editor'),
});
await user.click(btnOpen);
};

const renderCodeListTableEditor = async ({
queries = {},
previewContextProps = {},
componentProps = {},
} = {}) => {
const allQueries: ServicesContextProps = {
...queries,
...queriesMock,
};

return render(
<ServicesContextProvider {...allQueries} client={createQueryClientMock()}>
<PreviewContext.Provider value={{ ...defaultPreviewContextProps, ...previewContextProps }}>
<CodeListTableEditor
component={{
...mockComponent,
...componentProps,
}}
/>
</PreviewContext.Provider>
</ServicesContextProvider>,
const renderCodeListTableEditor = async ({ previewContextProps = {} } = {}) => {
const view = renderWithProviders(
<CodeListEditor
component={{
...mockComponent,
}}
/>,
{
queries: queriesMock,
queryClient: queryClientMock,
previewContextProps: previewContextProps,
},
);
await waitForElementToBeRemoved(screen.queryByTestId('studio-spinner-test-id'));
return view;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import React, { createRef, useEffect, useState } from 'react';
import type { IGenericEditComponent } from '@altinn/ux-editor/components/config/componentConfig';
import type { SelectionComponentType } from '@altinn/ux-editor/types/FormComponent';
import type { Option } from 'app-shared/types/Option';
import type { ApiError } from 'app-shared/types/api/ApiError';
import type { AxiosError } from 'axios';
import { useTranslation } from 'react-i18next';
import {
StudioCodeListEditor,
StudioModal,
StudioSpinner,
type CodeList,
} from '@studio/components';
import { TableIcon } from '@studio/icons';
import { useStudioEnvironmentParams } from 'app-shared/hooks/useStudioEnvironmentParams';
import { useUpdateOptionListMutation } from '../../../../../hooks/mutations/useUpdateOptionListMutation';
import { useOptionListsQuery } from '../../../../../hooks/queries/useOptionListsQuery';
import { useCodeListEditorTexts } from './hooks/useCodeListEditorTexts';
import { usePreviewContext } from 'app-development/contexts/PreviewContext';
import { ErrorMessage } from '@digdir/designsystemet-react';
import classes from './CodeListEditor.module.css';

type CodeListEditorProps = Pick<IGenericEditComponent<SelectionComponentType>, 'component'>;

export function CodeListEditor({ component }: CodeListEditorProps): React.ReactNode {
const { t } = useTranslation();
const { org, app } = useStudioEnvironmentParams();
const { data: optionsListMap, status, error } = useOptionListsQuery(org, app);
const [codeList, setCodeList] = useState<CodeList>([]);

useEffect(() => {
if (status === 'pending') return;
handleOptionsChange(optionsListMap[component.optionsId]);
}, [optionsListMap, component.optionsId, status]);

const handleOptionsChange = (options?: Option[]) => {
if (options === undefined) return;
setCodeList(options);

Check warning on line 38 in frontend/packages/ux-editor/src/components/config/editModal/EditOptions/EditCodeList/CodeListEditor.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/packages/ux-editor/src/components/config/editModal/EditOptions/EditCodeList/CodeListEditor.tsx#L38

Added line #L38 was not covered by tests
};

if (component.optionsId === undefined) return <StudioSpinner spinnerTitle={'test'} />;
switch (status) {
case 'pending':
return <StudioSpinner spinnerTitle={'test'} />; // Extract title to nb.json
case 'error':
return (

Check warning on line 46 in frontend/packages/ux-editor/src/components/config/editModal/EditOptions/EditCodeList/CodeListEditor.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/packages/ux-editor/src/components/config/editModal/EditOptions/EditCodeList/CodeListEditor.tsx#L46

Added line #L46 was not covered by tests
<ErrorMessage>
{error instanceof Error ? error.message : t('ux_editor.modal_properties_error_message')}
</ErrorMessage>
);
case 'success': {
return (
<CodeListEditorModal
codeList={codeList}
component={component}
handleOptionsChange={handleOptionsChange}
/>
);
}
}
}

type CodeListEditorModalProps = {
codeList: CodeList;
handleOptionsChange: (options?: Option[]) => void;
} & Pick<IGenericEditComponent<SelectionComponentType>, 'component'>;

function CodeListEditorModal({
codeList,
component,
handleOptionsChange,
}: CodeListEditorModalProps): React.ReactNode {
const { t } = useTranslation();
const { org, app } = useStudioEnvironmentParams();
const { doReloadPreview } = usePreviewContext();
const { mutate: uploadOptionList } = useUpdateOptionListMutation(org, app, {
hideDefaultError: (apiError: AxiosError<ApiError>) => !apiError.response.data.errorCode,

Check warning on line 77 in frontend/packages/ux-editor/src/components/config/editModal/EditOptions/EditCodeList/CodeListEditor.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/packages/ux-editor/src/components/config/editModal/EditOptions/EditCodeList/CodeListEditor.tsx#L77

Added line #L77 was not covered by tests
});
const editorTexts = useCodeListEditorTexts();
const modalRef = createRef<HTMLDialogElement>();

const handleClose = () => {
uploadOptionList({ optionListId: component.optionsId, optionsList: codeList });
doReloadPreview();
modalRef.current?.close();
};

return (
<StudioModal.Root>
<StudioModal.Trigger
className={classes.modalTrigger}
variant='secondary'
icon={<TableIcon />}
>
{t('ux_editor.modal_properties_code_list_open_editor')}
</StudioModal.Trigger>
<StudioModal.Dialog
ref={modalRef}
className={classes.manualTabModal}
closeButtonTitle={t('general.close')}
heading={t('ux_editor.modal_add_options_codelist')}
onBeforeClose={handleClose}
onInteractOutside={handleClose}
>
<StudioCodeListEditor
codeList={codeList}
onChange={handleOptionsChange}
texts={editorTexts}
/>
</StudioModal.Dialog>
</StudioModal.Root>
);
}

This file was deleted.

Loading

0 comments on commit 9e70770

Please sign in to comment.