Skip to content

Commit

Permalink
Merge branch 'main' into 13683-update-subform-tablecolumns-config-to-…
Browse files Browse the repository at this point in the history
…comply-with-ux-design
  • Loading branch information
Jondyr authored Oct 29, 2024
2 parents 4a0fee1 + 378bf66 commit 469d9bd
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 18 deletions.
3 changes: 3 additions & 0 deletions frontend/language/src/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -1318,6 +1318,9 @@
"ux_editor.component_properties.subform.choose_layout_set_description": " Før du kan bruke komponenten Tabell for underskjema, må du velge hvilket underskjema du skal bruke den med. Deretter kan du velge hvilke egenskaper komponenten skal ha.",
"ux_editor.component_properties.subform.choose_layout_set_header": "Velg underskjemaet du vil bruke",
"ux_editor.component_properties.subform.choose_layout_set_label": "Velg et underskjema",
"ux_editor.component_properties.subform.create_layout_set_button": "Lag et nytt underskjema",
"ux_editor.component_properties.subform.create_layout_set_description": "Hvis du velger å lage et nytt underskjema, oppretter vi et tomt underskjema for deg. Det må du selv utforme, før du kan sette opp tabellen.",
"ux_editor.component_properties.subform.created_layout_set_name": "Navn på underskjema",
"ux_editor.component_properties.subform.go_to_layout_set": "Gå til utforming av underskjemaet",
"ux_editor.component_properties.subform.no_layout_sets_acting_as_subform": "Det finnes ingen sidegrupper i løsningen som kan brukes som et underskjema",
"ux_editor.component_properties.subform.selected_layout_set_label": "Underskjema",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@

