Skip to content

Commit

Permalink
feat(summary2): add new props to overrides
Browse files Browse the repository at this point in the history
Adds a new property to overrides for RepeatingGroup and Subform
`display`.
This property has a default value dependent on which component is
targeted. RepeatingGroup is `full` and Subform is `table` (see
layout.schema.v1.json)

commit-id:a7e89d34
  • Loading branch information
Jondyr committed Feb 12, 2025
1 parent 75559c9 commit 9d9063b
Show file tree
Hide file tree
Showing 10 changed files with 194 additions and 63 deletions.
3 changes: 3 additions & 0 deletions frontend/language/src/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -1489,6 +1489,9 @@
"ux_editor.component_properties.summary.add_override": "Lag en ny overstyring",
"ux_editor.component_properties.summary.override.choose_component": "Velg komponent",
"ux_editor.component_properties.summary.override.description": "Overstyringer per komponent for oppsummeringen",
"ux_editor.component_properties.summary.override.display": "Visningstype",
"ux_editor.component_properties.summary.override.display.full": "Fullstendig",
"ux_editor.component_properties.summary.override.display.table": "Tabell",
"ux_editor.component_properties.summary.override.display_type": "Visningstype",
"ux_editor.component_properties.summary.override.display_type.list": "Liste",
"ux_editor.component_properties.summary.override.display_type.string": "Tekst",
Expand Down
11 changes: 6 additions & 5 deletions frontend/packages/shared/src/types/ComponentSpecificConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,6 @@ type SummarizableComponentProps = {
renderAsSummary?: BooleanExpression;
};

export type SummaryTargetType = 'page' | 'layoutSet' | 'component';

export type SummaryCustomTargetType = 'list' | 'string';

type LabeledComponentProps = {
labelSettings?: LabelSettings;
};
Expand Down Expand Up @@ -136,14 +132,19 @@ type PageValidation = {
show: AllowedValidationMasks;
};

export type OverrideDisplayType = 'list' | 'string';
export type OverrideDisplay = 'table' | 'full';
export type Summary2OverrideConfig = {
componentId: string;
hidden?: boolean;
emptyFieldText?: string;
isCompact?: boolean;
displayType?: SummaryCustomTargetType;
displayType?: OverrideDisplayType;
display?: OverrideDisplay;
};

export type SummaryTargetType = 'page' | 'layoutSet' | 'component';

