-
Notifications
You must be signed in to change notification settings - Fork 75
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: update summary2 override configuration design (#14379)
- Loading branch information
Showing
18 changed files
with
573 additions
and
142 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
...ackages/ux-editor/src/components/Properties/PropertiesHeader/ComponentMainConfig.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import React from 'react'; | ||
import { screen } from '@testing-library/react'; | ||
import { renderWithProviders } from '../../../testing/mocks'; | ||
import { ComponentMainConfig } from './ComponentMainConfig'; | ||
import type { FormItem } from '../../../types/FormItem'; | ||
import { ComponentType } from 'app-shared/types/ComponentType'; | ||
import userEvent from '@testing-library/user-event'; | ||
import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; | ||
import { QueryKey } from 'app-shared/types/QueryKey'; | ||
import { app, org } from '@studio/testing/testids'; | ||
import { layoutSet1NameMock } from '../../../testing/layoutSetsMock'; | ||
import { layout1NameMock, layoutMock } from '../../../testing/layoutMock'; | ||
|
||
const summary2Component: FormItem = { | ||
id: '0', | ||
type: ComponentType.Summary2, | ||
itemType: 'COMPONENT', | ||
target: {}, | ||
}; | ||
|
||
describe('ComponentMainConfig', () => { | ||
describe('Summary2', () => { | ||
it('should render summary2 config', async () => { | ||
const user = userEvent.setup(); | ||
render(summary2Component); | ||
expect(summary2AccordionButton()).toBeInTheDocument(); | ||
await user.click(summary2AccordionButton()); | ||
expect(summary2AddOverrideButton()).toBeInTheDocument(); | ||
}); | ||
|
||
it('should display overrides', async () => { | ||
const user = userEvent.setup(); | ||
const summary2ComponentWithOverrides = { | ||
...summary2Component, | ||
overrides: [{ componentId: '0' }], | ||
}; | ||
render(summary2ComponentWithOverrides); | ||
await user.click(summary2AccordionButton()); | ||
expect(summary2CollapsedButton(1)).toBeInTheDocument(); | ||
}); | ||
|
||
it('should call handleComponentChange when adding overrides', async () => { | ||
const user = userEvent.setup(); | ||
render(summary2Component); | ||
await user.click(summary2AccordionButton()); | ||
await user.click(summary2AddOverrideButton()); | ||
expect(handleComponentChange).toHaveBeenCalledTimes(1); | ||
}); | ||
}); | ||
}); | ||
|
||
const summary2AccordionButton = () => | ||
screen.getByRole('button', { name: /ux_editor.component_properties.summary.override.title/ }); | ||
const summary2AddOverrideButton = () => | ||
screen.getByRole('button', { name: /ux_editor.component_properties.summary.add_override/ }); | ||
const summary2CollapsedButton = (n: number) => | ||
screen.getByRole('button', { | ||
name: new RegExp(`ux_editor.component_properties.summary.overrides.nth.*:${n}}`), | ||
}); | ||
|
||
const handleComponentChange = jest.fn(); | ||
const render = (component: FormItem) => { | ||
const queryClient = createQueryClientMock(); | ||
queryClient.setQueryData([QueryKey.FormLayouts, org, app, layoutSet1NameMock], { | ||
[layout1NameMock]: layoutMock, | ||
}); | ||
renderWithProviders( | ||
<ComponentMainConfig component={component} handleComponentChange={handleComponentChange} />, | ||
{ | ||
queryClient, | ||
appContextProps: { | ||
selectedFormLayoutSetName: layoutSet1NameMock, | ||
selectedFormLayoutName: layout1NameMock, | ||
}, | ||
}, | ||
); | ||
}; |
49 changes: 49 additions & 0 deletions
49
...end/packages/ux-editor/src/components/Properties/PropertiesHeader/ComponentMainConfig.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import React from 'react'; | ||
import type { FormItem } from '../../../types/FormItem'; | ||
import { ComponentType } from 'app-shared/types/ComponentType'; | ||
import { Accordion } from '@digdir/designsystemet-react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { Summary2Override } from '../../config/componentSpecificContent/Summary2/Override/Summary2Override'; | ||
import type { Summary2OverrideConfig } from 'app-shared/types/ComponentSpecificConfig'; | ||
|
||
export type ComponentMainConfigProps = { | ||
component: FormItem; | ||
handleComponentChange: (component: FormItem) => void; | ||
}; | ||
|
||
export const ComponentMainConfig = ({ | ||
component, | ||
handleComponentChange, | ||
}: ComponentMainConfigProps) => { | ||
const [accordionOpen, setAccordionOpen] = React.useState<Record<string, boolean>>({}); | ||
const { t } = useTranslation(); | ||
|
||
const handleOverridesChange = (updatedOverrides: Summary2OverrideConfig[]): void => { | ||
const updatedComponent = { ...component } as FormItem<ComponentType.Summary2>; | ||
updatedComponent.overrides = updatedOverrides; | ||
handleComponentChange(updatedComponent); | ||
}; | ||
|
||
return ( | ||
<> | ||
{component.type === ComponentType.Summary2 && ( | ||
<Accordion color='subtle'> | ||
<Accordion.Item open={accordionOpen['summary2overrides'] === true}> | ||
<Accordion.Header | ||
onHeaderClick={() => | ||
setAccordionOpen((prev) => { | ||
return { ...prev, summary2overrides: !prev['summary2overrides'] }; | ||
}) | ||
} | ||
> | ||
{t('ux_editor.component_properties.summary.override.title')} | ||
</Accordion.Header> | ||
<Accordion.Content> | ||
<Summary2Override overrides={component.overrides} onChange={handleOverridesChange} /> | ||
</Accordion.Content> | ||
</Accordion.Item> | ||
</Accordion> | ||
)} | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
...ts/config/componentSpecificContent/Summary2/Override/OverrideFields/CompactViewSwitch.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
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) => { | ||
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' | ||
size='sm' | ||
onChange={(event: ChangeEvent<HTMLInputElement>) => | ||
onChange({ ...override, isCompact: event.target.checked }) | ||
} | ||
checked={override.isCompact ?? false} | ||
value='isCompact' | ||
> | ||
{t('ux_editor.component_properties.summary.override.is_compact')} | ||
</StudioSwitch> | ||
); | ||
}; |
3 changes: 3 additions & 0 deletions
3
...onfig/componentSpecificContent/Summary2/Override/OverrideFields/EmptyTextField.module.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.propertyButton { | ||
padding-left: var(--fds-spacing-0); | ||
} |
68 changes: 68 additions & 0 deletions
68
.../config/componentSpecificContent/Summary2/Override/OverrideFields/EmptyTextField.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import React from 'react'; | ||
import { renderWithProviders } from '../../../../../../testing/mocks'; | ||
import { EmptyTextField } from './EmptyTextField'; | ||
import { component1IdMock } from '../../../../../../testing/layoutMock'; | ||
import { userEvent } from '@testing-library/user-event'; | ||
import { screen } from '@testing-library/react'; | ||
import type { Summary2OverrideConfig } from 'app-shared/types/ComponentSpecificConfig'; | ||
|
||
describe('EmptyTextField', () => { | ||
it('Should display textfield when openbutton is clicked', async () => { | ||
const user = userEvent.setup(); | ||
render(); | ||
await user.click(emptyTextFieldButton()); | ||
expect(emptyTextFieldTextBox()).toBeInTheDocument(); | ||
}); | ||
|
||
it('should call onChange prop when textbox is edited', async () => { | ||
const user = userEvent.setup(); | ||
render(); | ||
await user.click(emptyTextFieldButton()); | ||
const inputtext = 'test string 123@'; | ||
await user.type(emptyTextFieldTextBox(), inputtext); | ||
expect(onChangeMock).toHaveBeenCalledWith( | ||
expect.objectContaining({ emptyFieldText: inputtext }), | ||
); | ||
}); | ||
|
||
it('should close editor on input blur', async () => { | ||
const user = userEvent.setup(); | ||
render(); | ||
await user.click(emptyTextFieldButton()); | ||
await user.type(emptyTextFieldTextBox(), 'inputtext'); | ||
expect(emptyTextFieldTextBox()).toBeInTheDocument(); | ||
await user.click(document.body); | ||
expect(emptyTextFieldTextBox()).not.toBeInTheDocument(); | ||
}); | ||
|
||
it('should close editor when pressing enter', async () => { | ||
const user = userEvent.setup(); | ||
render(); | ||
await user.click(emptyTextFieldButton()); | ||
await user.type(emptyTextFieldTextBox(), 'inputtext'); | ||
expect(emptyTextFieldTextBox()).toBeInTheDocument(); | ||
await user.type(emptyTextFieldTextBox(), '{enter}'); | ||
expect(emptyTextFieldTextBox()).not.toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
const emptyTextFieldButton = () => | ||
screen.getByRole('button', { | ||
name: /ux_editor.component_properties.summary.override.empty_field_text/i, | ||
}); | ||
const emptyTextFieldTextBox = () => | ||
screen.queryByRole('textbox', { | ||
name: /ux_editor.component_properties.summary.override.empty_field_text/i, | ||
}); | ||
|
||
const onChangeMock = jest.fn(); | ||
|
||
const render = () => { | ||
const override: Summary2OverrideConfig = { | ||
componentId: component1IdMock, | ||
hideEmptyFields: false, | ||
forceShow: true, | ||
hidden: false, | ||
}; | ||
return renderWithProviders(<EmptyTextField onChange={onChangeMock} override={override} />); | ||
}; |
47 changes: 47 additions & 0 deletions
47
...nents/config/componentSpecificContent/Summary2/Override/OverrideFields/EmptyTextField.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import React, { type ChangeEvent } from 'react'; | ||
import classes from './EmptyTextField.module.css'; | ||
import type { Summary2OverrideConfig } from 'app-shared/types/ComponentSpecificConfig'; | ||
import { StudioAlert, StudioProperty, StudioTextfield } from '@studio/components'; | ||
import { useTranslation } from 'react-i18next'; | ||
|
||
type EmptyTextFieldProps = { | ||
onChange: (updatedOverride: Summary2OverrideConfig) => void; | ||
override: Summary2OverrideConfig; | ||
}; | ||
|
||
export const EmptyTextField = ({ onChange, override }: EmptyTextFieldProps) => { | ||
const { t } = useTranslation(); | ||
const [open, setOpen] = React.useState(false); | ||
|
||
if (override.hideEmptyFields || !override.forceShow) { | ||
return ( | ||
<StudioAlert> | ||
{t('ux_editor.component_properties.summary.override.hide_empty_fields.info_message')} | ||
</StudioAlert> | ||
); | ||
} | ||
|
||
if (!open) { | ||
return ( | ||
<StudioProperty.Button | ||
className={classes.propertyButton} | ||
value={override.emptyFieldText} | ||
property={t('ux_editor.component_properties.summary.override.empty_field_text')} | ||
onClick={() => setOpen(true)} | ||
></StudioProperty.Button> | ||
); | ||
} | ||
|
||
return ( | ||
<StudioTextfield | ||
label={t('ux_editor.component_properties.summary.override.empty_field_text')} | ||
autoFocus={true} | ||
onBlur={() => setOpen(false)} | ||
onKeyDown={({ key }) => key === 'Enter' && setOpen(false)} | ||
value={override.emptyFieldText} | ||
onChange={(event: ChangeEvent<HTMLInputElement>) => | ||
onChange({ ...override, emptyFieldText: event.target.value }) | ||
} | ||
/> | ||
); | ||
}; |
26 changes: 26 additions & 0 deletions
26
...ents/config/componentSpecificContent/Summary2/Override/OverrideFields/ForceShowSwitch.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import React from 'react'; | ||
import type { Summary2OverrideConfig } from 'app-shared/types/ComponentSpecificConfig'; | ||
import { StudioSwitch } from '@studio/components'; | ||
import { useTranslation } from 'react-i18next'; | ||
|
||
type ForceShowSwitchProps = { | ||
onChange: (updatedOverride: Summary2OverrideConfig) => void; | ||
override: Summary2OverrideConfig; | ||
}; | ||
|
||
export const OverrideShowComponentSwitch = ({ onChange, override }: ForceShowSwitchProps) => { | ||
const { t } = useTranslation(); | ||
return ( | ||
<StudioSwitch | ||
position='right' | ||
size='sm' | ||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => | ||
onChange({ ...override, hidden: !event.target.checked }) | ||
} | ||
checked={!override.hidden} | ||
value={'hidden'} | ||
> | ||
{t('ux_editor.component_properties.summary.override.show_component')} | ||
</StudioSwitch> | ||
); | ||
}; |
30 changes: 30 additions & 0 deletions
30
...onfig/componentSpecificContent/Summary2/Override/OverrideFields/ShowEmptyFieldsSwitch.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import React from 'react'; | ||
import type { Summary2OverrideConfig } from 'app-shared/types/ComponentSpecificConfig'; | ||
import { StudioSwitch } from '@studio/components'; | ||
import { useTranslation } from 'react-i18next'; | ||
|
||
type ShowEmptyFieldSwitchProps = { | ||
onChange: (override: Summary2OverrideConfig) => void; | ||
override: Summary2OverrideConfig; | ||
}; | ||
|
||
export const ShowEmptyFieldSwitch = ({ onChange, override }: ShowEmptyFieldSwitchProps) => { | ||
const { t } = useTranslation(); | ||
return ( | ||
<StudioSwitch | ||
position='right' | ||
size='sm' | ||
onChange={async (event: React.ChangeEvent<HTMLInputElement>) => { | ||
const updatedOverride = { | ||
...override, | ||
hideEmptyFields: !event.target.checked, | ||
forceShow: event.target.checked, | ||
}; | ||
onChange(updatedOverride); | ||
}} | ||
checked={override.forceShow} | ||
> | ||
{t('ux_editor.component_properties.summary.override.hide_empty_fields')} | ||
</StudioSwitch> | ||
); | ||
}; |
Oops, something went wrong.