.description {
margin-top: var(--fds-spacing-2);
margin-bottom: var(--fds-spacing-4);
margin-bottom: var(--fds-spacing-1);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export type StudioRecommendedNextActionProps = {
saveButtonText?: string;
onSkip?: React.MouseEventHandler<HTMLButtonElement>;
skipButtonText?: string;
title: string;
description: string;
title?: string;
description?: string;
hideSaveButton?: boolean;
hideSkipButton?: boolean;
children: React.ReactNode;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.savelayoutSetButton {
display: flex;
align-self: flex-start;
border: 2px solid var(--success-color);
color: var(--success-color);
}

.headerIcon {
font-size: large;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from 'react';
import { renderWithProviders } from '../../../../../../testing/mocks';
import { CreateNewSubformLayoutSet } from './CreateNewSubformLayoutSet';
import type { ComponentType } from 'app-shared/types/ComponentType';
import { textMock } from '@studio/testing/mocks/i18nMock';
import { screen, waitFor } from '@testing-library/react';
import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
import { app, org } from '@studio/testing/testids';
import { QueryKey } from 'app-shared/types/QueryKey';
import { layoutSets } from 'app-shared/mocks/mocks';
import type { LayoutSets } from 'app-shared/types/api/LayoutSetsResponse';
import userEvent from '@testing-library/user-event';
import type { FormComponent } from '../../../../../../types/FormComponent';
import { AppContext } from '../../../../../../AppContext';
import { appContextMock } from '../../../../../../testing/appContextMock';

const onSubFormCreatedMock = jest.fn();

describe('CreateNewSubformLayoutSet ', () => {
afterEach(jest.clearAllMocks);

it('displays the card with label and input field', () => {
renderCreateNewSubformLayoutSet();
const card = screen.getByRole('textbox', {
name: textMock('ux_editor.component_properties.subform.created_layout_set_name'),
});

expect(card).toBeInTheDocument();
});

it('displays the input field', () => {
renderCreateNewSubformLayoutSet();
const input = screen.getByRole('textbox');
expect(input).toBeInTheDocument();
});

it('displays the save button', () => {
renderCreateNewSubformLayoutSet();
const saveButton = screen.getByRole('button', { name: textMock('general.close') });
expect(saveButton).toBeInTheDocument();
});

it('calls onSubFormCreated when save button is clicked', async () => {
const user = userEvent.setup();
renderCreateNewSubformLayoutSet();
const input = screen.getByRole('textbox');
await user.type(input, 'NewSubForm');
const saveButton = screen.getByRole('button', { name: textMock('general.close') });
await user.click(saveButton);
await waitFor(() => expect(onSubFormCreatedMock).toHaveBeenCalledTimes(1));
expect(onSubFormCreatedMock).toHaveBeenCalledWith('NewSubForm');
});
});

const renderCreateNewSubformLayoutSet = (
layoutSetsMock: LayoutSets = layoutSets,
componentProps: Partial<FormComponent<ComponentType.Subform>> = {},
) => {
const queryClient = createQueryClientMock();
queryClient.setQueryData([QueryKey.LayoutSets, org, app], layoutSetsMock);
return renderWithProviders(
<AppContext.Provider value={{ ...appContextMock }}>
<CreateNewSubformLayoutSet onSubFormCreated={onSubFormCreatedMock} {...componentProps} />
</AppContext.Provider>,
{ queryClient },
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { StudioButton, StudioCard, StudioTextfield } from '@studio/components';
import { ClipboardIcon, CheckmarkIcon } from '@studio/icons';
import { useAddLayoutSetMutation } from 'app-development/hooks/mutations/useAddLayoutSetMutation';
import { useStudioEnvironmentParams } from 'app-shared/hooks/useStudioEnvironmentParams';
import classes from './CreateNewSubformLayoutSet.module.css';

type CreateNewSubformLayoutSetProps = {
onSubFormCreated: (layoutSetName: string) => void;
};

export const CreateNewSubformLayoutSet = ({
onSubFormCreated,
}: CreateNewSubformLayoutSetProps): React.ReactElement => {
const { t } = useTranslation();
const [newSubForm, setNewSubForm] = useState('');
const { org, app } = useStudioEnvironmentParams();
const { mutate: addLayoutSet } = useAddLayoutSetMutation(org, app);

const createNewSubform = () => {
if (!newSubForm) return;
addLayoutSet({
layoutSetIdToUpdate: newSubForm,
layoutSetConfig: {
id: newSubForm,
type: 'subform',
},
});
onSubFormCreated(newSubForm);
setNewSubForm('');
};

function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
setNewSubForm(e.target.value);
}

return (
<StudioCard>
<StudioCard.Content>
<StudioCard.Header>
<ClipboardIcon className={classes.headerIcon} />
</StudioCard.Header>
<StudioTextfield
label={t('ux_editor.component_properties.subform.created_layout_set_name')}
value={newSubForm}
size='sm'
onChange={handleChange}
/>
<StudioButton
className={classes.savelayoutSetButton}
icon={<CheckmarkIcon />}
onClick={createNewSubform}
title={t('general.close')}
variant='tertiary'
color='success'
/>
</StudioCard.Content>
</StudioCard>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { CreateNewSubformLayoutSet } from './CreateNewSubformLayoutSet';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.button {
padding-left: 0;
border-radius: var(--fds-sizing-1);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,29 @@ import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { DefinedLayoutSet } from './DefinedLayoutSet/DefinedLayoutSet';
import { SelectLayoutSet } from './SelectLayoutSet/SelectLayoutSet';
import { StudioRecommendedNextAction } from '@studio/components';
import { StudioParagraph, StudioProperty, StudioRecommendedNextAction } from '@studio/components';
import { PlusIcon } from '@studio/icons';
import classes from './EditLayoutSet.module.css';
import { CreateNewSubformLayoutSet } from './CreateNewSubformLayoutSet';

type EditLayoutSetProps = {
existingLayoutSetForSubform: string;
onUpdateLayoutSet: (layoutSetId: string) => void;
onSubFormCreated: (layoutSetName: string) => void;
};

export const EditLayoutSet = ({
existingLayoutSetForSubform,
onUpdateLayoutSet,
onSubFormCreated,
}: EditLayoutSetProps): React.ReactElement => {
const { t } = useTranslation();
const [isLayoutSetSelectorVisible, setIsLayoutSetSelectorVisible] = useState<boolean>(false);
const [showCreateSubform, setShowCreateSubform] = useState<boolean>(false);

function handleClick() {
setShowCreateSubform(true);
}

if (isLayoutSetSelectorVisible) {
return (
Expand All @@ -26,23 +36,34 @@ export const EditLayoutSet = ({
/>
);
}

const layoutSetIsUndefined = !existingLayoutSetForSubform;
if (layoutSetIsUndefined) {
return (
<StudioRecommendedNextAction
title={t('ux_editor.component_properties.subform.choose_layout_set_header')}
description={t('ux_editor.component_properties.subform.choose_layout_set_description')}
hideSaveButton={true}
hideSkipButton={true}
>
<SelectLayoutSet
existingLayoutSetForSubform={existingLayoutSetForSubform}
onUpdateLayoutSet={onUpdateLayoutSet}
onSetLayoutSetSelectorVisible={setIsLayoutSetSelectorVisible}
showButtons={false}
/>
</StudioRecommendedNextAction>
<>
<StudioRecommendedNextAction
title={t('ux_editor.component_properties.subform.choose_layout_set_header')}
description={t('ux_editor.component_properties.subform.choose_layout_set_description')}
hideSaveButton={true}
hideSkipButton={true}
>
<StudioParagraph size='sm'>
{t('ux_editor.component_properties.subform.create_layout_set_description')}
</StudioParagraph>
<SelectLayoutSet
existingLayoutSetForSubform={existingLayoutSetForSubform}
onUpdateLayoutSet={onUpdateLayoutSet}
onSetLayoutSetSelectorVisible={setIsLayoutSetSelectorVisible}
showButtons={false}
/>
<StudioProperty.Button
className={classes.button}
property={t('ux_editor.component_properties.subform.create_layout_set_button')}
icon={<PlusIcon />}
onClick={handleClick}
/>
</StudioRecommendedNextAction>
{showCreateSubform && <CreateNewSubformLayoutSet onSubFormCreated={onSubFormCreated} />}
</>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,29 @@ describe('EditLayoutSetForSubform', () => {
expect(setLayoutSetButton).toBeInTheDocument();
});

it('displays a button(Opprett et nytt skjema) to set a layout set for the subform', async () => {
const subformLayoutSetId = 'subformLayoutSetId';
renderEditLayoutSetForSubform({ sets: [{ id: subformLayoutSetId, type: 'subform' }] });
const createNewLayoutSetButton = screen.getByRole('button', {
name: textMock('ux_editor.component_properties.subform.create_layout_set_button'),
});
expect(createNewLayoutSetButton).toBeInTheDocument();
});

it('renders CreateNewLayoutSet component when clicking the create new layout set button', async () => {
const user = userEvent.setup();
const subformLayoutSetId = 'subformLayoutSetId';
renderEditLayoutSetForSubform({ sets: [{ id: subformLayoutSetId, type: 'subform' }] });
const createNewLayoutSetButton = screen.getByRole('button', {
name: textMock('ux_editor.component_properties.subform.create_layout_set_button'),
});
await user.click(createNewLayoutSetButton);
const createNewLayoutSetComponent = screen.getByRole('textbox', {
name: textMock('ux_editor.component_properties.subform.created_layout_set_name'),
});
expect(createNewLayoutSetComponent).toBeInTheDocument();
});

it('displays a select to choose a layout set for the subform', async () => {
const subformLayoutSetId = 'subformLayoutSetId';
renderEditLayoutSetForSubform({ sets: [{ id: subformLayoutSetId, type: 'subform' }] });
Expand Down Expand Up @@ -111,6 +134,26 @@ describe('EditLayoutSetForSubform', () => {
);
});

it('calls handleComponentChange after creating a new layout set and clicking Lukk button', async () => {
const user = userEvent.setup();
const subformLayoutSetId = 'subformLayoutSetId';
renderEditLayoutSetForSubform({ sets: [{ id: subformLayoutSetId, type: 'subform' }] });
const createNewLayoutSetButton = screen.getByRole('button', {
name: textMock('ux_editor.component_properties.subform.create_layout_set_button'),
});
await user.click(createNewLayoutSetButton);
const input = screen.getByRole('textbox');
await user.type(input, 'NewSubForm');
const saveButton = screen.getByRole('button', { name: textMock('general.close') });
await user.click(saveButton);
expect(handleComponentChangeMock).toHaveBeenCalledTimes(1);
expect(handleComponentChangeMock).toHaveBeenCalledWith(
expect.objectContaining({
layoutSet: 'NewSubForm',
}),
);
});

it('closes the view mode when clicking close button after selecting a layout set', async () => {
const user = userEvent.setup();
const subformLayoutSetId = 'subformLayoutSetId';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import { SubformUtilsImpl } from '../../../../classes/SubformUtils';
import { useLayoutSetsQuery } from 'app-shared/hooks/queries/useLayoutSetsQuery';
import { useStudioEnvironmentParams } from 'app-shared/hooks/useStudioEnvironmentParams';
import type { IGenericEditComponent } from '../../../../components/config/componentConfig';
import { useAppContext } from '../../../../hooks';

export const EditLayoutSetForSubform = <T extends ComponentType>({
handleComponentChange,
component,
}: IGenericEditComponent<T>): React.ReactElement => {
const { org, app } = useStudioEnvironmentParams();
const { data: layoutSets } = useLayoutSetsQuery(org, app);
const { setSelectedFormLayoutSetName } = useAppContext();

const subformUtils = new SubformUtilsImpl(layoutSets.sets);

Expand All @@ -25,10 +27,16 @@ export const EditLayoutSetForSubform = <T extends ComponentType>({
handleComponentChange(updatedComponent);
};

function handleCreatedSubForm(layoutSetName: string) {
setSelectedFormLayoutSetName(layoutSetName);
handleUpdatedLayoutSet(layoutSetName);
}

return (
<EditLayoutSet
existingLayoutSetForSubform={component['layoutSet']}
onUpdateLayoutSet={handleUpdatedLayoutSet}
onSubFormCreated={handleCreatedSubForm}
/>
);
};

0 comments on commit 469d9bd

Please sign in to comment.