export type Summary2TargetConfig = {
type?: SummaryTargetType;
id?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,15 @@ import React, { type ChangeEvent } from 'react';
import type { Summary2OverrideConfig } from 'app-shared/types/ComponentSpecificConfig';
import { StudioSwitch } from '@studio/components';
import { useTranslation } from 'react-i18next';
import { useStudioEnvironmentParams } from 'app-shared/hooks/useStudioEnvironmentParams';
import { useAppContext } from '../../../../../../hooks';
import { useFormLayoutsQuery } from '../../../../../../hooks/queries/useFormLayoutsQuery';
import { getAllLayoutComponents } from '../../../../../../utils/formLayoutUtils';
import { ComponentType } from 'app-shared/types/ComponentType';

type CompactViewSwitchProps = {
onChange: (updatedOverride: Summary2OverrideConfig) => void;
override: Summary2OverrideConfig;
};

export const CompactViewSwitch = ({ onChange, override }: CompactViewSwitchProps) => {
export const Summary2OverrideCompactSwitch = ({ onChange, override }: CompactViewSwitchProps) => {
const { t } = useTranslation();
const { org, app } = useStudioEnvironmentParams();
const { selectedFormLayoutSetName } = useAppContext();
const { data: formLayoutsData } = useFormLayoutsQuery(org, app, selectedFormLayoutSetName);

const components = Object.values(formLayoutsData).flatMap((layout) =>
getAllLayoutComponents(layout),
);
const component = components.find((comp) => comp.id === override.componentId);
const isGroupComponent = component?.type === (ComponentType.Group as ComponentType);

if (!isGroupComponent) {
return null;
}
return (
<StudioSwitch
position='right'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import type {
OverrideDisplay,
Summary2OverrideConfig,
} from 'app-shared/types/ComponentSpecificConfig';
import { StudioNativeSelect } from '@studio/components';
import { useTranslation } from 'react-i18next';

type Summary2OverrideDisplaySelectProps = {
onChange: (override: Summary2OverrideConfig) => void;
override: Summary2OverrideConfig;
};

export const Summary2OverrideDisplaySelect = ({
onChange,
override,
}: Summary2OverrideDisplaySelectProps) => {
const { t } = useTranslation();

const options = [
{ value: 'table', text: t('ux_editor.component_properties.summary.override.display.table') },
{ value: 'full', text: t('ux_editor.component_properties.summary.override.display.full') },
];

return (
<StudioNativeSelect
size='sm'
label={t('ux_editor.component_properties.summary.override.display')}
value={override.display}
onChange={(event) => {
onChange({ ...override, display: event.target.value as OverrideDisplay });
}}
>
{options.map((option) => (
<option key={option.value} value={option.value}>
{option.text}
</option>
))}
</StudioNativeSelect>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,26 @@ import React from 'react';
import { StudioCard, StudioNativeSelect } from '@studio/components';
import type {
Summary2OverrideConfig,
SummaryCustomTargetType,
OverrideDisplayType,
} from 'app-shared/types/ComponentSpecificConfig';
import { type CustomConfigType, useCustomConfigType } from '../hook/useCustomConfigType';
import { useTranslation } from 'react-i18next';
import { mapSelectedTypeToConfig } from '../utils';
import { ComponentType } from 'app-shared/types/ComponentType';
import type { TargetComponentProps } from '../../Summary2Target/targetUtils';

export type Summary2OverrideDisplayTypeProps = {
override: Summary2OverrideConfig;
componentOptions: TargetComponentProps[];
onChange: (override: Summary2OverrideConfig) => void;
};

export const Summary2OverrideDisplayType = ({
override,
componentOptions,
onChange,
}: Summary2OverrideDisplayTypeProps) => {
const { t } = useTranslation();
const { displayType, componentId } = override;
const selectedComponentType = componentOptions?.find((comp) => comp.id === componentId)?.type;
const customConfigTypes: CustomConfigType[] = useCustomConfigType();

const checkboxOrMultipleselect =
selectedComponentType?.includes(ComponentType.MultipleSelect) ||
selectedComponentType?.includes(ComponentType.Checkboxes);

if (!checkboxOrMultipleselect) {
return null;
}

const handleCustomTypeChange = (newDisplayType: SummaryCustomTargetType): void => {
const handleCustomTypeChange = (newDisplayType: OverrideDisplayType): void => {
const summary2OverrideConfig = mapSelectedTypeToConfig({
componentId,
displayType: newDisplayType,
Expand All @@ -48,7 +35,7 @@ export const Summary2OverrideDisplayType = ({
size='sm'
label={t('ux_editor.component_properties.summary.override.display_type')}
value={displayType || 'list'}
onChange={(e) => handleCustomTypeChange(e.target.value as SummaryCustomTargetType)}
onChange={(e) => handleCustomTypeChange(e.target.value as OverrideDisplayType)}
>
{customConfigTypes.map((type: CustomConfigType) => (
<option key={type.label} value={type.value}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,22 @@ import {
componentWithMultipleSelectMock,
layout1NameMock,
layoutMock,
container2IdMock,
subformComponentMock,
} from '../../../../../testing/layoutMock';
import { textMock } from '@studio/testing/mocks/i18nMock';
import { layoutSet1NameMock, layoutSetsExtendedMock } from '../../../../../testing/layoutSetsMock';
import { renderWithProviders } from '../../../../../testing/mocks';

const checkBoxId = componentWithOptionsMock.id;
const multipleSelectId = componentWithMultipleSelectMock.id;
const subformComponentId = subformComponentMock.id;
const layoutMockWithMultipleSelect = {
...layoutMock,
components: {
...layoutMock.components,
[multipleSelectId]: componentWithMultipleSelectMock,
[subformComponentId]: subformComponentMock,
},
};

Expand All @@ -42,11 +46,12 @@ describe('Summary2Override', () => {
});

it('should be able to remove override', async () => {
const user = userEvent.setup();
render({
overrides: [{ componentId: '1' }],
});
await userEvent.click(overrideCollapsedButton(1));
await userEvent.click(removeOverrideButton());
await user.click(overrideCollapsedButton(1));
await user.click(removeOverrideButton());
expect(defaultProps.onChange).toHaveBeenCalledWith([]);
});

Expand All @@ -57,7 +62,7 @@ describe('Summary2Override', () => {
render({ overrides: [{ componentId }] });

await user.click(overrideCollapsedButton(1));
await user.click(overrideTypeSelector());
await user.click(overrideDisplayTypeSelector());
expect(renderedTypeOptions()).toBeTruthy();
},
);
Expand Down Expand Up @@ -143,7 +148,7 @@ describe('Summary2Override', () => {
const user = userEvent.setup();
render({ overrides: [{ componentId: multipleSelectId }] });
await user.click(overrideCollapsedButton(1));
await user.selectOptions(overrideTypeSelector(), overrideSelectType('string'));
await user.selectOptions(overrideDisplayTypeSelector(), overrideDisplayType('string'));

expect(defaultProps.onChange).toHaveBeenCalledWith(
expect.arrayContaining([{ componentId: multipleSelectId, displayType: 'string' }]),
Expand All @@ -154,7 +159,7 @@ describe('Summary2Override', () => {
const user = userEvent.setup();
render({ overrides: [{ componentId: checkBoxId }] });
await user.click(overrideCollapsedButton(1));
await user.selectOptions(overrideTypeSelector(), overrideSelectType('string'));
await user.selectOptions(overrideDisplayTypeSelector(), overrideDisplayType('string'));

await waitFor(() =>
expect(defaultProps.onChange).toHaveBeenCalledWith(
Expand All @@ -164,20 +169,61 @@ describe('Summary2Override', () => {
});

it('should collapse and uncollapse override', async () => {
const user = userEvent.setup();
render({ overrides: [{ componentId: '1' }] });
await userEvent.click(overrideCollapsedButton(1));
await user.click(overrideCollapsedButton(1));
expect(
screen.queryByRole('button', {
name: textMock('ux_editor.component_properties.summary.overrides.nth.*:1}'),
}),
).not.toBeInTheDocument();
await userEvent.click(overrideCloseButton());
await user.click(overrideCloseButton());
expect(
screen.queryByRole('button', {
name: textMock('ux_editor.component_properties.summary.overrides.nth.*:1}'),
}),
).not.toBeInTheDocument();
});

it('should render component specific overrides', async () => {
const user = userEvent.setup();
render({ overrides: [{ componentId: container2IdMock }] });

await user.click(overrideCollapsedButton(1));
await user.selectOptions(overrideDisplaySelector(), overrideDisplaySelectType('table'));

expect(defaultProps.onChange).toHaveBeenCalledWith(
expect.arrayContaining([{ componentId: container2IdMock, display: 'table' }]),
);
});

it.each([
{
componentId: container2IdMock,
defaultProps: { componentId: container2IdMock, display: 'table' },
},
{
componentId: subformComponentId,
defaultProps: { componentId: subformComponentId, display: 'full' },
},
])('should set default props for components', async (args) => {
const user = userEvent.setup();
render({ overrides: [{ componentId: component1IdMock }] });

await user.click(overrideCollapsedButton(1));
await user.click(overrideComponentSelect());
await user.click(
screen.getByRole('option', {
name: new RegExp(args.componentId),
}),
);

await waitFor(() =>
expect(defaultProps.onChange).toHaveBeenCalledWith(
expect.arrayContaining([expect.objectContaining(args.defaultProps)]),
),
);
});
});

const overrideCloseButton = () =>
Expand All @@ -202,12 +248,22 @@ const overrideComponentSelect = () =>
name: textMock('ux_editor.component_properties.summary.override.choose_component'),
});

const overrideTypeSelector = () =>
const overrideDisplaySelector = () =>
screen.getByRole('combobox', {
name: textMock('ux_editor.component_properties.summary.override.display'),
});

const overrideDisplaySelectType = (type: string) =>
screen.getByRole('option', {
name: textMock(`ux_editor.component_properties.summary.override.display.${type}`),
});

const overrideDisplayTypeSelector = () =>
screen.getByRole('combobox', {
name: textMock('ux_editor.component_properties.summary.override.display_type'),
});

const overrideSelectType = (type: string) =>
const overrideDisplayType = (type: string) =>
screen.getByRole('option', {
name: textMock(`ux_editor.component_properties.summary.override.display_type.${type}`),
});
Expand Down
Loading

0 comments on commit 9d9063b

Please sign in to comment.