From d7400aeab075911c67f6b36bcd8724cf26499bff Mon Sep 17 00:00:00 2001 From: Jamal Alabdullah Date: Fri, 14 Feb 2025 15:28:42 +0100 Subject: [PATCH 01/13] added new reusable component SelectPropertyEditor to dispay the selected value --- frontend/language/src/nb.json | 1 + .../SelectPropertyEditor.tsx | 45 ++++++++++++++ .../components/config/FormComponentConfig.tsx | 60 +++++++++++++++---- 3 files changed, 96 insertions(+), 10 deletions(-) create mode 100644 frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/SelectPropertyEditor.tsx diff --git a/frontend/language/src/nb.json b/frontend/language/src/nb.json index 4a766eca517..9840c6bbbd2 100644 --- a/frontend/language/src/nb.json +++ b/frontend/language/src/nb.json @@ -1445,6 +1445,7 @@ "ux_editor.component_properties.select_all_attachments": "Alle vedlegg", "ux_editor.component_properties.select_attachments": "Velg vedlegg", "ux_editor.component_properties.select_pdf": "Inkluder skjemagenerert pdf", + "ux_editor.component_properties.selected_validations": "Valideringstyper", "ux_editor.component_properties.sender": "Den som har sendt inn skjemaet", "ux_editor.component_properties.severity": "Alvorlighetsgrad", "ux_editor.component_properties.showAddButton": "Vis Legg til-knapp", diff --git a/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/SelectPropertyEditor.tsx b/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/SelectPropertyEditor.tsx new file mode 100644 index 00000000000..4b3be732322 --- /dev/null +++ b/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/SelectPropertyEditor.tsx @@ -0,0 +1,45 @@ +import React, { useState } from 'react'; +import { XMarkIcon } from '@studio/icons'; +import { StudioButton, StudioProperty } from '@studio/components'; +import classes from './CollapsiblePropertyEditor.module.css'; +import { useTranslation } from 'react-i18next'; + +export type SelectPropertyEditorProps = { + children?: React.ReactNode; + value?: string | React.ReactNode; + property?: string; + title?: string; +}; + +export const SelectPropertyEditor = ({ + children, + value, + property, + title, +}: SelectPropertyEditorProps) => { + const { t } = useTranslation(); + const [dataTypeSelectVisible, setDataTypeSelectVisible] = useState(false); + + return dataTypeSelectVisible ? ( +
+
+ {children} + + } + onClick={() => setDataTypeSelectVisible(false)} + title={t('general.close')} + variant='secondary' + disabled={undefined} + /> +
+
+ ) : ( + setDataTypeSelectVisible(true)} + property={property} + title={title} + value={value} + /> + ); +}; diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx index 872cb79ff9b..55a12c275ef 100644 --- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx @@ -18,8 +18,12 @@ import classes from './FormComponentConfig.module.css'; import { RedirectToLayoutSet } from './editModal/RedirectToLayoutSet'; import { ChevronDownIcon, ChevronUpIcon, PlusCircleIcon, XMarkIcon } from '@studio/icons'; import { StudioButton, StudioCard, StudioProperty } from '@studio/components'; -import { CollapsiblePropertyEditor } from './CollapsiblePropertyEditor'; +import { useComponentPropertyEnumValue } from '@altinn/ux-editor/hooks/useComponentPropertyEnumValue'; +import { SelectPropertyEditor } from './CollapsiblePropertyEditor/SelectPropertyEditor'; +//TODO: 1- Fix css for the SelectPropertyEditor component +//TODO: 2- Add test cases for the SelectPropertyEditor component. +//TODO: 3- Remove CollapsiblePropertyEditor component. And change the name of the folder to SelectPropertyEditor.(or new place for the new folder) export interface IEditFormComponentProps { editFormId: string; component: FormItem; @@ -44,6 +48,9 @@ export const FormComponentConfig = ({ const [showOtherComponents, setShowOtherComponents] = useState(false); const [showGrid, setShowGrid] = useState(false); + const selectedDataType = useComponentPropertyEnumValue(); + const [selectedValue, setSelectedValue] = useState([]); + if (!schema?.properties) return null; const { properties } = schema; @@ -221,26 +228,41 @@ export const FormComponentConfig = ({ {/** String properties */} {stringPropertyKeys.map((propertyKey) => { + const selectedStringPropertiesDisplay = () => { + const value = component[propertyKey]; + if (Array.isArray(value)) return value.map((dataType) => selectedDataType(dataType)); + if (value) return selectedDataType(value); + return undefined; + }; + return ( - + - + ); })} {/** Number properties (number and integer types) */} {numberPropertyKeys.map((propertyKey) => { return ( - - + ); })} {/** Array properties with enum values) */} {arrayPropertyKeys.map((propertyKey) => { + const selectedValuesDisplay = + component[propertyKey] && component[propertyKey].length > 0 + ? component[propertyKey].map((dataType) => ( +
{selectedDataType(dataType)}
+ )) + : undefined; + const selectProperty = + selectedValue.length > 0 + ? t('ux_editor.component_properties.selected_validations') + : componentPropertyLabel(propertyKey); return ( - + { + setSelectedValue(updatedComponent[propertyKey]); + handleComponentUpdate(updatedComponent); + }} propertyKey={propertyKey} key={propertyKey} enumValues={properties[propertyKey]?.items?.enum} multiple={true} /> - + ); })} - {/** Object properties */} + {/** Object properties */} {objectPropertyKeys.map((propertyKey) => { return ( From 686d2cdf3cbbfc04ccda84b989ce33d6cbe68a83 Mon Sep 17 00:00:00 2001 From: Jamal Alabdullah Date: Sun, 16 Feb 2025 19:10:20 +0100 Subject: [PATCH 02/13] Add css for component SelectPropertyEditor --- .../SelectPropertyEditor.tsx | 45 ------------------ .../config/FormComponentConfig.module.css | 1 + .../components/config/FormComponentConfig.tsx | 5 +- .../SelectPropertyEditor.module.css | 27 +++++++++++ .../SelectPropertyEditor.tsx | 47 +++++++++++++++++++ 5 files changed, 77 insertions(+), 48 deletions(-) delete mode 100644 frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/SelectPropertyEditor.tsx create mode 100644 frontend/packages/ux-editor/src/components/config/SelectPropertyEditor/SelectPropertyEditor.module.css create mode 100644 frontend/packages/ux-editor/src/components/config/SelectPropertyEditor/SelectPropertyEditor.tsx diff --git a/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/SelectPropertyEditor.tsx b/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/SelectPropertyEditor.tsx deleted file mode 100644 index 4b3be732322..00000000000 --- a/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/SelectPropertyEditor.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React, { useState } from 'react'; -import { XMarkIcon } from '@studio/icons'; -import { StudioButton, StudioProperty } from '@studio/components'; -import classes from './CollapsiblePropertyEditor.module.css'; -import { useTranslation } from 'react-i18next'; - -export type SelectPropertyEditorProps = { - children?: React.ReactNode; - value?: string | React.ReactNode; - property?: string; - title?: string; -}; - -export const SelectPropertyEditor = ({ - children, - value, - property, - title, -}: SelectPropertyEditorProps) => { - const { t } = useTranslation(); - const [dataTypeSelectVisible, setDataTypeSelectVisible] = useState(false); - - return dataTypeSelectVisible ? ( -
-
- {children} - - } - onClick={() => setDataTypeSelectVisible(false)} - title={t('general.close')} - variant='secondary' - disabled={undefined} - /> -
-
- ) : ( - setDataTypeSelectVisible(true)} - property={property} - title={title} - value={value} - /> - ); -}; diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.module.css b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.module.css index 6f4fe987c4c..1a8653696b4 100644 --- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.module.css +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.module.css @@ -14,6 +14,7 @@ .gridButton { padding: 0; + margin-bottom: var(--fds-spacing-0); } .gridHeader { diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx index 55a12c275ef..eb79c049fbd 100644 --- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx @@ -19,11 +19,10 @@ import { RedirectToLayoutSet } from './editModal/RedirectToLayoutSet'; import { ChevronDownIcon, ChevronUpIcon, PlusCircleIcon, XMarkIcon } from '@studio/icons'; import { StudioButton, StudioCard, StudioProperty } from '@studio/components'; import { useComponentPropertyEnumValue } from '@altinn/ux-editor/hooks/useComponentPropertyEnumValue'; -import { SelectPropertyEditor } from './CollapsiblePropertyEditor/SelectPropertyEditor'; +import { SelectPropertyEditor } from './SelectPropertyEditor/SelectPropertyEditor'; -//TODO: 1- Fix css for the SelectPropertyEditor component //TODO: 2- Add test cases for the SelectPropertyEditor component. -//TODO: 3- Remove CollapsiblePropertyEditor component. And change the name of the folder to SelectPropertyEditor.(or new place for the new folder) +//TODO: 3- Remove CollapsiblePropertyEditor folder. export interface IEditFormComponentProps { editFormId: string; component: FormItem; diff --git a/frontend/packages/ux-editor/src/components/config/SelectPropertyEditor/SelectPropertyEditor.module.css b/frontend/packages/ux-editor/src/components/config/SelectPropertyEditor/SelectPropertyEditor.module.css new file mode 100644 index 00000000000..8998a8c7639 --- /dev/null +++ b/frontend/packages/ux-editor/src/components/config/SelectPropertyEditor/SelectPropertyEditor.module.css @@ -0,0 +1,27 @@ +.container { + display: grid; + gap: var(--fds-spacing-2); + padding-bottom: var(--fds-spacing-1); + align-items: center; +} + +.viewMode { + margin-top: var(--fds-spacing-1); + margin-bottom: var(--fds-spacing-1); +} + +.editSelectProperty { + display: flex; + flex-direction: row; + align-items: end; + gap: var(--fds-spacing-4); + margin-bottom: var(--fds-spacing-3); +} + +.selectProperty { + flex: 1; +} + +.viewSelectProperty { + padding-left: 0; +} diff --git a/frontend/packages/ux-editor/src/components/config/SelectPropertyEditor/SelectPropertyEditor.tsx b/frontend/packages/ux-editor/src/components/config/SelectPropertyEditor/SelectPropertyEditor.tsx new file mode 100644 index 00000000000..001ed0309e4 --- /dev/null +++ b/frontend/packages/ux-editor/src/components/config/SelectPropertyEditor/SelectPropertyEditor.tsx @@ -0,0 +1,47 @@ +import React, { useState } from 'react'; +import { XMarkIcon } from '@studio/icons'; +import { StudioButton, StudioProperty } from '@studio/components'; +import classes from './SelectPropertyEditor.module.css'; +import { useTranslation } from 'react-i18next'; +import cn from 'classnames'; + +export type SelectPropertyEditorProps = { + children?: React.ReactNode; + value?: string | React.ReactNode; + property?: string; + title?: string; +}; + +export const SelectPropertyEditor = ({ + children, + value, + property, + title, +}: SelectPropertyEditorProps) => { + const { t } = useTranslation(); + const [dataTypeSelectVisible, setDataTypeSelectVisible] = useState(false); + + return ( +
+ {dataTypeSelectVisible ? ( +
+
{children}
+ } + onClick={() => setDataTypeSelectVisible(false)} + title={t('general.close')} + variant='secondary' + /> +
+ ) : ( + setDataTypeSelectVisible(true)} + property={property} + title={title} + value={value} + className={classes.viewSelectProperty} + /> + )} +
+ ); +}; From 11ef406fe9a97d00bdbf5a122c762ac166bf64f0 Mon Sep 17 00:00:00 2001 From: Jamal Alabdullah Date: Mon, 17 Feb 2025 00:07:35 +0100 Subject: [PATCH 03/13] added test for SelectPropertyEditor component --- .../SelectPropertyEditor.test.tsx | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 frontend/packages/ux-editor/src/components/config/SelectPropertyEditor/SelectPropertyEditor.test.tsx diff --git a/frontend/packages/ux-editor/src/components/config/SelectPropertyEditor/SelectPropertyEditor.test.tsx b/frontend/packages/ux-editor/src/components/config/SelectPropertyEditor/SelectPropertyEditor.test.tsx new file mode 100644 index 00000000000..43890b3bba9 --- /dev/null +++ b/frontend/packages/ux-editor/src/components/config/SelectPropertyEditor/SelectPropertyEditor.test.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import userEvent from '@testing-library/user-event'; +import { screen } from '@testing-library/react'; +import { renderWithProviders } from 'app-development/test/mocks'; +import { textMock } from '@studio/testing/mocks/i18nMock'; +import { SelectPropertyEditor, type SelectPropertyEditorProps } from './SelectPropertyEditor'; + +const children =
Test children
; +const value =
Test value
; +const property = 'Test property'; + +describe('SelectPropertyEditor', () => { + it('should render the children when button is clicked', async () => { + const user = userEvent.setup(); + renderSelectPropertyEditor(); + await user.click(screen.getByText('Test property')); + expect(screen.getByText('Test children')).toBeInTheDocument(); + }); + + it('should hide the children when the close button is clicked', async () => { + const user = userEvent.setup(); + renderSelectPropertyEditor(); + await user.click(screen.getByText('Test property')); + expect(screen.getByText('Test children')).toBeInTheDocument(); + await user.click(screen.getByRole('button', { name: textMock('general.close') })); + expect(screen.queryByText('Test children')).not.toBeInTheDocument(); + }); + + it('should render value', () => { + renderSelectPropertyEditor(); + expect(screen.getByText('Test value')).toBeInTheDocument(); + }); + + it('should render the property', () => { + renderSelectPropertyEditor({ property: 'Test property' }); + expect(screen.getByText('Test property')).toBeInTheDocument(); + }); +}); + +const defaultProps: SelectPropertyEditorProps = { + children, + value, + property, +}; + +const renderSelectPropertyEditor = (props: Partial = {}) => { + renderWithProviders()(); +}; From 51922aca245d924d2ba7154fcbceeee65645077b Mon Sep 17 00:00:00 2001 From: Jamal Alabdullah Date: Mon, 17 Feb 2025 09:56:30 +0100 Subject: [PATCH 04/13] remove CollapsiblePropertyEditor folder and update test --- .../CollapsiblePropertyEditor.module.css | 23 -------- .../CollapsiblePropertyEditor.test.tsx | 57 ------------------- .../CollapsiblePropertyEditor.tsx | 50 ---------------- .../config/CollapsiblePropertyEditor/index.ts | 1 - .../config/FormComponentConfig.test.tsx | 6 +- .../components/config/FormComponentConfig.tsx | 2 - .../config/SelectPropertyEditor/index.ts | 1 + 7 files changed, 4 insertions(+), 136 deletions(-) delete mode 100644 frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/CollapsiblePropertyEditor.module.css delete mode 100644 frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/CollapsiblePropertyEditor.test.tsx delete mode 100644 frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/CollapsiblePropertyEditor.tsx delete mode 100644 frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/index.ts create mode 100644 frontend/packages/ux-editor/src/components/config/SelectPropertyEditor/index.ts diff --git a/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/CollapsiblePropertyEditor.module.css b/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/CollapsiblePropertyEditor.module.css deleted file mode 100644 index ed1fcde3172..00000000000 --- a/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/CollapsiblePropertyEditor.module.css +++ /dev/null @@ -1,23 +0,0 @@ -.collapsibleContainer { - display: flex; - flex-direction: row; - align-items: flex-end; - gap: var(--fds-spacing-4); - padding-bottom: var(--fds-spacing-4); - padding-top: var(--fds-spacing-1); -} - -.collapsibleContainerClosed { - margin-top: var(--fds-spacing-1); - margin-bottom: var(--fds-spacing-1); -} - -.editorContent { - flex: 1; -} - -.button { - display: flex; - gap: var(--fds-spacing-3); - padding-left: 0; -} diff --git a/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/CollapsiblePropertyEditor.test.tsx b/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/CollapsiblePropertyEditor.test.tsx deleted file mode 100644 index 56f1f156a11..00000000000 --- a/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/CollapsiblePropertyEditor.test.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import { - CollapsiblePropertyEditor, - type CollapsiblePropertyEditorProps, -} from './CollapsiblePropertyEditor'; -import userEvent from '@testing-library/user-event'; -import { screen } from '@testing-library/react'; -import { renderWithProviders } from 'app-development/test/mocks'; -import { textMock } from '@studio/testing/mocks/i18nMock'; - -// Test data -const label = 'Test label'; -const children =
Test children
; -const icon =
Test icon
; - -describe('CollapsiblePropertyEditor', () => { - it('should render the label', () => { - renderCollapsiblePropertyEditor({ label: label }); - expect(screen.getByText('Test label')).toBeInTheDocument(); - }); - - it('should render the icon', () => { - renderCollapsiblePropertyEditor({ icon:
Test icon
}); - expect(screen.getByText('Test icon')).toBeInTheDocument(); - }); - - it('should render the children', () => { - renderCollapsiblePropertyEditor(); - expect(screen.queryByText('Test children')).not.toBeInTheDocument(); - }); - - it('should render the children when the button is clicked', async () => { - const user = userEvent.setup(); - renderCollapsiblePropertyEditor(); - await user.click(screen.getByText('Test label')); - expect(screen.getByText('Test children')).toBeInTheDocument(); - }); - - it('should hide the children when the close button is clicked', async () => { - const user = userEvent.setup(); - renderCollapsiblePropertyEditor(); - await user.click(screen.getByText('Test label')); - expect(screen.getByText('Test children')).toBeInTheDocument(); - await user.click(screen.getByRole('button', { name: textMock('general.close') })); - expect(screen.queryByText('Test children')).not.toBeInTheDocument(); - }); -}); - -const defaultProps: CollapsiblePropertyEditorProps = { - label, - children, - icon, -}; - -const renderCollapsiblePropertyEditor = (props: Partial = {}) => { - renderWithProviders()(); -}; diff --git a/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/CollapsiblePropertyEditor.tsx b/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/CollapsiblePropertyEditor.tsx deleted file mode 100644 index e4ea3c369ce..00000000000 --- a/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/CollapsiblePropertyEditor.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React, { useState } from 'react'; -import { PlusCircleIcon, XMarkIcon } from '@studio/icons'; -import { StudioButton, StudioProperty } from '@studio/components'; -import classes from './CollapsiblePropertyEditor.module.css'; -import { useTranslation } from 'react-i18next'; -import cn from 'classnames'; - -export type CollapsiblePropertyEditorProps = { - label?: string; - children?: React.ReactNode; - icon?: React.ReactNode; - disabledCloseButton?: boolean; -}; - -export const CollapsiblePropertyEditor = ({ - label, - children, - disabledCloseButton = false, - icon = , -}: CollapsiblePropertyEditorProps) => { - const { t } = useTranslation(); - const [isVisible, setIsVisible] = useState(false); - - return ( -
- {!isVisible ? ( - setIsVisible(true)} - property={label} - /> - ) : ( - <> -
{children}
- {!disabledCloseButton && ( - } - onClick={() => setIsVisible(false)} - title={t('general.close')} - variant='secondary' - /> - )} - - )} -
- ); -}; diff --git a/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/index.ts b/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/index.ts deleted file mode 100644 index b7790d086df..00000000000 --- a/frontend/packages/ux-editor/src/components/config/CollapsiblePropertyEditor/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { CollapsiblePropertyEditor } from './CollapsiblePropertyEditor'; diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx index c219f0340ff..c1564d7b45e 100644 --- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx @@ -160,7 +160,7 @@ describe('FormComponentConfig', () => { expect(screen.queryByText('unsupportedProperty')).not.toBeInTheDocument(); }); - it('should render CollapsiblePropertyEditor for the "sortOrder" property', async () => { + it('should render property text for the "sortOrder" property', async () => { const user = userEvent.setup(); render({ props: { @@ -187,7 +187,7 @@ describe('FormComponentConfig', () => { ).toBeInTheDocument(); }); - it('should render CollapsiblePropertyEditor for the "showValidations" property and EditStringValue for other properties', () => { + it('should render property text for the "showValidations" property', () => { render({ props: { schema: { @@ -217,7 +217,7 @@ describe('FormComponentConfig', () => { ).toBeInTheDocument(); }); - it('should render CollapsiblePropertyEditor for "preselectedOptionIndex" and EditNumberValue for other properties', () => { + it('should render property text for "preselectedOptionIndex" and EditNumberValue for other properties', () => { render({ props: { schema: { diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx index eb79c049fbd..26ece722c62 100644 --- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx @@ -21,8 +21,6 @@ import { StudioButton, StudioCard, StudioProperty } from '@studio/components'; import { useComponentPropertyEnumValue } from '@altinn/ux-editor/hooks/useComponentPropertyEnumValue'; import { SelectPropertyEditor } from './SelectPropertyEditor/SelectPropertyEditor'; -//TODO: 2- Add test cases for the SelectPropertyEditor component. -//TODO: 3- Remove CollapsiblePropertyEditor folder. export interface IEditFormComponentProps { editFormId: string; component: FormItem; diff --git a/frontend/packages/ux-editor/src/components/config/SelectPropertyEditor/index.ts b/frontend/packages/ux-editor/src/components/config/SelectPropertyEditor/index.ts new file mode 100644 index 00000000000..400bc19d606 --- /dev/null +++ b/frontend/packages/ux-editor/src/components/config/SelectPropertyEditor/index.ts @@ -0,0 +1 @@ +export { SelectPropertyEditor } from './SelectPropertyEditor'; From df71b07b04f752f666780617e3b48d891dd167e5 Mon Sep 17 00:00:00 2001 From: Jamal Alabdullah Date: Mon, 17 Feb 2025 12:30:27 +0100 Subject: [PATCH 05/13] added test --- .../config/FormComponentConfig.test.tsx | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx index c1564d7b45e..e127ea100e2 100644 --- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx @@ -6,13 +6,14 @@ import { componentMocks } from '../../testing/componentMocks'; import InputSchema from '../../testing/schemas/json/component/Input.schema.v1.json'; import DatepickerSchema from '../../testing/schemas/json/component/Datepicker.schema.v1.json'; import type { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; -import { screen } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import { textMock } from '@studio/testing/mocks/i18nMock'; import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs'; import userEvent from '@testing-library/user-event'; import { ComponentType } from 'app-shared/types/ComponentType'; const somePropertyName = 'somePropertyName'; +const selectedDataType = 'selectedDataType'; const customTextMockToHandleUndefined = ( keys: string | string[], variables?: KeyValuePairs, @@ -281,6 +282,32 @@ describe('FormComponentConfig', () => { ).not.toBeInTheDocument(); }); + it('should render selectedDataType for SelectPropertyEditor ', async () => { + const user = userEvent.setup(); + render({ + props: { + schema: { + properties: { + [selectedDataType]: { + type: 'string', + enum: ['option1', 'option2'], + }, + }, + }, + }, + }); + const button = screen.getByRole('button', { + name: textMock(`ux_editor.component_properties.${selectedDataType}`), + }); + await waitFor(() => expect(button).toBeInTheDocument()); + await user.click(button); + expect( + screen.getByRole('combobox', { + name: textMock(`ux_editor.component_properties.${selectedDataType}`), + }), + ).toBeInTheDocument(); + }); + it('should show description text for objects if key is defined', () => { render({ props: { From c88888860c26508919680b76912d13d7cd2d84cc Mon Sep 17 00:00:00 2001 From: Jamal Alabdullah Date: Mon, 17 Feb 2025 12:48:11 +0100 Subject: [PATCH 06/13] added test --- .../config/FormComponentConfig.test.tsx | 63 +++++++++++++------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx index e127ea100e2..942c3f913df 100644 --- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx @@ -13,7 +13,6 @@ import userEvent from '@testing-library/user-event'; import { ComponentType } from 'app-shared/types/ComponentType'; const somePropertyName = 'somePropertyName'; -const selectedDataType = 'selectedDataType'; const customTextMockToHandleUndefined = ( keys: string | string[], variables?: KeyValuePairs, @@ -282,30 +281,56 @@ describe('FormComponentConfig', () => { ).not.toBeInTheDocument(); }); - it('should render selectedDataType for SelectPropertyEditor ', async () => { + it('should call handleComponentUpdate and setSelectedValue when array property is updated', async () => { const user = userEvent.setup(); - render({ - props: { - schema: { + const handleComponentUpdateMock = jest.fn(); + const propertyKey = 'supportedArrayProperty'; + renderWithProviders( + , + ); + const arrayPropertyButton = screen.getByRole('button', { + name: textMock(`ux_editor.component_properties.${propertyKey}`), }); - const button = screen.getByRole('button', { - name: textMock(`ux_editor.component_properties.${selectedDataType}`), + await user.click(arrayPropertyButton); + + const combobox = screen.getByRole('combobox', { + name: textMock(`ux_editor.component_properties.${propertyKey}`), }); - await waitFor(() => expect(button).toBeInTheDocument()); - await user.click(button); - expect( - screen.getByRole('combobox', { - name: textMock(`ux_editor.component_properties.${selectedDataType}`), - }), - ).toBeInTheDocument(); + await user.click(combobox); + + const option1 = screen.getByRole('option', { + name: textMock('ux_editor.component_properties.enum_option1'), + }); + await user.click(option1); + + await waitFor(() => { + expect(handleComponentUpdateMock).toHaveBeenCalledWith( + expect.objectContaining({ + [propertyKey]: ['option1'], + }), + ); + }); + + const selectedValueDisplay = screen.getByRole('option', { + name: textMock('ux_editor.component_properties.enum_option1'), + }); + expect(selectedValueDisplay).toBeInTheDocument(); }); it('should show description text for objects if key is defined', () => { From 01340706e13741f1425679ef85a9d55a11473e78 Mon Sep 17 00:00:00 2001 From: Jamal Alabdullah Date: Mon, 17 Feb 2025 13:15:56 +0100 Subject: [PATCH 07/13] added test --- .../config/FormComponentConfig.test.tsx | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx index 942c3f913df..b04bca80214 100644 --- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx @@ -494,6 +494,40 @@ describe('FormComponentConfig', () => { ); }); + it('should render array properties with enum values correctly', async () => { + const user = userEvent.setup(); + const propertyKey = 'supportedArrayProperty'; + const enumValues = ['option1', 'option2']; + render({ + props: { + schema: { + properties: { + [propertyKey]: { + type: 'array', + items: { + type: 'string', + enum: enumValues, + }, + }, + }, + }, + component: { + ...componentMocks.Input, + [propertyKey]: enumValues, + }, + }, + }); + const arrayPropertyButton = screen.getByRole('button', { + name: textMock(`ux_editor.component_properties.${propertyKey}`), + }); + await user.click(arrayPropertyButton); + for (const dataType of enumValues) { + expect( + screen.getByText(textMock(`ux_editor.component_properties.enum_${dataType}`)), + ).toBeInTheDocument(); + } + }); + it('should call handleComponentUpdate with updated component when hasCustomFileEndings is true', async () => { const handleComponentUpdateMock = jest.fn(); render({ From c43bc0bc4f8aa7808e851691d8bc2dddeccc6cdc Mon Sep 17 00:00:00 2001 From: Jamal Alabdullah Date: Mon, 17 Feb 2025 15:32:38 +0100 Subject: [PATCH 08/13] refacture --- .../components/config/FormComponentConfig.tsx | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx index 26ece722c62..d55609cd953 100644 --- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { Alert, Card, Heading, Paragraph } from '@digdir/designsystemet-react'; import type { FormComponent } from '../../types/FormComponent'; import { EditBooleanValue } from './editModal/EditBooleanValue'; @@ -48,6 +48,16 @@ export const FormComponentConfig = ({ const selectedDataType = useComponentPropertyEnumValue(); const [selectedValue, setSelectedValue] = useState([]); + const memoizedGetSelectedValuesDisplay = useMemo( + () => (propertyKey: string) => { + if (!component[propertyKey] || component[propertyKey].length === 0) return undefined; + return component[propertyKey].map((dataType: string) => ( +
{selectedDataType(dataType)}
+ )); + }, + [component, selectedDataType], + ); + if (!schema?.properties) return null; const { properties } = schema; @@ -274,12 +284,6 @@ export const FormComponentConfig = ({ {/** Array properties with enum values) */} {arrayPropertyKeys.map((propertyKey) => { - const selectedValuesDisplay = - component[propertyKey] && component[propertyKey].length > 0 - ? component[propertyKey].map((dataType) => ( -
{selectedDataType(dataType)}
- )) - : undefined; const selectProperty = selectedValue.length > 0 ? t('ux_editor.component_properties.selected_validations') @@ -289,13 +293,17 @@ export const FormComponentConfig = ({ key={propertyKey} property={selectProperty} title={componentPropertyLabel(propertyKey)} - value={selectedValuesDisplay} + value={memoizedGetSelectedValuesDisplay(propertyKey)} > { - setSelectedValue(updatedComponent[propertyKey]); - handleComponentUpdate(updatedComponent); + try { + setSelectedValue(updatedComponent[propertyKey] || []); + handleComponentUpdate(updatedComponent); + } catch (error) { + console.error(error); + } }} propertyKey={propertyKey} key={propertyKey} From 6b4f59639d190008c4aa390163e4beaed543fba1 Mon Sep 17 00:00:00 2001 From: Jamal Alabdullah Date: Mon, 17 Feb 2025 15:51:44 +0100 Subject: [PATCH 09/13] refacture --- .../components/config/FormComponentConfig.tsx | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx index d55609cd953..271d02812ca 100644 --- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx @@ -58,6 +58,20 @@ export const FormComponentConfig = ({ [component, selectedDataType], ); + const memoizedSelectedStringPropertiesDisplay = useMemo( + () => (propertyKey: string) => { + try { + const value = component[propertyKey]; + if (Array.isArray(value)) return value.map((dataType) => selectedDataType(dataType)); + if (value) return selectedDataType(value); + return undefined; + } catch (error) { + return undefined; + } + }, + [component, selectedDataType], + ); + if (!schema?.properties) return null; const { properties } = schema; @@ -235,19 +249,12 @@ export const FormComponentConfig = ({ {/** String properties */} {stringPropertyKeys.map((propertyKey) => { - const selectedStringPropertiesDisplay = () => { - const value = component[propertyKey]; - if (Array.isArray(value)) return value.map((dataType) => selectedDataType(dataType)); - if (value) return selectedDataType(value); - return undefined; - }; - return ( { - try { - setSelectedValue(updatedComponent[propertyKey] || []); - handleComponentUpdate(updatedComponent); - } catch (error) { - console.error(error); - } + setSelectedValue(updatedComponent[propertyKey]); + handleComponentUpdate(updatedComponent); }} propertyKey={propertyKey} key={propertyKey} From a283811294c9cd4ef60477fc9ede4c62594bf484 Mon Sep 17 00:00:00 2001 From: Jamal Alabdullah Date: Mon, 17 Feb 2025 16:25:34 +0100 Subject: [PATCH 10/13] added test --- .../config/FormComponentConfig.test.tsx | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx index b04bca80214..52853722754 100644 --- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx @@ -333,6 +333,41 @@ describe('FormComponentConfig', () => { expect(selectedValueDisplay).toBeInTheDocument(); }); + it('should return undefined when an error occurs in memoizedSelectedStringPropertiesDisplay', async () => { + const propertyKey = 'invalidPropertyKey'; + const handleComponentUpdateMock = jest.fn(); + const user = userEvent.setup(); + render({ + props: { + schema: { + properties: { + [propertyKey]: { + type: 'array', + items: { + type: 'string', + enum: ['option1', 'option2'], + }, + }, + }, + }, + handleComponentUpdate: handleComponentUpdateMock, + }, + }); + const arrayPropertyButton = screen.getByRole('button', { + name: textMock(`ux_editor.component_properties.${propertyKey}`), + }); + await user.click(arrayPropertyButton); + const combobox = screen.getByRole('combobox', { + name: textMock(`ux_editor.component_properties.${propertyKey}`), + }); + await user.click(combobox); + const option1 = screen.getByRole('option', { + name: textMock('ux_editor.component_properties.enum_option1'), + }); + await user.click(option1); + expect(handleComponentUpdateMock).not.toHaveBeenCalled(); + }); + it('should show description text for objects if key is defined', () => { render({ props: { From 3985752c64d2e361ff01ae57676a2fc979f89e04 Mon Sep 17 00:00:00 2001 From: Jamal Alabdullah Date: Mon, 17 Feb 2025 19:34:58 +0100 Subject: [PATCH 11/13] refacture --- .../config/FormComponentConfig.test.tsx | 46 ------------------- .../components/config/FormComponentConfig.tsx | 11 ++--- 2 files changed, 3 insertions(+), 54 deletions(-) diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx index 52853722754..52e4a0a3e1a 100644 --- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.test.tsx @@ -333,52 +333,6 @@ describe('FormComponentConfig', () => { expect(selectedValueDisplay).toBeInTheDocument(); }); - it('should return undefined when an error occurs in memoizedSelectedStringPropertiesDisplay', async () => { - const propertyKey = 'invalidPropertyKey'; - const handleComponentUpdateMock = jest.fn(); - const user = userEvent.setup(); - render({ - props: { - schema: { - properties: { - [propertyKey]: { - type: 'array', - items: { - type: 'string', - enum: ['option1', 'option2'], - }, - }, - }, - }, - handleComponentUpdate: handleComponentUpdateMock, - }, - }); - const arrayPropertyButton = screen.getByRole('button', { - name: textMock(`ux_editor.component_properties.${propertyKey}`), - }); - await user.click(arrayPropertyButton); - const combobox = screen.getByRole('combobox', { - name: textMock(`ux_editor.component_properties.${propertyKey}`), - }); - await user.click(combobox); - const option1 = screen.getByRole('option', { - name: textMock('ux_editor.component_properties.enum_option1'), - }); - await user.click(option1); - expect(handleComponentUpdateMock).not.toHaveBeenCalled(); - }); - - it('should show description text for objects if key is defined', () => { - render({ - props: { - schema: InputSchema, - }, - }); - expect( - screen.getByText(textMock('ux_editor.component_properties_description.pageBreak')), - ).toBeInTheDocument(); - }); - it('should render default boolean values if defined', async () => { const user = userEvent.setup(); render({ diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx index 271d02812ca..fcde7bff554 100644 --- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx @@ -60,14 +60,9 @@ export const FormComponentConfig = ({ const memoizedSelectedStringPropertiesDisplay = useMemo( () => (propertyKey: string) => { - try { - const value = component[propertyKey]; - if (Array.isArray(value)) return value.map((dataType) => selectedDataType(dataType)); - if (value) return selectedDataType(value); - return undefined; - } catch (error) { - return undefined; - } + const value = component[propertyKey]; + if (Array.isArray(value)) return value.map((dataType) => selectedDataType(dataType)); + return value ? selectedDataType(value) : undefined; }, [component, selectedDataType], ); From f69a959d2fdf35a20c4c4ee2e32b52e06ed5e5b9 Mon Sep 17 00:00:00 2001 From: Jamal Alabdullah Date: Thu, 20 Feb 2025 16:14:59 +0100 Subject: [PATCH 12/13] fixed comments --- frontend/language/src/nb.json | 3 +-- .../ux-editor/src/components/config/FormComponentConfig.tsx | 6 +----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/frontend/language/src/nb.json b/frontend/language/src/nb.json index 5e24e4d2ac3..655187f69c0 100644 --- a/frontend/language/src/nb.json +++ b/frontend/language/src/nb.json @@ -1446,7 +1446,6 @@ "ux_editor.component_properties.select_all_attachments": "Alle vedlegg", "ux_editor.component_properties.select_attachments": "Velg vedlegg", "ux_editor.component_properties.select_pdf": "Inkluder skjemagenerert pdf", - "ux_editor.component_properties.selected_validations": "Valideringstyper", "ux_editor.component_properties.sender": "Den som har sendt inn skjemaet", "ux_editor.component_properties.severity": "Alvorlighetsgrad", "ux_editor.component_properties.showAddButton": "Vis Legg til-knapp", @@ -1456,7 +1455,7 @@ "ux_editor.component_properties.showIcon": "Vis ikon", "ux_editor.component_properties.showLabelsInTable": "Alternativene skal alltid vises i tabeller", "ux_editor.component_properties.showPageInAccordion": "Vis side i trekkspilliste", - "ux_editor.component_properties.showValidations": "Vis valideringstyper", + "ux_editor.component_properties.showValidations": "Valideringstyper", "ux_editor.component_properties.simplified": "Forenklet visning", "ux_editor.component_properties.size": "Størrelse", "ux_editor.component_properties.sortOrder": "Sorteringsrekkefølge", diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx index fcde7bff554..e7a8149d7df 100644 --- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx @@ -286,14 +286,10 @@ export const FormComponentConfig = ({ {/** Array properties with enum values) */} {arrayPropertyKeys.map((propertyKey) => { - const selectProperty = - selectedValue.length > 0 - ? t('ux_editor.component_properties.selected_validations') - : componentPropertyLabel(propertyKey); return ( From a9fe4311465f9c160d16c9aaa0158db8f84cc36e Mon Sep 17 00:00:00 2001 From: Jamal Alabdullah Date: Thu, 20 Feb 2025 16:30:59 +0100 Subject: [PATCH 13/13] uodated --- .../ux-editor/src/components/config/FormComponentConfig.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx index e7a8149d7df..88cb3086137 100644 --- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx @@ -46,7 +46,6 @@ export const FormComponentConfig = ({ const [showGrid, setShowGrid] = useState(false); const selectedDataType = useComponentPropertyEnumValue(); - const [selectedValue, setSelectedValue] = useState([]); const memoizedGetSelectedValuesDisplay = useMemo( () => (propertyKey: string) => { @@ -296,7 +295,6 @@ export const FormComponentConfig = ({ { - setSelectedValue(updatedComponent[propertyKey]); handleComponentUpdate(updatedComponent); }} propertyKey={propertyKey}