From fd872342c9fdccb0d0920c579401a133ca3156d4 Mon Sep 17 00:00:00 2001 From: andreastanderen Date: Wed, 8 Jan 2025 16:04:44 +0100 Subject: [PATCH 1/8] simplify studioToggleableTextfield some --- frontend/language/src/nb.json | 5 +- .../StudioIconTextfield.module.css | 7 +- .../StudioIconTextfield.stories.tsx | 2 +- .../StudioIconTextfield.test.tsx | 40 +++-- .../StudioIconTextfield.tsx | 11 +- .../StudioRecommendedNextAction.stories.tsx | 2 +- .../StudioTextfieldToggleView.module.css | 13 +- .../StudioTextfieldToggleView.test.tsx | 55 +++---- .../StudioTextfieldToggleView.tsx | 27 ++-- .../StudioToggleableTextfield.stories.tsx | 18 +-- .../StudioToggleableTextfield.test.tsx | 143 +++++++--------- .../StudioToggleableTextfield.tsx | 58 ++++--- ...tudioToggleableTextfieldSchema.stories.tsx | 17 +- .../StudioToggleableTextfieldSchema.test.tsx | 153 ++++-------------- .../StudioToggleableTextfieldSchema.tsx | 16 +- .../pages/CodeListPage/CodeListPage.test.tsx | 30 ++-- .../CodeListPage/CodeLists/CodeLists.test.tsx | 10 +- .../CodeLists/EditCodeList/EditCodeList.tsx | 25 +-- .../ConfigContent/ConfigContent.test.tsx | 2 +- .../EditLayoutSetName.test.tsx | 16 +- .../EditLayoutSetName/EditLayoutSetName.tsx | 23 +-- .../RecommendedActionChangeName.tsx | 3 +- .../EditTaskId/EditTaskId.test.tsx | 32 ++-- .../ConfigContent/EditTaskId/EditTaskId.tsx | 20 +-- .../CustomReceipt/CustomReceipt.module.css | 5 - .../CustomReceipt/CustomReceipt.test.tsx | 8 +- .../CustomReceipt/CustomReceipt.tsx | 25 +-- .../CustomReceiptContent.test.tsx | 5 +- .../ConfigPanel/ConfigPanel.test.tsx | 2 +- .../PageConfigPanel/EditPageId.module.css | 4 - .../PageConfigPanel/EditPageId.test.tsx | 20 +-- .../Properties/PageConfigPanel/EditPageId.tsx | 19 +-- .../PageConfigPanel/PageConfigPanel.test.tsx | 2 +- .../components/Properties/Properties.test.tsx | 10 +- .../EditComponentIdRow.test.tsx | 93 +++++------ .../EditComponentIdRow/EditComponentIdRow.tsx | 32 ++-- .../PropertiesHeader.test.tsx | 4 +- .../editModal/EditImage/EditImage.test.tsx | 10 +- .../ExternalImage/ExternalImage.module.css | 4 - .../ExternalImage/ExternalImage.test.tsx | 15 +- .../EditImage/ExternalImage/ExternalImage.tsx | 66 ++++---- .../DesignView/AddItem/ItemInfo/ItemInfo.tsx | 2 +- .../ProcessEditorPage/ProcessEditorPage.ts | 10 +- 43 files changed, 402 insertions(+), 662 deletions(-) diff --git a/frontend/language/src/nb.json b/frontend/language/src/nb.json index ff4bda69562..55260d3b940 100644 --- a/frontend/language/src/nb.json +++ b/frontend/language/src/nb.json @@ -15,7 +15,6 @@ "app_content_library.code_lists.code_list_accordion_title": "Kodeliste: {{codeListTitle}}", "app_content_library.code_lists.code_list_edit_id_label": "Navn på kodeliste", "app_content_library.code_lists.code_list_edit_id_title": "Rediger navn på kodelisten {{codeListName}}", - "app_content_library.code_lists.code_list_view_id_title": "Navn på kodeliste: {{codeListName}}", "app_content_library.code_lists.code_lists_count_info_none": "Det finnes ingen kodelister i biblioteket.", "app_content_library.code_lists.code_lists_count_info_plural": "Det finnes {{codeListsCount}} kodelister i biblioteket.", "app_content_library.code_lists.code_lists_count_info_single": "Det finnes 1 kodeliste i biblioteket.", @@ -689,7 +688,7 @@ "process_editor.configuration_panel_actions_set_server_action_info": "Angir at handlingen knyttes til neste steg i prosessen.", "process_editor.configuration_panel_actions_set_server_action_label": "Knytt handlingen til neste steg.", "process_editor.configuration_panel_actions_title": "Handlinger", - "process_editor.configuration_panel_change_task_id": "Endre ID", + "process_editor.configuration_panel_change_task_id_label": "Oppgave-ID", "process_editor.configuration_panel_confirmation_task": "Oppgave: Bekreftelse", "process_editor.configuration_panel_custom_receipt_accordion_header": "Kvittering", "process_editor.configuration_panel_custom_receipt_cancel_button": "Avbryt", @@ -703,7 +702,6 @@ "process_editor.configuration_panel_custom_receipt_delete_receipt": "Er du sikker på at du vil slette kvitteringen din?", "process_editor.configuration_panel_custom_receipt_heading": "Opprett din egen kvittering", "process_editor.configuration_panel_custom_receipt_info": "Hvis du heller vil lage din egen kvittering, kan du opprette den her. Kvitteringen du lager selv vil overstyre standardkvitteringen.", - "process_editor.configuration_panel_custom_receipt_layout_set_name": "Navn på kvittering: ", "process_editor.configuration_panel_custom_receipt_layout_set_name_validation": "Navnet må ha minst 2 tegn", "process_editor.configuration_panel_custom_receipt_navigate_to_design_button": "Gå til Utforming", "process_editor.configuration_panel_custom_receipt_navigate_to_design_title": "Gå til Utforming for å utforme kvitteringen din", @@ -1553,7 +1551,6 @@ "ux_editor.file_upload_component.valid_file_endings": "Innstillinger for filopplastingskomponent", "ux_editor.formLayout.warning_duplicates": "Du har den samme ID-en på flere komponenter: ", "ux_editor.formLayout.warning_duplicates.cannot_publish": "Du kan ikke publisere appen eller konfigurere komponentene før du har rettet opp feilen.", - "ux_editor.id_identifier": "ID: {{item}}", "ux_editor.image_component.settings": "Innstillinger for bilde", "ux_editor.info": "Informasjon", "ux_editor.input_popover_label": "Gi nytt navn til siden", diff --git a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.module.css b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.module.css index 3de73802696..62bea004556 100644 --- a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.module.css +++ b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.module.css @@ -1,15 +1,14 @@ .container { display: flex; gap: var(--fds-spacing-2); - padding: var(--fds-spacing-3); - box-sizing: border-box; + align-items: flex-start; + padding: var(--fds-spacing-2) var(--fds-spacing-2); } .prefixIcon { color: var(--fds-semantic-text-neutral-default); - margin-top: var(--fds-spacing-7); + margin-top: var(--fds-spacing-10); font-size: var(--fds-sizing-4); - align-content: center; } .textfield { diff --git a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.stories.tsx b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.stories.tsx index 2e82b53a1b8..c3a3c9f958d 100644 --- a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.stories.tsx +++ b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.stories.tsx @@ -19,7 +19,7 @@ export default meta; export const Preview: Story = (args) => ; Preview.args = { - icon: , + Icon: PencilIcon, value: 2.3, error: 'Your custom error message!', }; diff --git a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.test.tsx b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.test.tsx index f5ac15a1ade..9041543696b 100644 --- a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.test.tsx +++ b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.test.tsx @@ -8,35 +8,22 @@ import { testCustomAttributes } from '../../test-utils/testCustomAttributes'; describe('StudioIconTextfield', () => { it('render the icon', async () => { - renderStudioIconTextfield({ - icon: , - }); - expect(screen.getByTitle('my key icon title')).toBeInTheDocument(); + renderStudioIconTextfield(); + expect(screen.getByRole('img', { hidden: true })).toBeInTheDocument(); }); it('should render label', () => { - renderStudioIconTextfield({ - icon:
, - label: 'id', - }); - expect(screen.getByLabelText('id')).toBeInTheDocument(); + renderStudioIconTextfield(); + expect(screen.getByLabelText(label)).toBeInTheDocument(); }); it('should execute onChange callback when input value changes', async () => { const user = userEvent.setup(); - const onChangeMock = jest.fn(); - - renderStudioIconTextfield({ - icon:
, - label: 'Your ID', - onChange: onChangeMock, - }); - - const input = screen.getByLabelText('Your ID'); - + renderStudioIconTextfield(); + const input = screen.getByRole('textbox', { name: label }); const inputValue = 'my id is 123'; await user.type(input, inputValue); - expect(onChangeMock).toHaveBeenCalledTimes(inputValue.length); + expect(onChange).toHaveBeenCalledTimes(inputValue.length); }); it('should forward the rest of the props to the input', () => { @@ -44,6 +31,15 @@ describe('StudioIconTextfield', () => { testCustomAttributes(renderStudioIconTextfield, getTextbox); }); }); -const renderStudioIconTextfield = (props: StudioIconTextfieldProps) => { - return render(); + +const label = 'label'; +const onChange = jest.fn(); +const defaultProps: StudioIconTextfieldProps = { + Icon: KeyVerticalIcon, + label, + onChange, +}; + +const renderStudioIconTextfield = (props: Partial = {}) => { + return render(); }; diff --git a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.tsx b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.tsx index 8591ed8d0fd..034d0aef701 100644 --- a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.tsx +++ b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.tsx @@ -1,25 +1,22 @@ import React, { forwardRef } from 'react'; import { StudioTextfield, type StudioTextfieldProps } from '../StudioTextfield'; import cn from 'classnames'; - import classes from './StudioIconTextfield.module.css'; export type StudioIconTextfieldProps = { - icon: React.ReactNode; + Icon?: React.ComponentType>; } & StudioTextfieldProps; export const StudioIconTextfield = forwardRef( ( - { icon, className: givenClassName, ...rest }: StudioIconTextfieldProps, + { Icon, className: givenClassName, ...rest }: StudioIconTextfieldProps, ref, ): React.ReactElement => { const className = cn(givenClassName, classes.container); return (
-
- {icon} -
- + +
); }, diff --git a/frontend/libs/studio-components/src/components/StudioRecommendedNextAction/StudioRecommendedNextAction.stories.tsx b/frontend/libs/studio-components/src/components/StudioRecommendedNextAction/StudioRecommendedNextAction.stories.tsx index 84c2da73005..5560fa62401 100644 --- a/frontend/libs/studio-components/src/components/StudioRecommendedNextAction/StudioRecommendedNextAction.stories.tsx +++ b/frontend/libs/studio-components/src/components/StudioRecommendedNextAction/StudioRecommendedNextAction.stories.tsx @@ -55,7 +55,7 @@ export const ExampleUseCase: ExampleUseCase = (args): React.ReactElement => { setName(e.target.value)} - icon={} + Icon={KeyVerticalIcon} size='sm' label='Nytt navn' /> diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.module.css b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.module.css index d74c50041e5..2fbf0bbd7e6 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.module.css +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.module.css @@ -11,6 +11,7 @@ grid-template-columns: auto 1fr; text-align: left; align-items: center; + justify-content: space-between; color: var(--fds-semantic-text-neutral-default); width: 100%; gap: var(--fds-spacing-1); @@ -33,18 +34,12 @@ flex-direction: column; } -.editIconWrapper { - flex: 1; - text-align: right; +.editIcon { display: none; } -.button:hover .editIconWrapper, -.button:focus .editIconWrapper { +.button:hover .editIcon, +.button:focus .editIcon { display: flex; align-items: center; } - -.editIcon { - margin-left: auto; -} diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.test.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.test.tsx index 46df7dc3c0c..600ea469b56 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.test.tsx +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.test.tsx @@ -3,52 +3,47 @@ import { render, screen } from '@testing-library/react'; import { StudioTextfieldToggleView } from './StudioTextfieldToggleView'; import type { StudioTextfieldToggleViewProps } from './StudioTextfieldToggleView'; import userEvent from '@testing-library/user-event'; +import { KeyVerticalIcon } from '@studio/icons'; describe('StudioTextfieldToggleView', () => { it('should render button text', () => { - renderStudioTextfieldToggleView({ children: 'My awesome button' }); - expect(screen.getByRole('button', { name: 'My awesome button' })).toBeInTheDocument(); + renderStudioTextfieldToggleView(); + expect(screen.getByRole('button', { name: value })).toBeInTheDocument(); }); it('should execute the "onClick" method when button is clicked', async () => { const user = userEvent.setup(); - const onClickMock = jest.fn(); - - renderStudioTextfieldToggleView({ children: 'My awesome button text', onClick: onClickMock }); - - await user.click(screen.getByRole('button', { name: 'My awesome button text' })); - expect(onClickMock).toHaveBeenCalledTimes(1); - }); - - it('should render the KeyVerticalIcon', () => { - renderStudioTextfieldToggleView({ children: 'My awesome button text' }); - - // Uses testId to find the KeyVerticalIcon, since it's not available for screen reader. - expect(screen.getByTestId('keyIcon')).toBeInTheDocument(); + renderStudioTextfieldToggleView(); + await user.click(screen.getByRole('button', { name: value })); + expect(onClick).toHaveBeenCalledTimes(1); }); - it('should render the PencilIcon', () => { - renderStudioTextfieldToggleView({ children: 'My awesome button text' }); - - // Uses testId to find the EditIcon, since it's not available for screen reader. - expect(screen.getByTestId('editIcon')).toBeInTheDocument(); + it('should render the both given Icon and pencilIcon', () => { + renderStudioTextfieldToggleView(); + expect(screen.getAllByRole('img', { hidden: true })).toHaveLength(2); }); it('should forward the rest of the props to the button', () => { - renderStudioTextfieldToggleView({ children: 'My awesome button text', disabled: true }); - expect(screen.getByRole('button', { name: 'My awesome button text' })).toBeDisabled(); + renderStudioTextfieldToggleView({ disabled: true }); + expect(screen.getByRole('button', { name: value })).toBeDisabled(); }); it('should show label if defined', () => { - const studioTextfieldToggleViewLabel = 'studioTextfieldToggleViewLabel'; - renderStudioTextfieldToggleView({ - children: 'My awesome button text', - label: studioTextfieldToggleViewLabel, - }); - expect(screen.getByText(studioTextfieldToggleViewLabel)).toBeInTheDocument(); + renderStudioTextfieldToggleView(); + expect(screen.getByText(label)).toBeInTheDocument(); }); }); -const renderStudioTextfieldToggleView = (props: Partial) => { - return render(); +const value = 'value'; +const label = 'label'; +const onClick = jest.fn(); +const defaultProps: StudioTextfieldToggleViewProps = { + value, + label, + onClick, + Icon: KeyVerticalIcon, +}; + +const renderStudioTextfieldToggleView = (props: Partial = {}) => { + return render(); }; diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.tsx index adb59aebb7d..58825ad6a52 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.tsx +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.tsx @@ -1,36 +1,37 @@ import React from 'react'; -import { PencilIcon, KeyVerticalIcon } from '@studio/icons'; +import { PencilIcon } from '@studio/icons'; import { StudioButton, type StudioButtonProps } from '@studio/components'; import classes from './StudioTextfieldToggleView.module.css'; import cn from 'classnames'; -export type StudioTextfieldToggleViewProps = StudioButtonProps & { +export type StudioTextfieldToggleViewProps = Omit & { + Icon?: React.ComponentType>; label?: string; }; export const StudioTextfieldToggleView = ({ onClick, - children, - title, label, className: givenClass, - icon = , + Icon, ...rest }: StudioTextfieldToggleViewProps) => { const className = cn(classes.button, givenClass); return ( - - - {icon} + + + - {label && {label}} - {children} + {label && ( + + {label} + + )} + {rest.value} - - - + ); }; diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.stories.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.stories.tsx index a196435ca38..6cfe57ac258 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.stories.tsx +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.stories.tsx @@ -1,7 +1,6 @@ import React from 'react'; import type { Meta, StoryFn } from '@storybook/react'; import { StudioToggleableTextfield } from './StudioToggleableTextfield'; -import { KeyVerticalIcon } from '@studio/icons'; type Story = StoryFn; @@ -16,19 +15,10 @@ export const Preview: Story = (args) => ( Preview.args = { onIsViewMode: () => {}, - viewProps: { - variant: 'tertiary', - size: 'small', - label: 'My awesome label', - children: 'My awesome value', - }, - inputProps: { - icon: , - label: 'My awesome label', - size: 'small', - placeholder: 'Placeholder', - error: '', - }, + label: 'My awesome label', + title: 'My awesome title', + value: 'My awesome value', + error: '', }; export default meta; diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.test.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.test.tsx index 5f72290b1a4..e93ac27431a 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.test.tsx +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render, screen, fireEvent } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import { StudioToggleableTextfield, type StudioToggleableTextfieldProps, @@ -7,129 +7,96 @@ import { import userEvent from '@testing-library/user-event'; +const value: string = 'value'; +const label: string = 'label'; +const customValidation = jest.fn(); +const onBlur = jest.fn(); +const onChange = jest.fn(); + describe('StudioToggleableTextfield', () => { + afterEach(jest.clearAllMocks); + it('Renders the view mode by default', () => { - renderStudioTextField({ - viewProps: { children: 'Edit binding' }, - }); - expect(screen.getByRole('button', { name: 'Edit binding' })).toBeInTheDocument(); + renderStudioTextField(); + expect(screen.getByRole('button', { name: value })).toBeInTheDocument(); }); it('should toggle to edit-mode when edit button is clicked', async () => { const user = userEvent.setup(); - renderStudioTextField({ - viewProps: { children: 'Edit name' }, - inputProps: { value: '', icon:
, label: 'Your name' }, - }); - await user.click(screen.getByRole('button', { name: 'Edit name' })); - expect(screen.getByLabelText('Your name')).toBeEnabled(); + renderStudioTextField(); + await user.click(screen.getByRole('button', { name: value })); + expect(screen.getByRole('textbox', { name: label })).toBeInTheDocument(); }); it('should run custom validation when value changes', async () => { - const customValidation = jest.fn(); const user = userEvent.setup(); - renderStudioTextField({ - viewProps: { children: 'Edit name' }, - inputProps: { value: '', label: 'Your name', icon:
}, - customValidation, - }); - await user.click(screen.getByRole('button', { name: 'Edit name' })); - + renderStudioTextField({ customValidation }); + await user.click(screen.getByRole('button', { name: value })); const typedInputValue = 'John'; - await user.type(screen.getByLabelText('Your name'), typedInputValue); - + await user.type(screen.getByRole('textbox', { name: label }), typedInputValue); expect(customValidation).toHaveBeenCalledTimes(typedInputValue.length); }); - it('should be toggle back to view mode on blur', async () => { + it('should toggle back to view mode on blur', async () => { const user = userEvent.setup(); - - renderStudioTextField({ - viewProps: { children: 'edit' }, - inputProps: { value: 'value', label: 'Your name', icon:
}, - }); - - await user.click(screen.getByRole('button', { name: 'edit' })); - expect(screen.getByLabelText('Your name')).toBeEnabled(); - expect(screen.queryByRole('button', { name: 'edit' })).not.toBeInTheDocument(); - - fireEvent.blur(screen.getByLabelText('Your name')); - await screen.findByRole('button', { name: 'edit' }); + renderStudioTextField(); + const viewButton = screen.getByRole('button', { name: value }); + await user.click(viewButton); + const editTextfield = screen.getByRole('textbox', { name: label }); + expect(editTextfield).toBeInTheDocument(); + await user.tab(); + expect(screen.getByRole('button', { name: value })).toBeInTheDocument(); }); it('should execute onBlur method when input is blurred', async () => { - const onBlurMock = jest.fn(); const user = userEvent.setup(); - renderStudioTextField({ - viewProps: { children: 'Edit name' }, - inputProps: { onBlur: onBlurMock, label: 'Your name', icon:
}, - }); - - await user.click(screen.getByRole('button', { name: 'Edit name' })); - fireEvent.blur(screen.getByLabelText('Your name')); - expect(onBlurMock).toHaveBeenCalledTimes(1); + renderStudioTextField(); + await user.click(screen.getByRole('button', { name: value })); + await user.tab(); + expect(onBlur).toHaveBeenCalledTimes(1); }); it('should not toggle view on blur when input field has error', async () => { const user = userEvent.setup(); - - renderStudioTextField({ - viewProps: { children: 'Edit your name' }, - inputProps: { label: 'Your name', icon:
, error: 'Your name is a required field' }, - }); - - await user.click(screen.getByRole('button', { name: 'Edit your name' })); - - const inputField = screen.getByLabelText('Your name'); - fireEvent.blur(inputField); - - expect(inputField).toHaveAttribute('aria-invalid', 'true'); - expect(screen.getByText('Your name is a required field')).toBeInTheDocument(); - expect(screen.queryByRole('button', { name: 'Edit your name' })).not.toBeInTheDocument(); + const error = 'Your name is a required field'; + renderStudioTextField({ error }); + await user.click(screen.getByRole('button', { name: value })); + await user.tab(); + expect(screen.getByRole('textbox', { name: label })).toHaveAttribute('aria-invalid', 'true'); + expect(screen.getByText(error)).toBeInTheDocument(); + expect(screen.queryByRole('button', { name: value })).not.toBeInTheDocument(); }); it('should execute onChange method when input value changes', async () => { - const onChangeMock = jest.fn(); const user = userEvent.setup(); - - renderStudioTextField({ - viewProps: { children: 'edit' }, - inputProps: { onChange: onChangeMock, label: 'Your name', icon:
}, - }); - + renderStudioTextField(); const inputValue = 'John'; - await user.click(screen.getByRole('button', { name: 'edit' })); - await user.type(screen.getByLabelText('Your name'), inputValue); - - expect(onChangeMock).toHaveBeenCalledTimes(inputValue.length); + await user.click(screen.getByRole('button', { name: value })); + await user.type(screen.getByRole('textbox', { name: label }), inputValue); + expect(onChange).toHaveBeenCalledTimes(inputValue.length); }); it('should render error message if customValidation occured', async () => { const user = userEvent.setup(); - + const customError = 'Your name cannot include "test"'; renderStudioTextField({ - viewProps: { children: 'Edit name' }, - inputProps: { label: 'Your name', icon:
}, - customValidation: (value: string) => - value === 'test' ? 'Your name cannot be "test"' : undefined, + customValidation: (valueToValidate: string) => + valueToValidate.includes('test') ? customError : undefined, }); - - await user.click(screen.getByRole('button', { name: 'Edit name' })); - await user.type(screen.getByLabelText('Your name'), 'test'); - expect(screen.getByText('Your name cannot be "test"')); + await user.click(screen.getByRole('button', { name: value })); + await user.type(screen.getByRole('textbox', { name: label }), 'test'); + expect(screen.getByText(customError)); }); }); -const renderStudioTextField = (props: Partial) => { - const defaultProps: StudioToggleableTextfieldProps = { - inputProps: { - value: 'value', - icon:
, - }, - viewProps: { - children: 'edit', - }, - customValidation: jest.fn(), - }; +const defaultProps: StudioToggleableTextfieldProps = { + label, + value, + onBlur, + onChange, + customValidation, +}; + +const renderStudioTextField = (props: Partial = {}) => { return render(); }; diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.tsx index f5eb7472a15..9493c2f33fd 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.tsx +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.tsx @@ -1,33 +1,37 @@ import React, { forwardRef, useEffect, useState } from 'react'; -import { - StudioTextfieldToggleView, - type StudioTextfieldToggleViewProps, -} from './StudioTextfieldToggleView'; +import { StudioTextfieldToggleView } from './StudioTextfieldToggleView'; -import { StudioIconTextfield, type StudioIconTextfieldProps } from '../StudioIconTextfield'; +import { StudioIconTextfield } from '../StudioIconTextfield'; +import { KeyVerticalIcon } from '../../../../studio-icons'; export type StudioToggleableTextfieldProps = { customValidation?: (value: string) => string | undefined; - inputProps: StudioIconTextfieldProps; - viewProps: Omit; + error?: string; + Icon?: React.ComponentType>; + label: string; + onBlur?: (event: React.ChangeEvent) => void; + onChange?: (event: React.ChangeEvent) => void; onIsViewMode?: (isViewMode: boolean) => void; - setViewModeByDefault?: boolean; - autoFocus?: boolean; + title?: string; + value: string; }; export const StudioToggleableTextfield = forwardRef( ( { - inputProps, - viewProps, customValidation, + error, + Icon = KeyVerticalIcon, + label, + onBlur, + onChange, onIsViewMode, - setViewModeByDefault = true, - autoFocus = true, + title, + value, }: StudioToggleableTextfieldProps, ref, ) => { - const [isViewMode, setIsViewMode] = useState(setViewModeByDefault); + const [isViewMode, setIsViewMode] = useState(true); const [errorMessage, setErrorMessage] = useState(null); useEffect(() => { @@ -51,12 +55,12 @@ export const StudioToggleableTextfield = forwardRef): void => { // Should not close the view mode or blur if there is an error - if (errorMessage || inputProps.error) { + if (errorMessage || error) { return; } toggleViewMode(); - inputProps.onBlur?.(event); + onBlur?.(event); }; const handleOnChange = (event: React.ChangeEvent) => { @@ -64,19 +68,31 @@ export const StudioToggleableTextfield = forwardRef; + if (isViewMode) + return ( + + ); return ( ); }, diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.stories.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.stories.tsx index 5ef03fddad9..b10b419bc0e 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.stories.tsx +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.stories.tsx @@ -1,7 +1,6 @@ import React from 'react'; import type { Meta, StoryFn } from '@storybook/react'; import { StudioToggleableTextfieldSchema } from './StudioToggleableTextfieldSchema'; -import { KeyVerticalIcon } from '@studio/icons'; type Story = StoryFn; @@ -15,18 +14,10 @@ export const Preview: Story = (args) => ( ); Preview.args = { - viewProps: { - variant: 'tertiary', - size: 'small', - children: 'My awesome value', - }, - inputProps: { - icon: , - label: 'My awesome label', - size: 'small', - placeholder: 'Placeholder', - error: '', - }, + label: 'My awesome label', + title: 'My awesome title', + value: 'My awesome value', + error: '', }; export default meta; diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.test.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.test.tsx index 05a217fcf11..399da1f1650 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.test.tsx +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.test.tsx @@ -4,7 +4,7 @@ import { StudioToggleableTextfieldSchema, type StudioToggleableTextfieldSchemaProps, } from './StudioToggleableTextfieldSchema'; -import { fireEvent, render, screen } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; const defaultLayoutSchemaMock: JsonSchema = { @@ -27,112 +27,54 @@ const defaultLayoutSchemaMock: JsonSchema = { }, }, }; - +const value: string = 'value'; +const label: string = 'label'; const defaultProps: StudioToggleableTextfieldSchemaProps = { layoutSchema: defaultLayoutSchemaMock, relatedSchemas: [], - viewProps: { - value: '', - onChange: () => {}, - }, - inputProps: { - value: '', - onChange: () => {}, - icon:
, - }, + label, + value, + onChange: jest.fn(), propertyPath: 'definitions/component/properties/id', onError: jest.fn(), }; describe('StudioToggleableTextfieldSchema', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - it('should render as view mode as default and support rest props', () => { - renderStudioTextfieldSchema({ - viewProps: { - children: 'Edit id', - className: 'test-class', - }, - }); - const editButton = screen.getByRole('button', { name: 'Edit id' }); - expect(editButton).toBeInTheDocument(); - expect(editButton).toHaveClass('test-class'); - }); + beforeEach(jest.clearAllMocks); it('should toggle to edit mode when clicking edit', async () => { const user = userEvent.setup(); - - renderStudioTextfieldSchema({ - viewProps: { - children: 'Edit id', - }, - inputProps: { - ...defaultProps.inputProps, - label: 'Your id', - }, - }); - - await user.click(screen.getByRole('button', { name: 'Edit id' })); - expect(screen.getByLabelText('Your id')).toBeInTheDocument(); + renderStudioTextfieldSchema(); + await user.click(screen.getByRole('button', { name: value })); + expect(screen.getByRole('textbox', { name: label })).toBeInTheDocument(); }); it('should toggle to view mode on blur', async () => { const user = userEvent.setup(); - - renderStudioTextfieldSchema({ - viewProps: { - children: 'Edit id', - }, - inputProps: { - ...defaultProps.inputProps, - label: 'Your id', - }, - }); - - await user.click(screen.getByRole('button', { name: 'Edit id' })); - expect(screen.queryByRole('button', { name: 'Edit id' })).not.toBeInTheDocument(); - - fireEvent.blur(screen.getByLabelText('Your id')); - expect(screen.getByRole('button', { name: 'Edit id' })).toBeInTheDocument(); + renderStudioTextfieldSchema(); + await user.click(screen.getByRole('button', { name: value })); + expect(screen.queryByRole('button', { name: value })).not.toBeInTheDocument(); + await user.tab(); + expect(screen.getByRole('button', { name: value })).toBeInTheDocument(); }); it('should not toggle to view mode on blur if input is invalid', async () => { const user = userEvent.setup(); - + const error: string = 'error message'; renderStudioTextfieldSchema({ - viewProps: { - children: 'Edit id', - }, - inputProps: { - ...defaultProps.inputProps, - label: 'Your id', - error: 'my awesome error message', - }, + ...defaultProps, + error, }); - - await user.click(screen.getByRole('button', { name: 'Edit id' })); - expect(screen.queryByRole('button', { name: 'Edit id' })).not.toBeInTheDocument(); - - fireEvent.blur(screen.getByLabelText('Your id')); - expect(screen.queryByRole('button', { name: 'Edit id' })).not.toBeInTheDocument(); + await user.click(screen.getByRole('button', { name: value })); + await user.tab(); + expect(screen.queryByRole('button', { name: value })).not.toBeInTheDocument(); }); it('should validate field against json schema and invoke "onError" if validation has errors', async () => { const user = userEvent.setup(); - - renderStudioTextfieldSchema({ - viewProps: { - children: 'Edit id', - }, - inputProps: { - ...defaultProps.inputProps, - label: 'Your id', - }, - }); - await user.click(screen.getByRole('button', { name: 'Edit id' })); - - await user.type(screen.getByLabelText('Your id'), 'invalid-value-01'); + renderStudioTextfieldSchema(); + await user.click(screen.getByRole('button', { name: value })); + await user.type(screen.getByRole('textbox', { name: label }), 'invalid-value-01'); expect(defaultProps.onError).toHaveBeenCalledWith({ errorCode: 'pattern', details: 'Result of validate property', @@ -141,21 +83,9 @@ describe('StudioToggleableTextfieldSchema', () => { it('should validate field against json schema and invoke "onError" if field is required', async () => { const user = userEvent.setup(); - - renderStudioTextfieldSchema({ - viewProps: { - children: 'Edit id', - }, - inputProps: { - ...defaultProps.inputProps, - label: 'Your id', - }, - }); - await user.click(screen.getByRole('button', { name: 'Edit id' })); - - await user.type(screen.getByLabelText('Your id'), 'first-id'); - await user.clear(screen.getByLabelText('Your id')); - + renderStudioTextfieldSchema(); + await user.click(screen.getByRole('button', { name: value })); + await user.clear(screen.getByRole('textbox', { name: label })); expect(defaultProps.onError).toHaveBeenCalledWith({ errorCode: 'required', details: 'Property value is required', @@ -164,30 +94,15 @@ describe('StudioToggleableTextfieldSchema', () => { it('should invoke onChange and onError when input changes with error', async () => { const user = userEvent.setup(); - const onErrorMock = jest.fn(); - const onChangeMock = jest.fn(); - - renderStudioTextfieldSchema({ - onError: onErrorMock, - viewProps: { - children: 'Edit id', - }, - inputProps: { - ...defaultProps.inputProps, - label: 'Your id', - onChange: onChangeMock, - }, - }); - - await user.click(screen.getByRole('button', { name: 'Edit id' })); - - const invalidValue = '1'; - await user.type(screen.getByLabelText('Your id'), invalidValue); - expect(onErrorMock).toHaveBeenCalledWith({ - details: 'Result of validate property', + const invalidValue = 'invalid-value-01'; + renderStudioTextfieldSchema(); + await user.click(screen.getByRole('button', { name: value })); + await user.type(screen.getByRole('textbox', { name: label }), invalidValue); + expect(defaultProps.onError).toHaveBeenCalledWith({ errorCode: 'pattern', + details: 'Result of validate property', }); - expect(onChangeMock).toHaveBeenCalledTimes(1); + expect(defaultProps.onChange).toHaveBeenCalledTimes(invalidValue.length); }); }); diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.tsx index fe22cdae465..79b1ba5ac76 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.tsx +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.tsx @@ -25,10 +25,11 @@ export const StudioToggleableTextfieldSchema = forwardRef< >( ( { + error, layoutSchema, relatedSchemas, - inputProps, propertyPath, + onChange, onError, onIsViewMode, ...rest @@ -48,8 +49,8 @@ export const StudioToggleableTextfieldSchema = forwardRef< } if (propertyId) { - const error = jsonSchemaValidator.validateProperty(propertyId, newValue); - return error ? createSchemaError(error, 'Result of validate property') : null; + const schemaError = jsonSchemaValidator.validateProperty(propertyId, newValue); + return schemaError ? createSchemaError(schemaError, 'Result of validate property') : null; } return null; @@ -59,18 +60,15 @@ export const StudioToggleableTextfieldSchema = forwardRef< const validationError = validateAgainstSchema(event); onError?.(validationError || null); - inputProps.onChange?.(event); + onChange?.(event); }; return ( ) => handleOnChange(event), - error: inputProps.error, - }} + onChange={(event: React.ChangeEvent) => handleOnChange(event)} + error={error} onIsViewMode={onIsViewMode} /> ); diff --git a/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeListPage.test.tsx b/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeListPage.test.tsx index e02e6d3ced9..df654659b57 100644 --- a/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeListPage.test.tsx +++ b/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeListPage.test.tsx @@ -11,7 +11,7 @@ import { codeListsDataMock } from '../../../../../mocks/mockPagesConfig'; const onUpdateCodeListIdMock = jest.fn(); const onUpdateCodeListMock = jest.fn(); const onUploadCodeListMock = jest.fn(); -const codeListName = codeListsDataMock[0].title; +const codeListTitle = codeListsDataMock[0].title; const codeListMock: StudioComponentCodeList = [{ value: 'value', label: 'label' }]; const uploadedCodeListName = 'uploadedCodeListName'; @@ -51,18 +51,10 @@ describe('CodeListPage', () => { expect(codeListUploadButton).toBeInTheDocument(); }); - it('renders the code list as a clickable element', () => { - renderCodeListPage(); - const codeListAccordion = screen.getByRole('button', { name: codeListName }); - expect(codeListAccordion).toBeInTheDocument(); - }); - it('renders the code list accordion', () => { renderCodeListPage(); const codeListAccordion = screen.getByTitle( - textMock('app_content_library.code_lists.code_list_accordion_title', { - codeListTitle: codeListName, - }), + textMock('app_content_library.code_lists.code_list_accordion_title', { codeListTitle }), ); expect(codeListAccordion).toBeInTheDocument(); }); @@ -71,7 +63,7 @@ describe('CodeListPage', () => { const user = userEvent.setup(); const { rerender } = renderCodeListPage(); const codeListAccordionClosed = screen.getByRole('button', { - name: codeListName, + name: codeListTitle, expanded: false, }); expect(codeListAccordionClosed).toHaveAttribute('aria-expanded', 'false'); @@ -91,9 +83,9 @@ describe('CodeListPage', () => { it('calls onUpdateCodeListId when Id is changed', async () => { const user = userEvent.setup(); renderCodeListPage(); - await changeCodeListId(user, codeListName); + await changeCodeListId(user, codeListTitle); expect(onUpdateCodeListIdMock).toHaveBeenCalledTimes(1); - expect(onUpdateCodeListIdMock).toHaveBeenCalledWith(codeListName, codeListName + '2'); + expect(onUpdateCodeListIdMock).toHaveBeenCalledWith(codeListTitle, codeListTitle + '2'); }); it('calls onUpdateCodeList when code list is changed', async () => { @@ -104,7 +96,7 @@ describe('CodeListPage', () => { expect(onUpdateCodeListMock).toHaveBeenCalledTimes(1); expect(onUpdateCodeListMock).toHaveBeenLastCalledWith({ codeList: [{ ...codeListsDataMock[0].data[0], value: newValueText }], - title: codeListName, + title: codeListTitle, }); }); @@ -119,16 +111,14 @@ describe('CodeListPage', () => { const changeCodeListId = async (user: UserEvent, codeListNameToChange: string) => { const codeListIdToggleTextfield = screen.getByTitle( - textMock('app_content_library.code_lists.code_list_view_id_title', { - codeListName: codeListNameToChange, - }), - ); - await user.click(codeListIdToggleTextfield); - const codeListIdInput = screen.getByTitle( textMock('app_content_library.code_lists.code_list_edit_id_title', { codeListName: codeListNameToChange, }), ); + await user.click(codeListIdToggleTextfield); + const codeListIdInput = screen.getByRole('textbox', { + name: textMock('app_content_library.code_lists.code_list_edit_id_label'), + }); await user.type(codeListIdInput, '2'); await user.tab(); }; diff --git a/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/CodeLists.test.tsx b/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/CodeLists.test.tsx index 00136dc7cde..b4bda75c4dd 100644 --- a/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/CodeLists.test.tsx +++ b/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/CodeLists.test.tsx @@ -104,16 +104,14 @@ describe('CodeLists', () => { const changeCodeListId = async (user: UserEvent, oldCodeListId: string, newCodeListId: string) => { const codeListIdToggleTextfield = screen.getByTitle( - textMock('app_content_library.code_lists.code_list_view_id_title', { - codeListName: oldCodeListId, - }), - ); - await user.click(codeListIdToggleTextfield); - const codeListIdInput = screen.getByTitle( textMock('app_content_library.code_lists.code_list_edit_id_title', { codeListName: oldCodeListId, }), ); + await user.click(codeListIdToggleTextfield); + const codeListIdInput = screen.getByRole('textbox', { + name: textMock('app_content_library.code_lists.code_list_edit_id_label'), + }); await user.clear(codeListIdInput); await user.type(codeListIdInput, newCodeListId); await user.tab(); diff --git a/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/EditCodeList/EditCodeList.tsx b/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/EditCodeList/EditCodeList.tsx index adee15ed216..b36a1c00951 100644 --- a/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/EditCodeList/EditCodeList.tsx +++ b/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/EditCodeList/EditCodeList.tsx @@ -4,7 +4,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import type { CodeListWithMetadata } from '../../CodeListPage'; import { useCodeListEditorTexts } from '../../hooks/useCodeListEditorTexts'; -import { KeyVerticalIcon } from '@studio/icons'; import { updateCodeListWithMetadata } from '../CodeLists'; import { ArrayUtils, FileNameUtils } from '@studio/pure-functions'; import { useInputCodeListNameErrorMessage } from '../../hooks/useInputCodeListNameErrorMessage'; @@ -51,24 +50,12 @@ export function EditCodeList({
, - title: t('app_content_library.code_lists.code_list_edit_id_title', { - codeListName: codeListTitle, - }), - value: codeListTitle, - onBlur: (event) => handleUpdateCodeListId(event.target.value), - size: 'small', - }} - viewProps={{ - label: t('app_content_library.code_lists.code_list_edit_id_label'), - children: codeListTitle, - variant: 'tertiary', - title: t('app_content_library.code_lists.code_list_view_id_title', { - codeListName: codeListTitle, - }), - }} + label={t('app_content_library.code_lists.code_list_edit_id_label')} + onBlur={(event) => handleUpdateCodeListId(event.target.value)} + title={t('app_content_library.code_lists.code_list_edit_id_title', { + codeListName: codeListTitle, + })} + value={codeListTitle} />
diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/ConfigContent.test.tsx b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/ConfigContent.test.tsx index 9750b599a74..a9a17b552f3 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/ConfigContent.test.tsx +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/ConfigContent.test.tsx @@ -91,7 +91,7 @@ describe('ConfigContent', () => { expect( screen.getByRole('button', { - name: textMock('process_editor.configuration_panel_change_task_id'), + name: mockBpmnDetails.id, }), ).toBeInTheDocument(); }); diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetName/EditLayoutSetName.test.tsx b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetName/EditLayoutSetName.test.tsx index 222ac4bbfa4..4c97207eb28 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetName/EditLayoutSetName.test.tsx +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetName/EditLayoutSetName.test.tsx @@ -12,28 +12,18 @@ describe('EditLayoutSetName', () => { it('should render the layoutSetName button', () => { renderEditLayoutSetName(); const editLayoutSetName = screen.getByRole('button', { - name: textMock('process_editor.configuration_panel_layout_set_name_label'), + name: existingLayoutSetNameMock, }); expect(editLayoutSetName).toBeInTheDocument(); }); - it('should render the name of the layoutSetName textfield using the connected taskId', () => { - renderEditLayoutSetName(); - const layoutSetNameViewMode = screen.getByLabelText( - textMock('process_editor.configuration_panel_layout_set_name_label'), - ); - expect(layoutSetNameViewMode).toHaveTextContent( - textMock('process_editor.configuration_panel_layout_set_name') + existingLayoutSetNameMock, - ); - }); - it('should call mutateLayoutSet when changing name', async () => { const user = userEvent.setup(); const newLayoutSetName = 'newLayoutSetName'; const mutateLayoutSetIdMock = jest.fn(); renderEditLayoutSetName({ mutateLayoutSetId: mutateLayoutSetIdMock }); const editLayoutSetName = screen.getByRole('button', { - name: textMock('process_editor.configuration_panel_layout_set_name_label'), + name: existingLayoutSetNameMock, }); await user.click(editLayoutSetName); const inputNewLayoutSetName = screen.getByRole('textbox', { @@ -54,7 +44,7 @@ describe('EditLayoutSetName', () => { const mutateLayoutSetIdMock = jest.fn(); renderEditLayoutSetName({ mutateLayoutSetId: mutateLayoutSetIdMock }); const editLayoutSetName = screen.getByRole('button', { - name: textMock('process_editor.configuration_panel_layout_set_name_label'), + name: existingLayoutSetNameMock, }); await user.click(editLayoutSetName); const inputNewLayoutSetName = screen.getByRole('textbox', { diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetName/EditLayoutSetName.tsx b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetName/EditLayoutSetName.tsx index 0731f0ee424..aaf52dcc02b 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetName/EditLayoutSetName.tsx +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetName/EditLayoutSetName.tsx @@ -1,9 +1,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { StudioToggleableTextfield } from '@studio/components'; -import { KeyVerticalIcon } from '@studio/icons'; import { useBpmnApiContext } from '../../../../contexts/BpmnApiContext'; -import { Paragraph } from '@digdir/designsystemet-react'; import { useValidateLayoutSetName } from 'app-shared/hooks/useValidateLayoutSetName'; interface EditLayoutSetNameProps { @@ -27,23 +25,10 @@ export const EditLayoutSetName = ({ customValidation={(newLayoutSetName: string) => validateLayoutSetName(newLayoutSetName, layoutSets, existingLayoutSetName) } - inputProps={{ - icon: , - label: t('process_editor.configuration_panel_layout_set_name_label'), - value: existingLayoutSetName, - onBlur: (event) => handleOnLayoutSetNameBlur(event), - size: 'small', - }} - viewProps={{ - children: ( - - {t('process_editor.configuration_panel_layout_set_name')} - {existingLayoutSetName} - - ), - variant: 'tertiary', - 'aria-label': t('process_editor.configuration_panel_layout_set_name_label'), - }} + label={t('process_editor.configuration_panel_layout_set_name_label')} + onBlur={handleOnLayoutSetNameBlur} + title={t('process_editor.configuration_panel_layout_set_name_label')} + value={existingLayoutSetName} /> ); }; diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetNameRecommendedAction/RecommendedActionChangeName.tsx b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetNameRecommendedAction/RecommendedActionChangeName.tsx index aa2a37c720a..49397ca09b8 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetNameRecommendedAction/RecommendedActionChangeName.tsx +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetNameRecommendedAction/RecommendedActionChangeName.tsx @@ -45,8 +45,7 @@ export const RecommendedActionChangeName = (): React.ReactElement => { > } - size='sm' + Icon={KeyVerticalIcon} label={t('process_editor.recommended_action.new_name_label')} onChange={(event: React.ChangeEvent) => { setNewName(event.target.value); diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditTaskId/EditTaskId.test.tsx b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditTaskId/EditTaskId.test.tsx index fdab121c0d1..d95fb32ad34 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditTaskId/EditTaskId.test.tsx +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditTaskId/EditTaskId.test.tsx @@ -46,7 +46,7 @@ describe('EditTaskId', () => { expect( screen.getByRole('button', { - name: textMock('process_editor.configuration_panel_change_task_id'), + name: mockBpmnDetails.id, }), ).toBeInTheDocument(); }); @@ -56,12 +56,14 @@ describe('EditTaskId', () => { render(); const editButton = screen.getByRole('button', { - name: textMock('process_editor.configuration_panel_change_task_id'), + name: mockBpmnDetails.id, }); await user.click(editButton); expect( - screen.getByLabelText(textMock('process_editor.configuration_panel_change_task_id')), + screen.getByRole('textbox', { + name: textMock('process_editor.configuration_panel_change_task_id_label'), + }), ).toBeInTheDocument(); }); @@ -76,13 +78,13 @@ describe('EditTaskId', () => { render(); const editButton = screen.getByRole('button', { - name: textMock('process_editor.configuration_panel_change_task_id'), + name: mockBpmnDetails.id, }); await user.click(editButton); - const input = screen.getByLabelText( - textMock('process_editor.configuration_panel_change_task_id'), - ); + const input = screen.getByRole('textbox', { + name: textMock('process_editor.configuration_panel_change_task_id_label'), + }); await user.clear(input); await user.type(input, newId); @@ -146,13 +148,13 @@ describe('EditTaskId', () => { render(); const editButton = screen.getByRole('button', { - name: textMock('process_editor.configuration_panel_change_task_id'), + name: mockBpmnDetails.id, }); await user.click(editButton); - const input = screen.getByLabelText( - textMock('process_editor.configuration_panel_change_task_id'), - ); + const input = screen.getByRole('textbox', { + name: textMock('process_editor.configuration_panel_change_task_id_label'), + }); await user.clear(input); if (inputValue !== '') await user.type(input, inputValue); @@ -174,13 +176,13 @@ describe('EditTaskId', () => { render(); const editButton = screen.getByRole('button', { - name: textMock('process_editor.configuration_panel_change_task_id'), + name: mockBpmnDetails.id, }); await user.click(editButton); - const input = screen.getByLabelText( - textMock('process_editor.configuration_panel_change_task_id'), - ); + const input = screen.getByRole('textbox', { + name: textMock('process_editor.configuration_panel_change_task_id_label'), + }); await user.clear(input); await user.type(input, mockBpmnDetails.id); diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditTaskId/EditTaskId.tsx b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditTaskId/EditTaskId.tsx index 58a07ce2942..fcaedcc795e 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditTaskId/EditTaskId.tsx +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditTaskId/EditTaskId.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { StudioToggleableTextfield } from '@studio/components'; -import { KeyVerticalIcon } from '@studio/icons'; import { useBpmnContext } from '../../../../contexts/BpmnContext'; import { useBpmnConfigPanelFormContext } from '../../../../contexts/BpmnConfigPanelContext'; import type Modeling from 'bpmn-js/lib/features/modeling/Modeling'; @@ -45,22 +44,9 @@ export const EditTaskId = (): React.ReactElement => { return ( , - label: t('process_editor.configuration_panel_change_task_id'), - value: bpmnDetails.id, - onBlur: (event) => handleOnTaskIdBlur(event), - size: 'small', - }} - viewProps={{ - children: ( - - ID: {bpmnDetails.id} - - ), - variant: 'tertiary', - 'aria-label': t('process_editor.configuration_panel_change_task_id'), - }} + label={t('process_editor.configuration_panel_change_task_id_label')} + onBlur={handleOnTaskIdBlur} + value={bpmnDetails.id} /> ); }; diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceipt/CustomReceipt.module.css b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceipt/CustomReceipt.module.css index 8fcd79e026e..c4da8ec1012 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceipt/CustomReceipt.module.css +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceipt/CustomReceipt.module.css @@ -21,8 +21,3 @@ gap: var(--fds-spacing-2); padding-inline: var(--custom-receipt-spacing); } - -.textfield { - padding-inline: var(--custom-receipt-spacing); - padding-block: var(--fds-spacing-1); -} diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceipt/CustomReceipt.test.tsx b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceipt/CustomReceipt.test.tsx index 7dfe88f4107..05e8cec8fc5 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceipt/CustomReceipt.test.tsx +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceipt/CustomReceipt.test.tsx @@ -40,7 +40,7 @@ const mockAllDataModelIds: string[] = [ const defaultBpmnContextProps: BpmnApiContextProps = { ...mockBpmnApiContextValue, - existingCustomReceiptLayoutSetId: existingCustomReceiptLayoutSetId, + existingCustomReceiptLayoutSetId, allDataModelIds: mockAllDataModelIds, }; @@ -52,7 +52,7 @@ describe('CustomReceipt', () => { renderCustomReceipt(); const toggleableTextfieldButton = screen.getByRole('button', { - name: textMock('process_editor.configuration_panel_custom_receipt_textfield_label'), + name: existingCustomReceiptLayoutSetId, }); await user.click(toggleableTextfieldButton); @@ -77,7 +77,7 @@ describe('CustomReceipt', () => { renderCustomReceipt(); const toggleableTextfieldButton = screen.getByRole('button', { - name: textMock('process_editor.configuration_panel_custom_receipt_textfield_label'), + name: existingCustomReceiptLayoutSetId, }); await user.click(toggleableTextfieldButton); @@ -130,7 +130,7 @@ describe('CustomReceipt', () => { }); const toggleableTextfieldButton = screen.getByRole('button', { - name: textMock('process_editor.configuration_panel_custom_receipt_textfield_label'), + name: existingCustomReceiptLayoutSetId, }); await user.click(toggleableTextfieldButton); diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceipt/CustomReceipt.tsx b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceipt/CustomReceipt.tsx index 6064ce7155e..58b952aeae6 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceipt/CustomReceipt.tsx +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceipt/CustomReceipt.tsx @@ -1,14 +1,12 @@ import React from 'react'; import classes from './CustomReceipt.module.css'; import { StudioDeleteButton, StudioToggleableTextfield } from '@studio/components'; -import { KeyVerticalIcon } from '@studio/icons'; import { useBpmnApiContext } from '../../../../../contexts/BpmnApiContext'; import { getDataTypeFromLayoutSetsWithExistingId } from '../../../../../utils/configPanelUtils'; import { RedirectToCreatePageButton } from '../RedirectToCreatePageButton'; import { useTranslation } from 'react-i18next'; import { EditDataTypes } from '../../../ConfigContent/EditDataTypes'; import { PROTECTED_TASK_NAME_CUSTOM_RECEIPT } from 'app-shared/constants'; -import { Paragraph } from '@digdir/designsystemet-react'; import { useValidateLayoutSetName } from 'app-shared/hooks/useValidateLayoutSetName'; export const CustomReceipt = (): React.ReactElement => { @@ -49,26 +47,9 @@ export const CustomReceipt = (): React.ReactElement => { customValidation={(newLayoutSetName: string) => validateLayoutSetName(newLayoutSetName, layoutSets, existingCustomReceiptLayoutSetId) } - inputProps={{ - className: classes.textfield, - icon: , - label: t('process_editor.configuration_panel_custom_receipt_textfield_label'), - value: existingCustomReceiptLayoutSetId, - onBlur: handleEditLayoutSetId, - size: 'small', - }} - viewProps={{ - children: ( - - - {t('process_editor.configuration_panel_custom_receipt_layout_set_name')} - - {existingCustomReceiptLayoutSetId} - - ), - variant: 'tertiary', - 'aria-label': t('process_editor.configuration_panel_custom_receipt_textfield_label'), - }} + label={t('process_editor.configuration_panel_custom_receipt_textfield_label')} + onBlur={handleEditLayoutSetId} + value={existingCustomReceiptLayoutSetId} /> { }); it('shows the custom receipt when there is an existing custom receipt layout set id', () => { + const existingCustomReceiptLayoutSetId = 'existingCustomReceiptLayoutSetId'; renderCustomReceiptContent({ - existingCustomReceiptLayoutSetId: 'testId', + existingCustomReceiptLayoutSetId, }); const toggleableTextfieldButton = screen.getByRole('button', { - name: textMock('process_editor.configuration_panel_custom_receipt_textfield_label'), + name: existingCustomReceiptLayoutSetId, }); expect(toggleableTextfieldButton).toBeInTheDocument(); }); diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigPanel.test.tsx b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigPanel.test.tsx index 3385729a270..e869177e602 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigPanel.test.tsx +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigPanel.test.tsx @@ -43,7 +43,7 @@ describe('ConfigPanel', () => { bpmnDetails: { ...mockBpmnDetails, type: BpmnTypeEnum.Task }, }); const editTaskIdButton = screen.getByRole('button', { - name: textMock('process_editor.configuration_panel_change_task_id'), + name: mockBpmnDetails.id, }); expect(editTaskIdButton).toBeInTheDocument(); }); diff --git a/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/EditPageId.module.css b/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/EditPageId.module.css index eca00a24f2b..ae93910060b 100644 --- a/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/EditPageId.module.css +++ b/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/EditPageId.module.css @@ -1,7 +1,3 @@ .changePageId { background-color: var(--fds-semantic-surface-neutral-default); } - -.idInput { - padding: var(--fds-spacing-5); -} diff --git a/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/EditPageId.test.tsx b/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/EditPageId.test.tsx index 5438ed19694..f2fe20385bd 100644 --- a/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/EditPageId.test.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/EditPageId.test.tsx @@ -23,7 +23,7 @@ const layoutSetName = layoutSet1NameMock; describe('EditPageId', () => { it('renders given page ID', () => { renderEditPageId(); - screen.getByRole('button', { name: textMock('ux_editor.id_identifier') }); + screen.getByRole('button', { name: selectedLayout }); }); it('calls updateFormLayoutName and textIdMutation with new page ID when changed', async () => { @@ -36,11 +36,11 @@ describe('EditPageId', () => { updateFormLayoutName, }; renderEditPageId(mockQueries); - const pageIdButton = screen.getByRole('button', { name: textMock('ux_editor.id_identifier') }); + const pageIdButton = screen.getByRole('button', { name: selectedLayout }); await user.click(pageIdButton); - const editPageId = screen.getByLabelText( - textMock('ux_editor.modal_properties_textResourceBindings_page_id'), - ); + const editPageId = screen.getByRole('textbox', { + name: textMock('ux_editor.modal_properties_textResourceBindings_page_id'), + }); await user.clear(editPageId); await user.type(editPageId, newPageName); await user.tab(); @@ -66,11 +66,11 @@ describe('EditPageId', () => { updateFormLayoutName, }; renderEditPageId(mockQueries); - const pageIdButton = screen.getByRole('button', { name: textMock('ux_editor.id_identifier') }); + const pageIdButton = screen.getByRole('button', { name: selectedLayout }); await user.click(pageIdButton); - const editPageId = screen.getByLabelText( - textMock('ux_editor.modal_properties_textResourceBindings_page_id'), - ); + const editPageId = screen.getByRole('textbox', { + name: textMock('ux_editor.modal_properties_textResourceBindings_page_id'), + }); await user.click(editPageId); await user.tab(); expect(updateFormLayoutName).not.toHaveBeenCalled(); @@ -83,7 +83,7 @@ describe('EditPageId', () => { renderEditPageId(); const notUniqueErrorMessage = screen.queryByText(textMock('ux_editor.pages_error_unique')); expect(notUniqueErrorMessage).not.toBeInTheDocument(); - const pageIdButton = screen.getByRole('button', { name: textMock('ux_editor.id_identifier') }); + const pageIdButton = screen.getByRole('button', { name: selectedLayout }); await user.click(pageIdButton); const editPageId = screen.getByRole('textbox', { name: textMock('ux_editor.modal_properties_textResourceBindings_page_id'), diff --git a/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/EditPageId.tsx b/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/EditPageId.tsx index 551a9f50f72..e900a4172b2 100644 --- a/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/EditPageId.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/EditPageId.tsx @@ -1,6 +1,5 @@ import React from 'react'; import classes from './EditPageId.module.css'; -import { KeyVerticalIcon } from '@studio/icons'; import { getPageNameErrorKey } from '../../../utils/designViewUtils'; import { useUpdateLayoutNameMutation } from '../../../hooks/mutations/useUpdateLayoutNameMutation'; import { StudioToggleableTextfield } from '@studio/components'; @@ -8,7 +7,6 @@ import { useTextIdMutation } from 'app-development/hooks/mutations'; import { useStudioEnvironmentParams } from 'app-shared/hooks/useStudioEnvironmentParams'; import { useAppContext, useText } from '../../../hooks'; import { useFormLayoutSettingsQuery } from '../../../hooks/queries/useFormLayoutSettingsQuery'; -import { Trans } from 'react-i18next'; export interface EditPageIdProps { layoutName: string; @@ -47,23 +45,14 @@ export const EditPageId = ({ layoutName }: EditPageIdProps) => { return (
, - variant: 'tertiary', - fullWidth: true, - }} - inputProps={{ - icon: , - value: layoutName, - onBlur: (event) => handleSaveNewName(event.target.value), - label: t('ux_editor.modal_properties_textResourceBindings_page_id'), - size: 'small', - className: classes.idInput, - }} customValidation={(value: string) => { const validationResult = getPageNameErrorKey(value, layoutName, layoutOrder); return validationResult ? t(validationResult) : undefined; }} + label={t('ux_editor.modal_properties_textResourceBindings_page_id')} + onBlur={(event) => handleSaveNewName(event.target.value)} + title={t('ux_editor.modal_properties_textResourceBindings_page_id')} + value={layoutName} />
); diff --git a/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/PageConfigPanel.test.tsx b/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/PageConfigPanel.test.tsx index 8bbe3c3dc6d..23471edc68f 100644 --- a/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/PageConfigPanel.test.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/PageConfigPanel.test.tsx @@ -79,7 +79,7 @@ describe('PageConfigPanel', () => { }); expect(screen.queryByRole('heading', { name: newSelectedPage })).not.toBeInTheDocument(); screen.getByRole('heading', { name: newVisualPageName }); - screen.getByRole('button', { name: textMock('ux_editor.id_identifier') }); + screen.getByRole('button', { name: newSelectedPage }); }); it('render warning when layout is selected and has duplicated ids', () => { diff --git a/frontend/packages/ux-editor/src/components/Properties/Properties.test.tsx b/frontend/packages/ux-editor/src/components/Properties/Properties.test.tsx index b86d7839142..10938dd9cc7 100644 --- a/frontend/packages/ux-editor/src/components/Properties/Properties.test.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/Properties.test.tsx @@ -51,9 +51,7 @@ jest.mock('../config/Expressions', () => ({ })); describe('Properties', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); + beforeEach(jest.clearAllMocks); describe('Page config', () => { it('shows page config when formItem is undefined', () => { @@ -83,7 +81,7 @@ describe('Properties', () => { }); expect(heading).toBeInTheDocument(); const editComponentIdButton = screen.getByRole('button', { - name: textMock('ux_editor.id_identifier'), + name: componentMocks[ComponentType.Input].id, }); expect(editComponentIdButton).toBeInTheDocument(); await user.click(editComponentIdButton); @@ -100,7 +98,9 @@ describe('Properties', () => { it('should not invoke handleUpdate when the id is invalid', async () => { const user = userEvent.setup(); renderProperties(); - await user.click(screen.getByRole('button', { name: textMock('ux_editor.id_identifier') })); + await user.click( + screen.getByRole('button', { name: componentMocks[ComponentType.Input].id }), + ); const invalidId = 'invalidId-01'; await user.type( diff --git a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.test.tsx b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.test.tsx index 7d4ce6659e5..371b3cd49a8 100644 --- a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.test.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.test.tsx @@ -11,43 +11,26 @@ import { QueryKey } from 'app-shared/types/QueryKey'; import { layout1NameMock, layoutMock } from '@altinn/ux-editor/testing/layoutMock'; import { layoutSet1NameMock } from '@altinn/ux-editor/testing/layoutSetsMock'; import { app, org } from '@studio/testing/testids'; +import { componentMocks } from '@altinn/ux-editor/testing/componentMocks'; const layoutSetName = layoutSet1NameMock; const layouts: IFormLayouts = { [layout1NameMock]: layoutMock, }; -const studioRender = async (props: Partial = {}) => { - queryClientMock.setQueryData([QueryKey.FormLayouts, org, app, layoutSetName], layouts); - return renderWithProviders( - , - ); -}; - describe('EditComponentIdRow', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); + beforeEach(jest.clearAllMocks); it('should render button ', async () => { - await studioRender(); - const testIdButton = screen.getByRole('button', { name: textMock('ux_editor.id_identifier') }); + await renderEditComponentIdRow(); + const testIdButton = screen.getByRole('button', { name: componentId }); expect(testIdButton).toBeInTheDocument(); }); it('should render textField when the button is clicked', async () => { const user = userEvent.setup(); - await studioRender(); - const testIdButton = screen.getByRole('button', { name: textMock('ux_editor.id_identifier') }); + await renderEditComponentIdRow(); + const testIdButton = screen.getByRole('button', { name: componentId }); await user.click(testIdButton); const textField = screen.getByRole('textbox', { name: textMock('ux_editor.modal_properties_component_change_id'), @@ -57,8 +40,8 @@ describe('EditComponentIdRow', () => { it('should not render the textfield when changing from edit mode to view mode ', async () => { const user = userEvent.setup(); - await studioRender(); - const testIdButton = screen.getByRole('button', { name: textMock('ux_editor.id_identifier') }); + await renderEditComponentIdRow(); + const testIdButton = screen.getByRole('button', { name: componentId }); await user.click(testIdButton); const textField = screen.getByRole('textbox', { name: textMock('ux_editor.modal_properties_component_change_id'), @@ -69,22 +52,27 @@ describe('EditComponentIdRow', () => { it('should call onChange when user change the input in text filed.', async () => { const user = userEvent.setup(); - const handleComponentUpdate = jest.fn(); - await studioRender({ handleComponentUpdate }); - const testIdButton = screen.getByRole('button', { name: textMock('ux_editor.id_identifier') }); + await renderEditComponentIdRow(); + const testIdButton = screen.getByRole('button', { name: componentId }); await user.click(testIdButton); const textField = screen.getByRole('textbox', { name: textMock('ux_editor.modal_properties_component_change_id'), }); - await user.type(textField, 'newTestId'); + const newTestId = 'newTestId'; + await user.clear(textField); + await user.type(textField, newTestId); await user.click(document.body); - expect(handleComponentUpdate).toHaveBeenCalled(); + expect(handleComponentUpdate).toHaveBeenCalledTimes(1); + expect(handleComponentUpdate).toHaveBeenCalledWith({ + ...componentMocks[ComponentType.Input], + id: newTestId, + }); }); it('should show error required error message when id is empty', async () => { const user = userEvent.setup(); - await studioRender(); - const testIdButton = screen.getByRole('button', { name: textMock('ux_editor.id_identifier') }); + await renderEditComponentIdRow(); + const testIdButton = screen.getByRole('button', { name: componentId }); await user.click(testIdButton); const textField = screen.getByRole('textbox', { name: textMock('ux_editor.modal_properties_component_change_id'), @@ -95,8 +83,8 @@ describe('EditComponentIdRow', () => { it('should show error message when id is not unique', async () => { const user = userEvent.setup(); - await studioRender(); - const testIdButton = screen.getByRole('button', { name: textMock('ux_editor.id_identifier') }); + await renderEditComponentIdRow(); + const testIdButton = screen.getByRole('button', { name: componentId }); await user.click(testIdButton); const textField = screen.getByRole('textbox', { name: textMock('ux_editor.modal_properties_component_change_id'), @@ -110,32 +98,39 @@ describe('EditComponentIdRow', () => { it('should show error message when id of an attachment component type has duplicate id', async () => { const user = userEvent.setup(); + const idOccupiedByDataType = 'idOccupiedByDataType'; queryClientMock.setQueryData([QueryKey.AppMetadata, org, app], { - dataTypes: [{ id: 'newTestId' }], + dataTypes: [{ id: idOccupiedByDataType }], }); - await studioRender({ - component: { - type: ComponentType.FileUpload, - id: '', - itemType: 'COMPONENT', - description: 'test', - displayMode: 'test', - hasCustomFileEndings: false, - maxFileSizeInMB: 100, - maxNumberOfAttachments: 2, - minNumberOfAttachments: 1, - }, + await renderEditComponentIdRow({ + component: componentMocks[ComponentType.FileUpload], + }); + const testIdButton = screen.getByRole('button', { + name: componentMocks[ComponentType.FileUpload].id, }); - const testIdButton = screen.getByRole('button', { name: textMock('ux_editor.id_identifier') }); await user.click(testIdButton); const textField = screen.getByRole('textbox', { name: textMock('ux_editor.modal_properties_component_change_id'), }); await user.clear(textField); - await user.type(textField, 'newTestId'); + await user.type(textField, idOccupiedByDataType); await user.click(document.body); expect( screen.getByText(textMock('ux_editor.error_component_id_exists_as_data_type')), ).toBeInTheDocument(); }); }); + +const componentId = componentMocks[ComponentType.Input].id; +const handleComponentUpdate = jest.fn(); +const helpText = 'helpText'; +const defaultProps: EditComponentIdRowProps = { + component: componentMocks[ComponentType.Input], + handleComponentUpdate, + helpText, +}; + +const renderEditComponentIdRow = async (props: Partial = {}) => { + queryClientMock.setQueryData([QueryKey.FormLayouts, org, app, layoutSetName], layouts); + return renderWithProviders(); +}; diff --git a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.tsx b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.tsx index 110efc4fe10..286e509a554 100644 --- a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.tsx @@ -1,10 +1,9 @@ import React, { useState } from 'react'; import { StudioToggleableTextfieldSchema, type SchemaValidationError } from '@studio/components'; import { Alert } from '@digdir/designsystemet-react'; -import { KeyVerticalIcon } from '@studio/icons'; import classes from './EditComponentIdRow.module.css'; import { idExists } from '../../../../utils/formLayoutsUtils'; -import { Trans, useTranslation } from 'react-i18next'; +import { useTranslation } from 'react-i18next'; import type { FormItem } from '../../../../types/FormItem'; import { useLayoutSchemaQuery } from '../../../../hooks/queries/useLayoutSchemaQuery'; import { useFormLayouts } from '../../../../hooks'; @@ -85,29 +84,20 @@ export const EditComponentIdRow = ({ return (
, - title: component.id, - variant: 'tertiary', - fullWidth: true, - }} - inputProps={{ - icon: , - value: idInputValue, - onBlur: (event) => saveComponentUpdate(event.target.value), - label: t('ux_editor.modal_properties_component_change_id'), - size: 'small', - error: errorMessage, - }} customValidation={(value) => { return validateId(value); }} + error={errorMessage} + key={component.id} + label={t('ux_editor.modal_properties_component_change_id')} + layoutSchema={layoutSchema} + onBlur={(event) => saveComponentUpdate(event.target.value)} + onError={handleValidationError} onIsViewMode={setIsViewMode} + propertyPath='definitions/component/properties/id' + relatedSchemas={[expressionSchema, numberFormatSchema]} + title={component.id} + value={component.id} /> {!isViewMode && (
diff --git a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/PropertiesHeader.test.tsx b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/PropertiesHeader.test.tsx index de05ced479d..74674655c7a 100644 --- a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/PropertiesHeader.test.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/PropertiesHeader.test.tsx @@ -65,7 +65,7 @@ describe('PropertiesHeader', () => { renderPropertiesHeader(); const editComponentIdButton = screen.getByRole('button', { - name: textMock('ux_editor.id_identifier'), + name: component1Mock.id, }); await user.click(editComponentIdButton); @@ -83,7 +83,7 @@ describe('PropertiesHeader', () => { renderPropertiesHeader(); const editComponentIdButton = screen.getByRole('button', { - name: textMock('ux_editor.id_identifier'), + name: component1Mock.id, }); await user.click(editComponentIdButton); diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/EditImage.test.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/EditImage.test.tsx index c628bcb4fb3..eba48c3105f 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/EditImage.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/EditImage.test.tsx @@ -6,6 +6,7 @@ import { ComponentType } from 'app-shared/types/ComponentType'; import { componentMocks } from '../../../../testing/componentMocks'; import { textMock } from '@studio/testing/mocks/i18nMock'; import { renderWithProviders } from '../../../../testing/mocks'; +import type { UserEvent } from '@testing-library/user-event'; import userEvent from '@testing-library/user-event'; import type { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; import { queriesMock } from 'app-shared/mocks/queriesMock'; @@ -186,7 +187,7 @@ const getTabs = (): { addImageTab: HTMLElement; pasteUrlTab: HTMLElement } => { }; }; -const goToExternalUrlTab = async (user) => { +const goToExternalUrlTab = async (user: UserEvent) => { await user.click( screen.getByRole('tab', { name: textMock('ux_editor.properties_panel.images.enter_external_url_tab_title'), @@ -194,15 +195,14 @@ const goToExternalUrlTab = async (user) => { ); }; -const clickExistingUrlButton = async (user, existingExternalUrl: string) => { +const clickExistingUrlButton = async (user: UserEvent, existingExternalUrl: string) => { const existingUrlButton = screen.getByRole('button', { - name: - textMock('ux_editor.properties_panel.images.enter_external_url') + ' ' + existingExternalUrl, + name: existingExternalUrl, }); await user.click(existingUrlButton); }; -const enterUrlInField = async (user, url: string | undefined) => { +const enterUrlInField = async (user: UserEvent, url: string | undefined) => { const enterUrlField = screen.getByRole('textbox', { name: textMock('ux_editor.properties_panel.images.enter_external_url'), }); diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.module.css b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.module.css index 5fa067d03b6..4a0b85b0d44 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.module.css +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.module.css @@ -1,7 +1,3 @@ -.missingUrl { - font-style: italic; -} - .alertContainer { padding: 0 var(--fds-spacing-3); } diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.test.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.test.tsx index 212282cf99a..c6ee29f68cc 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.test.tsx @@ -9,6 +9,7 @@ import { QueryKey } from 'app-shared/types/QueryKey'; import { app, org } from '@studio/testing/testids'; import type { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; import { queriesMock } from 'app-shared/mocks/queriesMock'; +import type { UserEvent } from '@testing-library/user-event'; import userEvent from '@testing-library/user-event'; import type { ExternalImageUrlValidationResponse } from 'app-shared/types/api/ExternalImageUrlValidationResponse'; @@ -223,23 +224,17 @@ const getInvalidUrlErrorMessage = () => const getNotAnImageErrorMessage = () => screen.getByText(textMock('ux_editor.properties_panel.images.invalid_external_url_not_an_image')); -const getExistingUrlButton = (url: string) => - screen.getByRole('button', { - name: textMock('ux_editor.properties_panel.images.enter_external_url') + ' ' + url, - }); +const getExistingUrlButton = (url: string) => screen.getByRole('button', { name: url }); const getEnterUrlWithPlaceholderButton = () => screen.getByRole('button', { - name: - textMock('ux_editor.properties_panel.images.enter_external_url') + - ' ' + - textMock('ux_editor.properties_panel.images.external_url_not_added'), + name: textMock('ux_editor.properties_panel.images.external_url_not_added'), }); -const inputUrlInField = async (user, url: string) => { +const inputUrlInField = async (user: UserEvent, url: string) => { const inputUrlField = getInputUrlField(); + await user.clear(inputUrlField); if (url) await user.type(inputUrlField, url); - else await user.clear(inputUrlField); await waitFor(() => inputUrlField.blur()); }; diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx index 1e413c8f286..bab2fd9b87b 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx @@ -1,7 +1,6 @@ -import type { ChangeEvent } from 'react'; import React, { useEffect, useState } from 'react'; import { LinkIcon } from '@studio/icons'; -import { StudioToggleableTextfield } from '@studio/components'; +import { StudioIconTextfield, StudioToggleableTextfield } from '@studio/components'; import { useTranslation } from 'react-i18next'; import classes from './ExternalImage.module.css'; import { useValidateImageExternalUrlQuery } from 'app-shared/hooks/queries/useValidateImageExternalUrlQuery'; @@ -22,7 +21,6 @@ export const ExternalImage = ({ onUrlDelete, imageOriginsFromLibrary, }: ExternalImageProps) => { - const { t } = useTranslation(); const { org, app } = useStudioEnvironmentParams(); const [url, setUrl] = useState(existingImageUrl); const { data: validationResult, status: validationStatus } = useValidateImageExternalUrlQuery( @@ -38,39 +36,18 @@ export const ExternalImage = ({ }, [validationResult, validationStatus, onUrlChange, url, existingImageUrl]); const handleBlur = async (newUrl: string) => { - if (isBLurInitialWithEmptyInput(url, newUrl)) return; - if (newUrl === '') { + if (newUrl === '' && !isBLurInitialWithEmptyInput(url, newUrl)) { onUrlDelete(); - setUrl(undefined); - return; } setUrl(newUrl); }; return ( <> - - {t('ux_editor.properties_panel.images.external_url_not_added')} - - ), - label: t('ux_editor.properties_panel.images.enter_external_url'), - title: url, - variant: 'tertiary', - fullWidth: true, - icon: , - }} - inputProps={{ - icon: , - value: existingImageUrl, - onBlur: ({ target }: ChangeEvent) => handleBlur(target.value), - label: t('ux_editor.properties_panel.images.enter_external_url'), - size: 'small', - }} - setViewModeByDefault={!!existingImageUrl} - autoFocus={false} + handleBlur(event.target.value)} /> {!!url && ( ) => void; +}; + +const EditUrl = ({ url, existingImageUrl, onBlur }: EditUrlProps): React.ReactElement => { + const { t } = useTranslation(); + const [isViewMode, setIsViewMode] = useState(false); + + const noUrlProvided = url === undefined && !existingImageUrl; + const label = t('ux_editor.properties_panel.images.enter_external_url'); + const noUrlText = t('ux_editor.properties_panel.images.external_url_not_added'); + const currentUrl = !url ? noUrlText : url; + const showValue = currentUrl !== noUrlText || isViewMode; + const value = showValue ? currentUrl : undefined; + + return noUrlProvided ? ( + + ) : ( + + ); +}; + const isBLurInitialWithEmptyInput = (existingUrl: string, newUrl: string) => newUrl === '' && existingUrl === undefined; diff --git a/frontend/packages/ux-editor/src/containers/DesignView/AddItem/ItemInfo/ItemInfo.tsx b/frontend/packages/ux-editor/src/containers/DesignView/AddItem/ItemInfo/ItemInfo.tsx index f8a333fc05f..42f8372ae90 100644 --- a/frontend/packages/ux-editor/src/containers/DesignView/AddItem/ItemInfo/ItemInfo.tsx +++ b/frontend/packages/ux-editor/src/containers/DesignView/AddItem/ItemInfo/ItemInfo.tsx @@ -55,7 +55,7 @@ export const ItemInfo = ({ item, onAddItem, onCancel, setItem }: ItemInfoProps) description={t('ux_editor.add_item.component_info_generated_id_description')} > } + Icon={PencilIcon} label={t('Komponent ID')} value={item.componentId} onChange={(event: any) => { diff --git a/frontend/testing/playwright/pages/ProcessEditorPage/ProcessEditorPage.ts b/frontend/testing/playwright/pages/ProcessEditorPage/ProcessEditorPage.ts index 10caad4cbf7..f2a4318c9a2 100644 --- a/frontend/testing/playwright/pages/ProcessEditorPage/ProcessEditorPage.ts +++ b/frontend/testing/playwright/pages/ProcessEditorPage/ProcessEditorPage.ts @@ -86,7 +86,7 @@ export class ProcessEditorPage extends BasePage { public async waitForEditIdInputFieldToBeVisible(): Promise { const inputField = this.page.getByRole('textbox', { - name: this.textMock('process_editor.configuration_panel_change_task_id'), + name: this.textMock('process_editor.configuration_panel_change_task_id_label'), }); await expect(inputField).toBeVisible(); } @@ -94,7 +94,7 @@ export class ProcessEditorPage extends BasePage { public async emptyIdTextfield(): Promise { await this.page .getByRole('textbox', { - name: this.textMock('process_editor.configuration_panel_change_task_id'), + name: this.textMock('process_editor.configuration_panel_change_task_id_label'), }) .clear(); } @@ -102,14 +102,14 @@ export class ProcessEditorPage extends BasePage { public async writeNewId(id: string): Promise { await this.page .getByRole('textbox', { - name: this.textMock('process_editor.configuration_panel_change_task_id'), + name: this.textMock('process_editor.configuration_panel_change_task_id_label'), }) .fill(id); } public async waitForTextBoxToHaveValue(id: string): Promise { const textBox = this.page.getByRole('textbox', { - name: this.textMock('process_editor.configuration_panel_change_task_id'), + name: this.textMock('process_editor.configuration_panel_change_task_id_label'), }); await expect(textBox).toHaveValue(id); } @@ -117,7 +117,7 @@ export class ProcessEditorPage extends BasePage { public async saveNewId(): Promise { await this.page .getByRole('textbox', { - name: this.textMock('process_editor.configuration_panel_change_task_id'), + name: this.textMock('process_editor.configuration_panel_change_task_id_label'), }) .blur(); } From 58beda39c6c6c2b591b1d4c974a5f98fafa3e4b7 Mon Sep 17 00:00:00 2001 From: andreastanderen Date: Mon, 13 Jan 2025 10:12:46 +0100 Subject: [PATCH 2/8] fix playwright tests --- .../ProcessEditorPage/CustomReceiptConfig.ts | 4 ++-- .../ProcessEditorPage/ProcessEditorPage.ts | 21 +++++++------------ .../testing/playwright/pages/UiEditorPage.ts | 5 +++-- .../process-editor/process-editor.spec.ts | 4 +++- .../tests/text-editor/text-editor.spec.ts | 2 +- 5 files changed, 17 insertions(+), 19 deletions(-) diff --git a/frontend/testing/playwright/pages/ProcessEditorPage/CustomReceiptConfig.ts b/frontend/testing/playwright/pages/ProcessEditorPage/CustomReceiptConfig.ts index 81bb80b41b1..2580ca8361c 100644 --- a/frontend/testing/playwright/pages/ProcessEditorPage/CustomReceiptConfig.ts +++ b/frontend/testing/playwright/pages/ProcessEditorPage/CustomReceiptConfig.ts @@ -55,9 +55,9 @@ export class CustomReceiptConfig extends BasePage { .click(); } - public async waitForEditLayoutSetIdButtonToBeVisible(): Promise { + public async waitForEditLayoutSetIdButtonToBeVisible(layoutSetId: string): Promise { const button = this.page.getByRole('button', { - name: this.textMock('process_editor.configuration_panel_custom_receipt_textfield_label'), + name: layoutSetId, }); await expect(button).toBeVisible(); } diff --git a/frontend/testing/playwright/pages/ProcessEditorPage/ProcessEditorPage.ts b/frontend/testing/playwright/pages/ProcessEditorPage/ProcessEditorPage.ts index f2a4318c9a2..85215226d68 100644 --- a/frontend/testing/playwright/pages/ProcessEditorPage/ProcessEditorPage.ts +++ b/frontend/testing/playwright/pages/ProcessEditorPage/ProcessEditorPage.ts @@ -1,5 +1,5 @@ import { expect } from '@playwright/test'; -import type { Page } from '@playwright/test'; +import type { Page, Locator } from '@playwright/test'; import { ActionsConfig } from './ActionsConfig'; import { PolicyConfig } from './PolicyConfig'; import { BasePage } from '../../helpers/BasePage'; @@ -73,15 +73,13 @@ export class ProcessEditorPage extends BasePage { } public async getTaskIdFromOpenNewlyAddedTask(): Promise { - const selector = 'text=ID: Activity_'; - await this.page.waitForSelector(selector); - return await this.getFullIdFromButtonSelector(selector); + const button = this.page.getByRole('button', { name: /^Activity_/ }); + await button.waitFor(); + return await this.getFullIdFromButtonSelector(button); } public async clickOnTaskIdEditButton(id: string): Promise { - await this.page - .getByText(`${this.textMock('process_editor.configuration_panel_id_label')} ${id}`) - .click(); + await this.page.getByRole('button', { name: id }).click(); } public async waitForEditIdInputFieldToBeVisible(): Promise { @@ -123,9 +121,7 @@ export class ProcessEditorPage extends BasePage { } public async waitForNewTaskIdButtonToBeVisible(id: string): Promise { - const button = this.page.getByText( - `${this.textMock('process_editor.configuration_panel_id_label')} ${id}`, - ); + const button = this.page.getByRole('button', { name: id }); await expect(button).toBeVisible(); } @@ -165,10 +161,9 @@ export class ProcessEditorPage extends BasePage { await this.page.mouse.up(); } - private async getFullIdFromButtonSelector(selector: string): Promise { - const button = this.page.locator(selector); + private async getFullIdFromButtonSelector(button: Locator): Promise { const fullText = await button.textContent(); - const extractedText = fullText.match(/ID: (Activity_\w+)/); + const extractedText = fullText.match(/(Activity_\w+)/); return extractedText[1]; } } diff --git a/frontend/testing/playwright/pages/UiEditorPage.ts b/frontend/testing/playwright/pages/UiEditorPage.ts index eb890f44e32..de777a1208e 100644 --- a/frontend/testing/playwright/pages/UiEditorPage.ts +++ b/frontend/testing/playwright/pages/UiEditorPage.ts @@ -231,8 +231,9 @@ export class UiEditorPage extends BasePage { await expect(newTreeItemLabel).toBeVisible(); } - public async deleteOldComponentId(): Promise { - await this.page.getByRole('button', { name: /ID:/ }).click(); + public async deleteOldComponentId(componentId: string): Promise { + const button = this.page.getByRole('button', { name: componentId }); + await button.click(); await this.page .getByLabel(this.textMock('ux_editor.modal_properties_component_change_id')) .clear(); diff --git a/frontend/testing/playwright/tests/process-editor/process-editor.spec.ts b/frontend/testing/playwright/tests/process-editor/process-editor.spec.ts index ce653356886..614b75e4656 100644 --- a/frontend/testing/playwright/tests/process-editor/process-editor.spec.ts +++ b/frontend/testing/playwright/tests/process-editor/process-editor.spec.ts @@ -184,7 +184,9 @@ test('That it is possible to create a custom receipt', async ({ page, testAppNam await processEditorPage.customReceiptConfig.waitForSaveNewCustomReceiptButtonToBeVisible(); await processEditorPage.customReceiptConfig.clickOnSaveNewCustomReceiptButton(); - await processEditorPage.customReceiptConfig.waitForEditLayoutSetIdButtonToBeVisible(); + await processEditorPage.customReceiptConfig.waitForEditLayoutSetIdButtonToBeVisible( + newLayoutSetId, + ); // --------------------- Check that files are uploaded to Gitea --------------------- await goToGiteaAndNavigateToApplicationMetadataFile(header, giteaPage); diff --git a/frontend/testing/playwright/tests/text-editor/text-editor.spec.ts b/frontend/testing/playwright/tests/text-editor/text-editor.spec.ts index ad7b2c586e2..45f375b1a02 100644 --- a/frontend/testing/playwright/tests/text-editor/text-editor.spec.ts +++ b/frontend/testing/playwright/tests/text-editor/text-editor.spec.ts @@ -59,7 +59,7 @@ test('That it is possible to create a text at the ui-editor page, and that the t await navigateToUiEditorAndVerifyPage(header, uiEditorPage); await uiEditorPage.dragComponentIntoDroppableList(ComponentType.Input); - await uiEditorPage.deleteOldComponentId(); + await uiEditorPage.deleteOldComponentId(COMPONENT_ID); await uiEditorPage.writeNewComponentId(COMPONENT_ID); await uiEditorPage.waitForXAmountOfMilliseconds(1000); // Wait for the API call to be done From 07a4e01c460afdb7e4f05dc63b3283a3bc3e41a2 Mon Sep 17 00:00:00 2001 From: andreastanderen Date: Wed, 15 Jan 2025 14:24:19 +0100 Subject: [PATCH 3/8] fix PR comments --- .../StudioIconTextfield.module.css | 2 +- .../StudioIconTextfield.stories.tsx | 1 + .../StudioIconTextfield.tsx | 5 ++-- .../StudioTextfieldToggleView.module.css | 1 - .../StudioTextfieldToggleView.tsx | 2 +- .../StudioToggleableTextfield.tsx | 15 +++++------ .../EditImage/ExternalImage/ExternalImage.tsx | 27 +++++++++++++------ .../testing/playwright/pages/UiEditorPage.ts | 4 +-- .../tests/text-editor/text-editor.spec.ts | 2 +- 9 files changed, 35 insertions(+), 24 deletions(-) diff --git a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.module.css b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.module.css index 62bea004556..dc2df805a0f 100644 --- a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.module.css +++ b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.module.css @@ -2,7 +2,7 @@ display: flex; gap: var(--fds-spacing-2); align-items: flex-start; - padding: var(--fds-spacing-2) var(--fds-spacing-2); + padding: var(--fds-spacing-2); } .prefixIcon { diff --git a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.stories.tsx b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.stories.tsx index c3a3c9f958d..298a1a9746a 100644 --- a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.stories.tsx +++ b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.stories.tsx @@ -20,6 +20,7 @@ export const Preview: Story = (args) => >; + label: string; } & StudioTextfieldProps; export const StudioIconTextfield = forwardRef( ( - { Icon, className: givenClassName, ...rest }: StudioIconTextfieldProps, + { Icon, label, className: givenClassName, ...rest }: StudioIconTextfieldProps, ref, ): React.ReactElement => { const className = cn(givenClassName, classes.container); return (
- +
); }, diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.module.css b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.module.css index 2fbf0bbd7e6..2f3b475ffe0 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.module.css +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.module.css @@ -11,7 +11,6 @@ grid-template-columns: auto 1fr; text-align: left; align-items: center; - justify-content: space-between; color: var(--fds-semantic-text-neutral-default); width: 100%; gap: var(--fds-spacing-1); diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.tsx index 58825ad6a52..651ff197ee0 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.tsx +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.tsx @@ -6,7 +6,7 @@ import cn from 'classnames'; export type StudioTextfieldToggleViewProps = Omit & { Icon?: React.ComponentType>; - label?: string; + label: string; }; export const StudioTextfieldToggleView = ({ diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.tsx index 9493c2f33fd..88a426bc9ac 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.tsx +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.tsx @@ -1,6 +1,5 @@ import React, { forwardRef, useEffect, useState } from 'react'; import { StudioTextfieldToggleView } from './StudioTextfieldToggleView'; - import { StudioIconTextfield } from '../StudioIconTextfield'; import { KeyVerticalIcon } from '../../../../studio-icons'; @@ -74,9 +73,9 @@ export const StudioToggleableTextfield = forwardRef @@ -84,15 +83,15 @@ export const StudioToggleableTextfield = forwardRef ); }, diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx index bab2fd9b87b..936134cc5e9 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx @@ -74,17 +74,12 @@ type EditUrlProps = { const EditUrl = ({ url, existingImageUrl, onBlur }: EditUrlProps): React.ReactElement => { const { t } = useTranslation(); const [isViewMode, setIsViewMode] = useState(false); - - const noUrlProvided = url === undefined && !existingImageUrl; const label = t('ux_editor.properties_panel.images.enter_external_url'); const noUrlText = t('ux_editor.properties_panel.images.external_url_not_added'); - const currentUrl = !url ? noUrlText : url; - const showValue = currentUrl !== noUrlText || isViewMode; - const value = showValue ? currentUrl : undefined; + const value = calculateViewValue(url, noUrlText, isViewMode); - return noUrlProvided ? ( - - ) : ( + debugger; + return isInitialUrlProvided(url, existingImageUrl) ? ( + ) : ( + ); }; const isBLurInitialWithEmptyInput = (existingUrl: string, newUrl: string) => newUrl === '' && existingUrl === undefined; + +const isInitialUrlProvided = (url: string, existingImageUrl: string) => + url !== undefined || !!existingImageUrl; + +const calculateViewValue = ( + url: string, + noUrlText: string, + isViewMode: boolean, +): string | undefined => { + const currentUrl = !url ? noUrlText : url; + const currentUrlIsUserProvided = currentUrl !== noUrlText; + const showValue = currentUrlIsUserProvided || isViewMode; + return showValue ? currentUrl : undefined; +}; diff --git a/frontend/testing/playwright/pages/UiEditorPage.ts b/frontend/testing/playwright/pages/UiEditorPage.ts index de777a1208e..7294542f91f 100644 --- a/frontend/testing/playwright/pages/UiEditorPage.ts +++ b/frontend/testing/playwright/pages/UiEditorPage.ts @@ -231,8 +231,8 @@ export class UiEditorPage extends BasePage { await expect(newTreeItemLabel).toBeVisible(); } - public async deleteOldComponentId(componentId: string): Promise { - const button = this.page.getByRole('button', { name: componentId }); + public async deleteOldComponentId(): Promise { + const button = this.page.getByRole('button', { name: /^Input-/ }); await button.click(); await this.page .getByLabel(this.textMock('ux_editor.modal_properties_component_change_id')) diff --git a/frontend/testing/playwright/tests/text-editor/text-editor.spec.ts b/frontend/testing/playwright/tests/text-editor/text-editor.spec.ts index 45f375b1a02..ad7b2c586e2 100644 --- a/frontend/testing/playwright/tests/text-editor/text-editor.spec.ts +++ b/frontend/testing/playwright/tests/text-editor/text-editor.spec.ts @@ -59,7 +59,7 @@ test('That it is possible to create a text at the ui-editor page, and that the t await navigateToUiEditorAndVerifyPage(header, uiEditorPage); await uiEditorPage.dragComponentIntoDroppableList(ComponentType.Input); - await uiEditorPage.deleteOldComponentId(COMPONENT_ID); + await uiEditorPage.deleteOldComponentId(); await uiEditorPage.writeNewComponentId(COMPONENT_ID); await uiEditorPage.waitForXAmountOfMilliseconds(1000); // Wait for the API call to be done From e33e8d1a85829f5fffe416900104bfc6e32319e6 Mon Sep 17 00:00:00 2001 From: andreastanderen Date: Wed, 15 Jan 2025 14:49:05 +0100 Subject: [PATCH 4/8] clean up --- .../StudioTextfieldToggleView.module.css | 3 ++- .../ConfigPanel/ConfigContent/ConfigContent.module.css | 2 +- .../components/ConfigViewerPanel/ConfigViewerPanel.module.css | 2 +- .../config/editModal/EditImage/ExternalImage/ExternalImage.tsx | 3 +-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.module.css b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.module.css index 2f3b475ffe0..ca504b48762 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.module.css +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.module.css @@ -3,6 +3,7 @@ justify-content: flex-start; align-items: center; border-radius: 0; + border: none; width: 100%; } @@ -13,7 +14,7 @@ align-items: center; color: var(--fds-semantic-text-neutral-default); width: 100%; - gap: var(--fds-spacing-1); + gap: var(--fds-spacing-2); } .textContainer, diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/ConfigContent.module.css b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/ConfigContent.module.css index 6d8ddd217b4..e29a77382c9 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/ConfigContent.module.css +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/ConfigContent.module.css @@ -19,5 +19,5 @@ } .displayTile { - padding-inline: var(--fds-spacing-4); + padding-inline: var(--fds-spacing-3); } diff --git a/frontend/packages/process-editor/src/components/ConfigViewerPanel/ConfigViewerPanel.module.css b/frontend/packages/process-editor/src/components/ConfigViewerPanel/ConfigViewerPanel.module.css index 0cecc36699c..1dbc5026387 100644 --- a/frontend/packages/process-editor/src/components/ConfigViewerPanel/ConfigViewerPanel.module.css +++ b/frontend/packages/process-editor/src/components/ConfigViewerPanel/ConfigViewerPanel.module.css @@ -1,6 +1,6 @@ .container { display: flex; flex-direction: column; - padding: var(--fds-spacing-4); + padding: var(--fds-spacing-3); gap: var(--fds-spacing-2); } diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx index 936134cc5e9..51e44e28cba 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx @@ -73,12 +73,11 @@ type EditUrlProps = { const EditUrl = ({ url, existingImageUrl, onBlur }: EditUrlProps): React.ReactElement => { const { t } = useTranslation(); - const [isViewMode, setIsViewMode] = useState(false); + const [isViewMode, setIsViewMode] = useState(); const label = t('ux_editor.properties_panel.images.enter_external_url'); const noUrlText = t('ux_editor.properties_panel.images.external_url_not_added'); const value = calculateViewValue(url, noUrlText, isViewMode); - debugger; return isInitialUrlProvided(url, existingImageUrl) ? ( Date: Thu, 16 Jan 2025 16:16:14 +0100 Subject: [PATCH 5/8] fix PR comments --- .../StudioIconTextfield.test.tsx | 5 +- .../StudioTextfieldToggleView.test.tsx | 5 +- .../StudioToggleableTextfield.test.tsx | 2 +- .../StudioToggleableTextfield.tsx | 10 +--- .../EditComponentIdRow.test.tsx | 5 +- .../ExternalImage/ExternalImage.test.tsx | 48 ++++++++++++++++++- .../EditImage/ExternalImage/ExternalImage.tsx | 12 +++-- 7 files changed, 69 insertions(+), 18 deletions(-) diff --git a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.test.tsx b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.test.tsx index 9041543696b..8c3780f520d 100644 --- a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.test.tsx +++ b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.test.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import type { RenderResult } from '@testing-library/react'; import { render, screen } from '@testing-library/react'; import { StudioIconTextfield } from './StudioIconTextfield'; import type { StudioIconTextfieldProps } from './StudioIconTextfield'; @@ -7,6 +8,8 @@ import userEvent from '@testing-library/user-event'; import { testCustomAttributes } from '../../test-utils/testCustomAttributes'; describe('StudioIconTextfield', () => { + afterEach(jest.clearAllMocks); + it('render the icon', async () => { renderStudioIconTextfield(); expect(screen.getByRole('img', { hidden: true })).toBeInTheDocument(); @@ -40,6 +43,6 @@ const defaultProps: StudioIconTextfieldProps = { onChange, }; -const renderStudioIconTextfield = (props: Partial = {}) => { +const renderStudioIconTextfield = (props: Partial = {}): RenderResult => { return render(); }; diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.test.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.test.tsx index 600ea469b56..69980325140 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.test.tsx +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.test.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import type { RenderResult } from '@testing-library/react'; import { render, screen } from '@testing-library/react'; import { StudioTextfieldToggleView } from './StudioTextfieldToggleView'; import type { StudioTextfieldToggleViewProps } from './StudioTextfieldToggleView'; @@ -44,6 +45,8 @@ const defaultProps: StudioTextfieldToggleViewProps = { Icon: KeyVerticalIcon, }; -const renderStudioTextfieldToggleView = (props: Partial = {}) => { +const renderStudioTextfieldToggleView = ( + props: Partial = {}, +): RenderResult => { return render(); }; diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.test.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.test.tsx index e93ac27431a..46f6db027dd 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.test.tsx +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.test.tsx @@ -62,7 +62,7 @@ describe('StudioToggleableTextfield', () => { renderStudioTextField({ error }); await user.click(screen.getByRole('button', { name: value })); await user.tab(); - expect(screen.getByRole('textbox', { name: label })).toHaveAttribute('aria-invalid', 'true'); + expect(screen.getByRole('textbox', { name: label })).toBeInvalid(); expect(screen.getByText(error)).toBeInTheDocument(); expect(screen.queryByRole('button', { name: value })).not.toBeInTheDocument(); }); diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.tsx index 88a426bc9ac..5b15026df9c 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.tsx +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.tsx @@ -1,19 +1,13 @@ import React, { forwardRef, useEffect, useState } from 'react'; import { StudioTextfieldToggleView } from './StudioTextfieldToggleView'; +import type { StudioIconTextfieldProps } from '../StudioIconTextfield'; import { StudioIconTextfield } from '../StudioIconTextfield'; import { KeyVerticalIcon } from '../../../../studio-icons'; export type StudioToggleableTextfieldProps = { customValidation?: (value: string) => string | undefined; - error?: string; - Icon?: React.ComponentType>; - label: string; - onBlur?: (event: React.ChangeEvent) => void; - onChange?: (event: React.ChangeEvent) => void; onIsViewMode?: (isViewMode: boolean) => void; - title?: string; - value: string; -}; +} & StudioIconTextfieldProps; export const StudioToggleableTextfield = forwardRef( ( diff --git a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.test.tsx b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.test.tsx index 371b3cd49a8..eb393d56adb 100644 --- a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.test.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.test.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import type { RenderResult } from '@testing-library/react'; import { screen } from '@testing-library/react'; import { renderWithProviders } from '../../../../testing/mocks'; import { EditComponentIdRow, type EditComponentIdRowProps } from './EditComponentIdRow'; @@ -130,7 +131,9 @@ const defaultProps: EditComponentIdRowProps = { helpText, }; -const renderEditComponentIdRow = async (props: Partial = {}) => { +const renderEditComponentIdRow = async ( + props: Partial = {}, +): RenderResult => { queryClientMock.setQueryData([QueryKey.FormLayouts, org, app, layoutSetName], layouts); return renderWithProviders(); }; diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.test.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.test.tsx index c6ee29f68cc..29e0ee45603 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react'; import { renderWithProviders } from '../../../../../testing/mocks'; import type { ExternalImageProps } from './ExternalImage'; -import { ExternalImage } from './ExternalImage'; +import { calculateViewValue, ExternalImage } from './ExternalImage'; import { textMock } from '@studio/testing/mocks/i18nMock'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; import { QueryKey } from 'app-shared/types/QueryKey'; @@ -252,3 +252,49 @@ const renderExternalImage = ( ) => { renderWithProviders(, { queries, queryClient }); }; + +describe('calculateViewValue', () => { + const noUrlText = 'No URL Provided'; + + it('should return the URL when URL is provided and view mode is false', () => { + const url = 'http://example.com'; + const result = calculateViewValue(url, noUrlText, false); + expect(result).toBe(url); + }); + + it('should return the URL when URL is provided and view mode is true', () => { + const url = 'http://example.com'; + const result = calculateViewValue(url, noUrlText, true); + expect(result).toBe(url); + }); + + it('should return undefined when URL is empty and view mode is false', () => { + const result = calculateViewValue('', noUrlText, false); + expect(result).toBe(undefined); + }); + + it('should return noUrlText when URL is empty and view mode is true', () => { + const result = calculateViewValue('', noUrlText, true); + expect(result).toBe(noUrlText); + }); + + it('should return undefined when URL is undefined and view mode is false', () => { + const result = calculateViewValue(undefined, noUrlText, false); + expect(result).toBe(undefined); + }); + + it('should return noUrlText when URL is undefined and view mode is true', () => { + const result = calculateViewValue(undefined, noUrlText, true); + expect(result).toBe(noUrlText); + }); + + it('should return undefined when URL is equal to noUrlText and view mode is false', () => { + const result = calculateViewValue(noUrlText, noUrlText, false); + expect(result).toBe(undefined); + }); + + it('should return noUrlText when URL is equal to noUrlText and view mode is true', () => { + const result = calculateViewValue(noUrlText, noUrlText, true); + expect(result).toBe(noUrlText); + }); +}); diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx index 51e44e28cba..3bea5867fc0 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx @@ -92,14 +92,16 @@ const EditUrl = ({ url, existingImageUrl, onBlur }: EditUrlProps): React.ReactEl ); }; -const isBLurInitialWithEmptyInput = (existingUrl: string, newUrl: string) => +const isBLurInitialWithEmptyInput = (existingUrl: string | undefined, newUrl: string): boolean => newUrl === '' && existingUrl === undefined; -const isInitialUrlProvided = (url: string, existingImageUrl: string) => - url !== undefined || !!existingImageUrl; +const isInitialUrlProvided = ( + url: string | undefined, + existingImageUrl: string | undefined, +): boolean => url !== undefined || !!existingImageUrl; -const calculateViewValue = ( - url: string, +export const calculateViewValue = ( + url: string | undefined, noUrlText: string, isViewMode: boolean, ): string | undefined => { From 5ab0fd3f8abf58d540d7c43f9874be9c34fb0c9f Mon Sep 17 00:00:00 2001 From: andreastanderen Date: Fri, 17 Jan 2025 10:50:37 +0100 Subject: [PATCH 6/8] use StudioProperty.Button instead of StudioTextfieldToggleView --- .../StudioIconTextfield.stories.tsx | 2 +- .../StudioIconTextfield.test.tsx | 19 +++++-- .../StudioIconTextfield.tsx | 20 ++++--- .../StudioRecommendedNextAction.stories.tsx | 2 +- .../StudioTextfieldToggleView.module.css | 45 ---------------- .../StudioTextfieldToggleView.test.tsx | 52 ------------------- .../StudioTextfieldToggleView.tsx | 37 ------------- .../StudioTextfieldToggleView/index.ts | 4 -- .../StudioToggleableTextfield.test.tsx | 40 +++++++------- .../StudioToggleableTextfield.tsx | 35 ++++++++----- .../StudioToggleableTextfield/index.ts | 4 -- .../StudioToggleableTextfieldSchema.test.tsx | 25 +++++---- .../StudioToggleableTextfieldSchema.tsx | 20 ++++--- .../ConfigContent/ConfigContent.test.tsx | 10 ++-- .../EditLayoutSetName.test.tsx | 7 +-- .../RecommendedActionChangeName.test.tsx | 1 - .../RecommendedActionChangeName.tsx | 2 +- .../EditTaskId/EditTaskId.test.tsx | 41 ++++++--------- .../CustomReceipt/CustomReceipt.test.tsx | 38 ++++---------- .../CustomReceiptContent.test.tsx | 2 +- .../ConfigPanel/ConfigPanel.test.tsx | 2 +- .../PageConfigPanel/EditPageId.test.tsx | 18 +++++-- .../PageConfigPanel/PageConfigPanel.test.tsx | 5 +- .../components/Properties/Properties.test.tsx | 11 ++-- .../EditComponentIdRow.test.tsx | 30 +++++++---- .../PropertiesHeader.test.tsx | 4 +- .../editModal/EditImage/EditImage.test.tsx | 6 +-- .../ExternalImage/ExternalImage.test.tsx | 24 ++++----- .../EditImage/ExternalImage/ExternalImage.tsx | 12 ++--- .../DesignView/AddItem/ItemInfo/ItemInfo.tsx | 2 +- 30 files changed, 206 insertions(+), 314 deletions(-) delete mode 100644 frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.module.css delete mode 100644 frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.test.tsx delete mode 100644 frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.tsx delete mode 100644 frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/index.ts diff --git a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.stories.tsx b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.stories.tsx index 298a1a9746a..dac31e25505 100644 --- a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.stories.tsx +++ b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.stories.tsx @@ -19,7 +19,7 @@ export default meta; export const Preview: Story = (args) => ; Preview.args = { - Icon: PencilIcon, + icon: , label: 'Write a text', value: 2.3, error: 'Your custom error message!', diff --git a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.test.tsx b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.test.tsx index 8c3780f520d..aac251b30ff 100644 --- a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.test.tsx +++ b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.test.tsx @@ -10,14 +10,26 @@ import { testCustomAttributes } from '../../test-utils/testCustomAttributes'; describe('StudioIconTextfield', () => { afterEach(jest.clearAllMocks); - it('render the icon', async () => { + it('should render label', () => { renderStudioIconTextfield(); + expect(screen.getByLabelText(label)).toBeInTheDocument(); + }); + + it('should render value when provided', () => { + const value = 'value'; + renderStudioIconTextfield({ value }); + expect(screen.getByRole('textbox', { name: label })).toHaveValue(value); + }); + + it('render icon when provided', () => { + const icon = ; + renderStudioIconTextfield({ icon }); expect(screen.getByRole('img', { hidden: true })).toBeInTheDocument(); }); - it('should render label', () => { + it('does not render the icon if not provided', () => { renderStudioIconTextfield(); - expect(screen.getByLabelText(label)).toBeInTheDocument(); + expect(screen.queryByRole('img', { hidden: true })).not.toBeInTheDocument(); }); it('should execute onChange callback when input value changes', async () => { @@ -38,7 +50,6 @@ describe('StudioIconTextfield', () => { const label = 'label'; const onChange = jest.fn(); const defaultProps: StudioIconTextfieldProps = { - Icon: KeyVerticalIcon, label, onChange, }; diff --git a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.tsx b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.tsx index 075299bc0d7..b9122d09db3 100644 --- a/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.tsx +++ b/frontend/libs/studio-components/src/components/StudioIconTextfield/StudioIconTextfield.tsx @@ -2,22 +2,28 @@ import React, { forwardRef } from 'react'; import { StudioTextfield, type StudioTextfieldProps } from '../StudioTextfield'; import cn from 'classnames'; import classes from './StudioIconTextfield.module.css'; +import type { Override } from '../../types/Override'; -export type StudioIconTextfieldProps = { - Icon?: React.ComponentType>; - label: string; -} & StudioTextfieldProps; +export type StudioIconTextfieldProps = Override< + { + icon?: React.ReactNode; + label: string; + }, + StudioTextfieldProps +>; export const StudioIconTextfield = forwardRef( ( - { Icon, label, className: givenClassName, ...rest }: StudioIconTextfieldProps, + { icon, label, className: givenClassName, ...rest }: StudioIconTextfieldProps, ref, ): React.ReactElement => { const className = cn(givenClassName, classes.container); return (
- - +
+ {icon} +
+
); }, diff --git a/frontend/libs/studio-components/src/components/StudioRecommendedNextAction/StudioRecommendedNextAction.stories.tsx b/frontend/libs/studio-components/src/components/StudioRecommendedNextAction/StudioRecommendedNextAction.stories.tsx index 5560fa62401..84c2da73005 100644 --- a/frontend/libs/studio-components/src/components/StudioRecommendedNextAction/StudioRecommendedNextAction.stories.tsx +++ b/frontend/libs/studio-components/src/components/StudioRecommendedNextAction/StudioRecommendedNextAction.stories.tsx @@ -55,7 +55,7 @@ export const ExampleUseCase: ExampleUseCase = (args): React.ReactElement => { setName(e.target.value)} - Icon={KeyVerticalIcon} + icon={} size='sm' label='Nytt navn' /> diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.module.css b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.module.css deleted file mode 100644 index ca504b48762..00000000000 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.module.css +++ /dev/null @@ -1,45 +0,0 @@ -.button { - display: flex; - justify-content: flex-start; - align-items: center; - border-radius: 0; - border: none; - width: 100%; -} - -.viewModeIconsContainer { - display: grid; - grid-template-columns: auto 1fr; - text-align: left; - align-items: center; - color: var(--fds-semantic-text-neutral-default); - width: 100%; - gap: var(--fds-spacing-2); -} - -.textContainer, -.label, -.ellipsis { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -} - -.label { - font-weight: bold; -} - -.textContainer { - display: flex; - flex-direction: column; -} - -.editIcon { - display: none; -} - -.button:hover .editIcon, -.button:focus .editIcon { - display: flex; - align-items: center; -} diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.test.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.test.tsx deleted file mode 100644 index 69980325140..00000000000 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.test.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import type { RenderResult } from '@testing-library/react'; -import { render, screen } from '@testing-library/react'; -import { StudioTextfieldToggleView } from './StudioTextfieldToggleView'; -import type { StudioTextfieldToggleViewProps } from './StudioTextfieldToggleView'; -import userEvent from '@testing-library/user-event'; -import { KeyVerticalIcon } from '@studio/icons'; - -describe('StudioTextfieldToggleView', () => { - it('should render button text', () => { - renderStudioTextfieldToggleView(); - expect(screen.getByRole('button', { name: value })).toBeInTheDocument(); - }); - - it('should execute the "onClick" method when button is clicked', async () => { - const user = userEvent.setup(); - renderStudioTextfieldToggleView(); - await user.click(screen.getByRole('button', { name: value })); - expect(onClick).toHaveBeenCalledTimes(1); - }); - - it('should render the both given Icon and pencilIcon', () => { - renderStudioTextfieldToggleView(); - expect(screen.getAllByRole('img', { hidden: true })).toHaveLength(2); - }); - - it('should forward the rest of the props to the button', () => { - renderStudioTextfieldToggleView({ disabled: true }); - expect(screen.getByRole('button', { name: value })).toBeDisabled(); - }); - - it('should show label if defined', () => { - renderStudioTextfieldToggleView(); - expect(screen.getByText(label)).toBeInTheDocument(); - }); -}); - -const value = 'value'; -const label = 'label'; -const onClick = jest.fn(); -const defaultProps: StudioTextfieldToggleViewProps = { - value, - label, - onClick, - Icon: KeyVerticalIcon, -}; - -const renderStudioTextfieldToggleView = ( - props: Partial = {}, -): RenderResult => { - return render(); -}; diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.tsx deleted file mode 100644 index 651ff197ee0..00000000000 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/StudioTextfieldToggleView.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import { PencilIcon } from '@studio/icons'; -import { StudioButton, type StudioButtonProps } from '@studio/components'; -import classes from './StudioTextfieldToggleView.module.css'; -import cn from 'classnames'; - -export type StudioTextfieldToggleViewProps = Omit & { - Icon?: React.ComponentType>; - label: string; -}; - -export const StudioTextfieldToggleView = ({ - onClick, - label, - className: givenClass, - Icon, - ...rest -}: StudioTextfieldToggleViewProps) => { - const className = cn(classes.button, givenClass); - - return ( - - - - - {label && ( - - {label} - - )} - {rest.value} - - - - - ); -}; diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/index.ts b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/index.ts deleted file mode 100644 index 5be1059ebdc..00000000000 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioTextfieldToggleView/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { - StudioTextfieldToggleView, - type StudioTextfieldToggleViewProps, -} from './StudioTextfieldToggleView'; diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.test.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.test.tsx index 46f6db027dd..27147bf9cbc 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.test.tsx +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.test.tsx @@ -17,21 +17,23 @@ describe('StudioToggleableTextfield', () => { afterEach(jest.clearAllMocks); it('Renders the view mode by default', () => { - renderStudioTextField(); - expect(screen.getByRole('button', { name: value })).toBeInTheDocument(); + renderStudioToggleableTextfield(); + expect(screen.getByRole('button', { name: label })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: label })).toHaveTextContent(value); }); it('should toggle to edit-mode when edit button is clicked', async () => { const user = userEvent.setup(); - renderStudioTextField(); - await user.click(screen.getByRole('button', { name: value })); + renderStudioToggleableTextfield(); + await user.click(screen.getByRole('button', { name: label })); expect(screen.getByRole('textbox', { name: label })).toBeInTheDocument(); + expect(screen.getByRole('textbox', { name: label })).toHaveValue(value); }); it('should run custom validation when value changes', async () => { const user = userEvent.setup(); - renderStudioTextField({ customValidation }); - await user.click(screen.getByRole('button', { name: value })); + renderStudioToggleableTextfield({ customValidation }); + await user.click(screen.getByRole('button', { name: label })); const typedInputValue = 'John'; await user.type(screen.getByRole('textbox', { name: label }), typedInputValue); expect(customValidation).toHaveBeenCalledTimes(typedInputValue.length); @@ -39,19 +41,19 @@ describe('StudioToggleableTextfield', () => { it('should toggle back to view mode on blur', async () => { const user = userEvent.setup(); - renderStudioTextField(); - const viewButton = screen.getByRole('button', { name: value }); + renderStudioToggleableTextfield(); + const viewButton = screen.getByRole('button', { name: label }); await user.click(viewButton); const editTextfield = screen.getByRole('textbox', { name: label }); expect(editTextfield).toBeInTheDocument(); await user.tab(); - expect(screen.getByRole('button', { name: value })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: label })).toBeInTheDocument(); }); it('should execute onBlur method when input is blurred', async () => { const user = userEvent.setup(); - renderStudioTextField(); - await user.click(screen.getByRole('button', { name: value })); + renderStudioToggleableTextfield(); + await user.click(screen.getByRole('button', { name: label })); await user.tab(); expect(onBlur).toHaveBeenCalledTimes(1); }); @@ -59,19 +61,19 @@ describe('StudioToggleableTextfield', () => { it('should not toggle view on blur when input field has error', async () => { const user = userEvent.setup(); const error = 'Your name is a required field'; - renderStudioTextField({ error }); - await user.click(screen.getByRole('button', { name: value })); + renderStudioToggleableTextfield({ error }); + await user.click(screen.getByRole('button', { name: label })); await user.tab(); expect(screen.getByRole('textbox', { name: label })).toBeInvalid(); expect(screen.getByText(error)).toBeInTheDocument(); - expect(screen.queryByRole('button', { name: value })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: label })).not.toBeInTheDocument(); }); it('should execute onChange method when input value changes', async () => { const user = userEvent.setup(); - renderStudioTextField(); + renderStudioToggleableTextfield(); const inputValue = 'John'; - await user.click(screen.getByRole('button', { name: value })); + await user.click(screen.getByRole('button', { name: label })); await user.type(screen.getByRole('textbox', { name: label }), inputValue); expect(onChange).toHaveBeenCalledTimes(inputValue.length); }); @@ -79,11 +81,11 @@ describe('StudioToggleableTextfield', () => { it('should render error message if customValidation occured', async () => { const user = userEvent.setup(); const customError = 'Your name cannot include "test"'; - renderStudioTextField({ + renderStudioToggleableTextfield({ customValidation: (valueToValidate: string) => valueToValidate.includes('test') ? customError : undefined, }); - await user.click(screen.getByRole('button', { name: value })); + await user.click(screen.getByRole('button', { name: label })); await user.type(screen.getByRole('textbox', { name: label }), 'test'); expect(screen.getByText(customError)); }); @@ -97,6 +99,6 @@ const defaultProps: StudioToggleableTextfieldProps = { customValidation, }; -const renderStudioTextField = (props: Partial = {}) => { +const renderStudioToggleableTextfield = (props: Partial = {}) => { return render(); }; diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.tsx index 5b15026df9c..309461d5e38 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.tsx +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/StudioToggleableTextfield.tsx @@ -1,26 +1,32 @@ import React, { forwardRef, useEffect, useState } from 'react'; -import { StudioTextfieldToggleView } from './StudioTextfieldToggleView'; import type { StudioIconTextfieldProps } from '../StudioIconTextfield'; import { StudioIconTextfield } from '../StudioIconTextfield'; -import { KeyVerticalIcon } from '../../../../studio-icons'; +import { StudioProperty } from '../StudioProperty'; +import { KeyVerticalIcon } from '@studio/icons'; +import type { Override } from '../../types/Override'; -export type StudioToggleableTextfieldProps = { - customValidation?: (value: string) => string | undefined; - onIsViewMode?: (isViewMode: boolean) => void; -} & StudioIconTextfieldProps; +export type StudioToggleableTextfieldProps = Override< + { + customValidation?: (value: string) => string | undefined; + onIsViewMode?: (isViewMode: boolean) => void; + }, + StudioIconTextfieldProps +>; export const StudioToggleableTextfield = forwardRef( ( { - customValidation, error, - Icon = KeyVerticalIcon, + customValidation, + icon = , label, onBlur, onChange, + onClick, onIsViewMode, title, value, + ...rest }: StudioToggleableTextfieldProps, ref, ) => { @@ -46,7 +52,7 @@ export const StudioToggleableTextfield = forwardRef): void => { + const handleOnBlur = (event: React.FocusEvent): void => { // Should not close the view mode or blur if there is an error if (errorMessage || error) { return; @@ -66,9 +72,9 @@ export const StudioToggleableTextfield = forwardRef ); }, diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/index.ts b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/index.ts index 5cd34e368d4..500d66c3de8 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfield/index.ts +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfield/index.ts @@ -2,7 +2,3 @@ export { StudioToggleableTextfield, type StudioToggleableTextfieldProps, } from './StudioToggleableTextfield'; -export { - StudioTextfieldToggleView, - type StudioTextfieldToggleViewProps, -} from './StudioTextfieldToggleView'; diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.test.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.test.tsx index 399da1f1650..f8030a862cb 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.test.tsx +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.test.tsx @@ -45,17 +45,22 @@ describe('StudioToggleableTextfieldSchema', () => { it('should toggle to edit mode when clicking edit', async () => { const user = userEvent.setup(); renderStudioTextfieldSchema(); - await user.click(screen.getByRole('button', { name: value })); - expect(screen.getByRole('textbox', { name: label })).toBeInTheDocument(); + const viewButton = screen.getByRole('button', { name: label }); + expect(viewButton).toBeInTheDocument(); + expect(viewButton).toHaveTextContent(value); + await user.click(viewButton); + const editTextfield = screen.getByRole('textbox', { name: label }); + expect(editTextfield).toBeInTheDocument(); + expect(editTextfield).toHaveValue(value); }); it('should toggle to view mode on blur', async () => { const user = userEvent.setup(); renderStudioTextfieldSchema(); - await user.click(screen.getByRole('button', { name: value })); - expect(screen.queryByRole('button', { name: value })).not.toBeInTheDocument(); + await user.click(screen.getByRole('button', { name: label })); + expect(screen.queryByRole('button', { name: label })).not.toBeInTheDocument(); await user.tab(); - expect(screen.getByRole('button', { name: value })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: label })).toBeInTheDocument(); }); it('should not toggle to view mode on blur if input is invalid', async () => { @@ -65,15 +70,15 @@ describe('StudioToggleableTextfieldSchema', () => { ...defaultProps, error, }); - await user.click(screen.getByRole('button', { name: value })); + await user.click(screen.getByRole('button', { name: label })); await user.tab(); - expect(screen.queryByRole('button', { name: value })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: label })).not.toBeInTheDocument(); }); it('should validate field against json schema and invoke "onError" if validation has errors', async () => { const user = userEvent.setup(); renderStudioTextfieldSchema(); - await user.click(screen.getByRole('button', { name: value })); + await user.click(screen.getByRole('button', { name: label })); await user.type(screen.getByRole('textbox', { name: label }), 'invalid-value-01'); expect(defaultProps.onError).toHaveBeenCalledWith({ errorCode: 'pattern', @@ -84,7 +89,7 @@ describe('StudioToggleableTextfieldSchema', () => { it('should validate field against json schema and invoke "onError" if field is required', async () => { const user = userEvent.setup(); renderStudioTextfieldSchema(); - await user.click(screen.getByRole('button', { name: value })); + await user.click(screen.getByRole('button', { name: label })); await user.clear(screen.getByRole('textbox', { name: label })); expect(defaultProps.onError).toHaveBeenCalledWith({ errorCode: 'required', @@ -96,7 +101,7 @@ describe('StudioToggleableTextfieldSchema', () => { const user = userEvent.setup(); const invalidValue = 'invalid-value-01'; renderStudioTextfieldSchema(); - await user.click(screen.getByRole('button', { name: value })); + await user.click(screen.getByRole('button', { name: label })); await user.type(screen.getByRole('textbox', { name: label }), invalidValue); expect(defaultProps.onError).toHaveBeenCalledWith({ errorCode: 'pattern', diff --git a/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.tsx b/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.tsx index 79b1ba5ac76..d0676bf860e 100644 --- a/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.tsx +++ b/frontend/libs/studio-components/src/components/StudioToggleableTextfieldSchema/StudioToggleableTextfieldSchema.tsx @@ -5,19 +5,23 @@ import { StudioToggleableTextfield, type StudioToggleableTextfieldProps, } from '../StudioToggleableTextfield'; +import type { Override } from '../../types/Override'; export type SchemaValidationError = { errorCode: string; details: string; }; -export type StudioToggleableTextfieldSchemaProps = { - layoutSchema: JsonSchema; - relatedSchemas: JsonSchema[]; - propertyPath: string; - onIsViewMode?: (isViewMode: boolean) => void; - onError?: (error: SchemaValidationError | null) => void; -} & StudioToggleableTextfieldProps; +export type StudioToggleableTextfieldSchemaProps = Override< + { + layoutSchema: JsonSchema; + relatedSchemas: JsonSchema[]; + propertyPath: string; + onIsViewMode?: (isViewMode: boolean) => void; + onError?: (error: SchemaValidationError | null) => void; + }, + StudioToggleableTextfieldProps +>; export const StudioToggleableTextfieldSchema = forwardRef< HTMLDivElement, @@ -65,11 +69,11 @@ export const StudioToggleableTextfieldSchema = forwardRef< return ( ) => handleOnChange(event)} error={error} onIsViewMode={onIsViewMode} + {...rest} /> ); }, diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/ConfigContent.test.tsx b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/ConfigContent.test.tsx index a9a17b552f3..67aed809619 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/ConfigContent.test.tsx +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/ConfigContent.test.tsx @@ -89,11 +89,11 @@ describe('ConfigContent', () => { it('should render EditTaskId component', () => { renderConfigContent(); - expect( - screen.getByRole('button', { - name: mockBpmnDetails.id, - }), - ).toBeInTheDocument(); + const editTaskIdButton = screen.getByRole('button', { + name: textMock('process_editor.configuration_panel_change_task_id_label'), + }); + expect(editTaskIdButton).toBeInTheDocument(); + expect(editTaskIdButton).toHaveTextContent(mockBpmnDetails.id); }); it.each(['data', 'confirmation', 'feedback', 'signing'])( diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetName/EditLayoutSetName.test.tsx b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetName/EditLayoutSetName.test.tsx index 4c97207eb28..807307841fe 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetName/EditLayoutSetName.test.tsx +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetName/EditLayoutSetName.test.tsx @@ -12,9 +12,10 @@ describe('EditLayoutSetName', () => { it('should render the layoutSetName button', () => { renderEditLayoutSetName(); const editLayoutSetName = screen.getByRole('button', { - name: existingLayoutSetNameMock, + name: textMock('process_editor.configuration_panel_layout_set_name_label'), }); expect(editLayoutSetName).toBeInTheDocument(); + expect(editLayoutSetName).toHaveTextContent(existingLayoutSetNameMock); }); it('should call mutateLayoutSet when changing name', async () => { @@ -23,7 +24,7 @@ describe('EditLayoutSetName', () => { const mutateLayoutSetIdMock = jest.fn(); renderEditLayoutSetName({ mutateLayoutSetId: mutateLayoutSetIdMock }); const editLayoutSetName = screen.getByRole('button', { - name: existingLayoutSetNameMock, + name: textMock('process_editor.configuration_panel_layout_set_name_label'), }); await user.click(editLayoutSetName); const inputNewLayoutSetName = screen.getByRole('textbox', { @@ -44,7 +45,7 @@ describe('EditLayoutSetName', () => { const mutateLayoutSetIdMock = jest.fn(); renderEditLayoutSetName({ mutateLayoutSetId: mutateLayoutSetIdMock }); const editLayoutSetName = screen.getByRole('button', { - name: existingLayoutSetNameMock, + name: textMock('process_editor.configuration_panel_layout_set_name_label'), }); await user.click(editLayoutSetName); const inputNewLayoutSetName = screen.getByRole('textbox', { diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetNameRecommendedAction/RecommendedActionChangeName.test.tsx b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetNameRecommendedAction/RecommendedActionChangeName.test.tsx index 675d6467205..c8e502e07d5 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetNameRecommendedAction/RecommendedActionChangeName.test.tsx +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetNameRecommendedAction/RecommendedActionChangeName.test.tsx @@ -42,7 +42,6 @@ describe('RecommendedActionChangeName', () => { name: textMock('process_editor.recommended_action.new_name_label'), }); await user.type(newNameInput, newLayoutSetName); - expect(validateLayoutSetNameMock).toHaveBeenCalledTimes(newLayoutSetName.length); expect(validateLayoutSetNameMock).toHaveBeenCalledWith(newLayoutSetName, expect.any(Object)); }); diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetNameRecommendedAction/RecommendedActionChangeName.tsx b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetNameRecommendedAction/RecommendedActionChangeName.tsx index 49397ca09b8..e52a6ec7939 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetNameRecommendedAction/RecommendedActionChangeName.tsx +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditLayoutSetNameRecommendedAction/RecommendedActionChangeName.tsx @@ -45,7 +45,7 @@ export const RecommendedActionChangeName = (): React.ReactElement => { > } label={t('process_editor.recommended_action.new_name_label')} onChange={(event: React.ChangeEvent) => { setNewName(event.target.value); diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditTaskId/EditTaskId.test.tsx b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditTaskId/EditTaskId.test.tsx index d95fb32ad34..9230053cce1 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditTaskId/EditTaskId.test.tsx +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigContent/EditTaskId/EditTaskId.test.tsx @@ -43,28 +43,25 @@ describe('EditTaskId', () => { }); it('should render task id as view mode by default', () => { render(); - - expect( - screen.getByRole('button', { - name: mockBpmnDetails.id, - }), - ).toBeInTheDocument(); + const editTaskIdButton = screen.getByRole('button', { + name: textMock('process_editor.configuration_panel_change_task_id_label'), + }); + expect(editTaskIdButton).toBeInTheDocument(); + expect(editTaskIdButton).toHaveTextContent(mockBpmnDetails.id); }); it('should render task id in edit mode when clicking on the edit button', async () => { const user = userEvent.setup(); render(); - const editButton = screen.getByRole('button', { - name: mockBpmnDetails.id, + name: textMock('process_editor.configuration_panel_change_task_id_label'), }); await user.click(editButton); - - expect( - screen.getByRole('textbox', { - name: textMock('process_editor.configuration_panel_change_task_id_label'), - }), - ).toBeInTheDocument(); + const editTaskIdInput = screen.getByRole('textbox', { + name: textMock('process_editor.configuration_panel_change_task_id_label'), + }); + expect(editTaskIdInput).toBeInTheDocument(); + expect(editTaskIdInput).toHaveValue(mockBpmnDetails.id); }); it('should update metadataFromRef and updateId (implicitly calling setBpmnDetails) when changing task id', async () => { @@ -78,7 +75,7 @@ describe('EditTaskId', () => { render(); const editButton = screen.getByRole('button', { - name: mockBpmnDetails.id, + name: textMock('process_editor.configuration_panel_change_task_id_label'), }); await user.click(editButton); @@ -146,20 +143,16 @@ describe('EditTaskId', () => { it(`should display validation error when task id ${description}`, async () => { const user = userEvent.setup(); render(); - const editButton = screen.getByRole('button', { - name: mockBpmnDetails.id, + name: textMock('process_editor.configuration_panel_change_task_id_label'), }); await user.click(editButton); - const input = screen.getByRole('textbox', { name: textMock('process_editor.configuration_panel_change_task_id_label'), }); - await user.clear(input); if (inputValue !== '') await user.type(input, inputValue); await user.tab(); - const errorMessage = await screen.findByText(textMock(expectedError, textArgs)); expect(errorMessage).toBeInTheDocument(); }); @@ -172,22 +165,18 @@ describe('EditTaskId', () => { (useBpmnConfigPanelFormContext as jest.Mock).mockReturnValue({ metadataFormRef: metadataFormRefMock, }); - render(); - const editButton = screen.getByRole('button', { - name: mockBpmnDetails.id, + name: textMock('process_editor.configuration_panel_change_task_id_label'), }); + expect(editButton).toHaveTextContent(mockBpmnDetails.id); await user.click(editButton); - const input = screen.getByRole('textbox', { name: textMock('process_editor.configuration_panel_change_task_id_label'), }); - await user.clear(input); await user.type(input, mockBpmnDetails.id); await user.tab(); - expect(metadataFormRefMock.current).toBeUndefined(); expect(setBpmnDetailsMock).not.toHaveBeenCalled(); }); diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceipt/CustomReceipt.test.tsx b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceipt/CustomReceipt.test.tsx index 05e8cec8fc5..c408c39af39 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceipt/CustomReceipt.test.tsx +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceipt/CustomReceipt.test.tsx @@ -50,21 +50,17 @@ describe('CustomReceipt', () => { it('calls "mutateLayoutSetId" when the layoutSet id is changed', async () => { const user = userEvent.setup(); renderCustomReceipt(); - const toggleableTextfieldButton = screen.getByRole('button', { - name: existingCustomReceiptLayoutSetId, + name: textMock('process_editor.configuration_panel_custom_receipt_textfield_label'), }); - await user.click(toggleableTextfieldButton); - - const textfield = screen.getByLabelText( - textMock('process_editor.configuration_panel_custom_receipt_textfield_label'), - ); + const textfield = screen.getByRole('textbox', { + name: textMock('process_editor.configuration_panel_custom_receipt_textfield_label'), + }); const newLayoutSetId: string = 'Test2'; await user.clear(textfield); await user.type(textfield, newLayoutSetId); await user.tab(); - expect(mockBpmnApiContextValue.mutateLayoutSetId).toHaveBeenCalledTimes(1); expect(mockBpmnApiContextValue.mutateLayoutSetId).toHaveBeenCalledWith({ layoutSetIdToUpdate: existingCustomReceiptLayoutSetId, @@ -75,34 +71,29 @@ describe('CustomReceipt', () => { it('does not call "mutateLayoutSetId" when the layoutSet id is changed to the original id', async () => { const user = userEvent.setup(); renderCustomReceipt(); - const toggleableTextfieldButton = screen.getByRole('button', { - name: existingCustomReceiptLayoutSetId, + name: textMock('process_editor.configuration_panel_custom_receipt_textfield_label'), }); - + expect(toggleableTextfieldButton).toHaveTextContent(existingCustomReceiptLayoutSetId); await user.click(toggleableTextfieldButton); - const textfield = screen.getByLabelText( textMock('process_editor.configuration_panel_custom_receipt_textfield_label'), ); await user.clear(textfield); await user.type(textfield, existingCustomReceiptLayoutSetId); await user.tab(); - expect(mockBpmnApiContextValue.mutateLayoutSetId).not.toHaveBeenCalled(); }); it('calls "mutateDataTypes" when the data model id is changed', async () => { const user = userEvent.setup(); renderCustomReceipt(); - const propertyButton = screen.getByRole('button', { name: textMock('process_editor.configuration_panel_set_data_model', { dataModelName: mockBpmnApiContextValue.layoutSets.sets[0].dataType, }), }); await user.click(propertyButton); - const combobox = screen.getByRole('combobox', { name: textMock('process_editor.configuration_panel_set_data_model_label'), }); @@ -110,7 +101,6 @@ describe('CustomReceipt', () => { const newOption: string = mockAllDataModelIds[1]; const option = screen.getByRole('option', { name: newOption }); await user.click(option); - expect(mockBpmnApiContextValue.mutateDataTypes).toHaveBeenCalledTimes(1); expect(mockBpmnApiContextValue.mutateDataTypes).toHaveBeenCalledWith({ connectedTaskId: PROTECTED_TASK_NAME_CUSTOM_RECEIPT, @@ -128,28 +118,21 @@ describe('CustomReceipt', () => { renderCustomReceipt({ layoutSets: { sets: [layoutSetWithCustomReceipt, layoutSetWithDataTask] }, }); - const toggleableTextfieldButton = screen.getByRole('button', { - name: existingCustomReceiptLayoutSetId, + name: textMock('process_editor.configuration_panel_custom_receipt_textfield_label'), }); - await user.click(toggleableTextfieldButton); - - const inputField = screen.getByLabelText( - textMock('process_editor.configuration_panel_custom_receipt_textfield_label'), - ); - + const inputField = screen.getByRole('textbox', { + name: textMock('process_editor.configuration_panel_custom_receipt_textfield_label'), + }); await user.clear(inputField); if (invalidLayoutSetId !== emptyLayoutSetName) await user.type(inputField, invalidLayoutSetId); await user.tab(); - const errorTextKey = layoutSetIdTextKeys[invalidLayoutSetId]; - if (errorTextKey) { const error = screen.getByText(textMock(errorTextKey)); expect(error).toBeInTheDocument(); } - expect(mockBpmnApiContextValue.mutateLayoutSetId).not.toHaveBeenCalled(); }); @@ -157,7 +140,6 @@ describe('CustomReceipt', () => { const user = userEvent.setup(); jest.spyOn(window, 'confirm').mockImplementation(() => true); renderCustomReceipt(); - const deleteButton = screen.getByRole('button', { name: textMock('process_editor.configuration_panel_custom_receipt_delete_button'), }); diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceiptContent.test.tsx b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceiptContent.test.tsx index 2e815b280d4..1f4b46261cf 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceiptContent.test.tsx +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigEndEvent/CustomReceiptContent/CustomReceiptContent.test.tsx @@ -99,7 +99,7 @@ describe('CustomReceiptContent', () => { }); const toggleableTextfieldButton = screen.getByRole('button', { - name: existingCustomReceiptLayoutSetId, + name: textMock('process_editor.configuration_panel_custom_receipt_textfield_label'), }); expect(toggleableTextfieldButton).toBeInTheDocument(); }); diff --git a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigPanel.test.tsx b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigPanel.test.tsx index e869177e602..d2723d5c5e4 100644 --- a/frontend/packages/process-editor/src/components/ConfigPanel/ConfigPanel.test.tsx +++ b/frontend/packages/process-editor/src/components/ConfigPanel/ConfigPanel.test.tsx @@ -43,7 +43,7 @@ describe('ConfigPanel', () => { bpmnDetails: { ...mockBpmnDetails, type: BpmnTypeEnum.Task }, }); const editTaskIdButton = screen.getByRole('button', { - name: mockBpmnDetails.id, + name: textMock('process_editor.configuration_panel_change_task_id_label'), }); expect(editTaskIdButton).toBeInTheDocument(); }); diff --git a/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/EditPageId.test.tsx b/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/EditPageId.test.tsx index f2fe20385bd..6aa01b802ba 100644 --- a/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/EditPageId.test.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/EditPageId.test.tsx @@ -23,7 +23,11 @@ const layoutSetName = layoutSet1NameMock; describe('EditPageId', () => { it('renders given page ID', () => { renderEditPageId(); - screen.getByRole('button', { name: selectedLayout }); + const editPageIdButton = screen.getByRole('button', { + name: textMock('ux_editor.modal_properties_textResourceBindings_page_id'), + }); + expect(editPageIdButton).toBeInTheDocument(); + expect(editPageIdButton).toHaveTextContent(selectedLayout); }); it('calls updateFormLayoutName and textIdMutation with new page ID when changed', async () => { @@ -36,7 +40,9 @@ describe('EditPageId', () => { updateFormLayoutName, }; renderEditPageId(mockQueries); - const pageIdButton = screen.getByRole('button', { name: selectedLayout }); + const pageIdButton = screen.getByRole('button', { + name: textMock('ux_editor.modal_properties_textResourceBindings_page_id'), + }); await user.click(pageIdButton); const editPageId = screen.getByRole('textbox', { name: textMock('ux_editor.modal_properties_textResourceBindings_page_id'), @@ -66,7 +72,9 @@ describe('EditPageId', () => { updateFormLayoutName, }; renderEditPageId(mockQueries); - const pageIdButton = screen.getByRole('button', { name: selectedLayout }); + const pageIdButton = screen.getByRole('button', { + name: textMock('ux_editor.modal_properties_textResourceBindings_page_id'), + }); await user.click(pageIdButton); const editPageId = screen.getByRole('textbox', { name: textMock('ux_editor.modal_properties_textResourceBindings_page_id'), @@ -83,7 +91,9 @@ describe('EditPageId', () => { renderEditPageId(); const notUniqueErrorMessage = screen.queryByText(textMock('ux_editor.pages_error_unique')); expect(notUniqueErrorMessage).not.toBeInTheDocument(); - const pageIdButton = screen.getByRole('button', { name: selectedLayout }); + const pageIdButton = screen.getByRole('button', { + name: textMock('ux_editor.modal_properties_textResourceBindings_page_id'), + }); await user.click(pageIdButton); const editPageId = screen.getByRole('textbox', { name: textMock('ux_editor.modal_properties_textResourceBindings_page_id'), diff --git a/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/PageConfigPanel.test.tsx b/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/PageConfigPanel.test.tsx index 23471edc68f..fa8aa5ffafd 100644 --- a/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/PageConfigPanel.test.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/PageConfigPanel/PageConfigPanel.test.tsx @@ -79,7 +79,10 @@ describe('PageConfigPanel', () => { }); expect(screen.queryByRole('heading', { name: newSelectedPage })).not.toBeInTheDocument(); screen.getByRole('heading', { name: newVisualPageName }); - screen.getByRole('button', { name: newSelectedPage }); + const editPageIdButton = screen.getByRole('button', { + name: textMock('ux_editor.modal_properties_textResourceBindings_page_id'), + }); + expect(editPageIdButton).toHaveTextContent(newSelectedPage); }); it('render warning when layout is selected and has duplicated ids', () => { diff --git a/frontend/packages/ux-editor/src/components/Properties/Properties.test.tsx b/frontend/packages/ux-editor/src/components/Properties/Properties.test.tsx index 10938dd9cc7..9c1663a3c74 100644 --- a/frontend/packages/ux-editor/src/components/Properties/Properties.test.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/Properties.test.tsx @@ -81,9 +81,10 @@ describe('Properties', () => { }); expect(heading).toBeInTheDocument(); const editComponentIdButton = screen.getByRole('button', { - name: componentMocks[ComponentType.Input].id, + name: textMock('ux_editor.modal_properties_component_change_id'), }); expect(editComponentIdButton).toBeInTheDocument(); + expect(editComponentIdButton).toHaveTextContent(componentMocks[ComponentType.Input].id); await user.click(editComponentIdButton); const textbox = screen.getByRole('textbox', { name: textMock('ux_editor.modal_properties_component_change_id'), @@ -99,12 +100,16 @@ describe('Properties', () => { const user = userEvent.setup(); renderProperties(); await user.click( - screen.getByRole('button', { name: componentMocks[ComponentType.Input].id }), + screen.getByRole('button', { + name: textMock('ux_editor.modal_properties_component_change_id'), + }), ); const invalidId = 'invalidId-01'; await user.type( - screen.getByLabelText(textMock('ux_editor.modal_properties_component_change_id')), + screen.getByRole('textbox', { + name: textMock('ux_editor.modal_properties_component_change_id'), + }), invalidId, ); diff --git a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.test.tsx b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.test.tsx index eb393d56adb..82134d1e5c0 100644 --- a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.test.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.test.tsx @@ -24,14 +24,19 @@ describe('EditComponentIdRow', () => { it('should render button ', async () => { await renderEditComponentIdRow(); - const testIdButton = screen.getByRole('button', { name: componentId }); + const testIdButton = screen.getByRole('button', { + name: textMock('ux_editor.modal_properties_component_change_id'), + }); expect(testIdButton).toBeInTheDocument(); + expect(testIdButton).toHaveTextContent(componentMocks[ComponentType.Input].id); }); it('should render textField when the button is clicked', async () => { const user = userEvent.setup(); await renderEditComponentIdRow(); - const testIdButton = screen.getByRole('button', { name: componentId }); + const testIdButton = screen.getByRole('button', { + name: textMock('ux_editor.modal_properties_component_change_id'), + }); await user.click(testIdButton); const textField = screen.getByRole('textbox', { name: textMock('ux_editor.modal_properties_component_change_id'), @@ -42,7 +47,9 @@ describe('EditComponentIdRow', () => { it('should not render the textfield when changing from edit mode to view mode ', async () => { const user = userEvent.setup(); await renderEditComponentIdRow(); - const testIdButton = screen.getByRole('button', { name: componentId }); + const testIdButton = screen.getByRole('button', { + name: textMock('ux_editor.modal_properties_component_change_id'), + }); await user.click(testIdButton); const textField = screen.getByRole('textbox', { name: textMock('ux_editor.modal_properties_component_change_id'), @@ -54,7 +61,9 @@ describe('EditComponentIdRow', () => { it('should call onChange when user change the input in text filed.', async () => { const user = userEvent.setup(); await renderEditComponentIdRow(); - const testIdButton = screen.getByRole('button', { name: componentId }); + const testIdButton = screen.getByRole('button', { + name: textMock('ux_editor.modal_properties_component_change_id'), + }); await user.click(testIdButton); const textField = screen.getByRole('textbox', { name: textMock('ux_editor.modal_properties_component_change_id'), @@ -73,7 +82,9 @@ describe('EditComponentIdRow', () => { it('should show error required error message when id is empty', async () => { const user = userEvent.setup(); await renderEditComponentIdRow(); - const testIdButton = screen.getByRole('button', { name: componentId }); + const testIdButton = screen.getByRole('button', { + name: textMock('ux_editor.modal_properties_component_change_id'), + }); await user.click(testIdButton); const textField = screen.getByRole('textbox', { name: textMock('ux_editor.modal_properties_component_change_id'), @@ -85,7 +96,9 @@ describe('EditComponentIdRow', () => { it('should show error message when id is not unique', async () => { const user = userEvent.setup(); await renderEditComponentIdRow(); - const testIdButton = screen.getByRole('button', { name: componentId }); + const testIdButton = screen.getByRole('button', { + name: textMock('ux_editor.modal_properties_component_change_id'), + }); await user.click(testIdButton); const textField = screen.getByRole('textbox', { name: textMock('ux_editor.modal_properties_component_change_id'), @@ -107,7 +120,7 @@ describe('EditComponentIdRow', () => { component: componentMocks[ComponentType.FileUpload], }); const testIdButton = screen.getByRole('button', { - name: componentMocks[ComponentType.FileUpload].id, + name: textMock('ux_editor.modal_properties_component_change_id'), }); await user.click(testIdButton); const textField = screen.getByRole('textbox', { @@ -122,7 +135,6 @@ describe('EditComponentIdRow', () => { }); }); -const componentId = componentMocks[ComponentType.Input].id; const handleComponentUpdate = jest.fn(); const helpText = 'helpText'; const defaultProps: EditComponentIdRowProps = { @@ -133,7 +145,7 @@ const defaultProps: EditComponentIdRowProps = { const renderEditComponentIdRow = async ( props: Partial = {}, -): RenderResult => { +): Promise => { queryClientMock.setQueryData([QueryKey.FormLayouts, org, app, layoutSetName], layouts); return renderWithProviders(); }; diff --git a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/PropertiesHeader.test.tsx b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/PropertiesHeader.test.tsx index 74674655c7a..eda89576cdd 100644 --- a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/PropertiesHeader.test.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/PropertiesHeader.test.tsx @@ -65,7 +65,7 @@ describe('PropertiesHeader', () => { renderPropertiesHeader(); const editComponentIdButton = screen.getByRole('button', { - name: component1Mock.id, + name: textMock('ux_editor.modal_properties_component_change_id'), }); await user.click(editComponentIdButton); @@ -83,7 +83,7 @@ describe('PropertiesHeader', () => { renderPropertiesHeader(); const editComponentIdButton = screen.getByRole('button', { - name: component1Mock.id, + name: textMock('ux_editor.modal_properties_component_change_id'), }); await user.click(editComponentIdButton); diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/EditImage.test.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/EditImage.test.tsx index eba48c3105f..466168ec52b 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/EditImage.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/EditImage.test.tsx @@ -89,7 +89,7 @@ describe('EditImage', () => { }, }); await goToExternalUrlTab(user); - await clickExistingUrlButton(user, existingExternalUrl); + await clickExistingUrlButton(user); await enterUrlInField(user, undefined); expect(handleComponentChangeMock).toHaveBeenCalledTimes(1); expect(handleComponentChangeMock).toHaveBeenCalledWith({ @@ -195,9 +195,9 @@ const goToExternalUrlTab = async (user: UserEvent) => { ); }; -const clickExistingUrlButton = async (user: UserEvent, existingExternalUrl: string) => { +const clickExistingUrlButton = async (user: UserEvent) => { const existingUrlButton = screen.getByRole('button', { - name: existingExternalUrl, + name: textMock('ux_editor.properties_panel.images.enter_external_url'), }); await user.click(existingUrlButton); }; diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.test.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.test.tsx index 29e0ee45603..3a47a3c9772 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.test.tsx @@ -38,8 +38,9 @@ describe('ExternalImage', () => { it('shows existing url in view mode if exist', () => { const existingUrl = 'someExistingUrl'; renderExternalImage({ existingImageUrl: existingUrl }); - const existingUrlButton = getExistingUrlButton(existingUrl); + const existingUrlButton = getExistingUrlButton(); expect(existingUrlButton).toBeInTheDocument(); + expect(existingUrlButton).toHaveTextContent(existingUrl); }); it('shows "invalid url" error message by default if existing url is validated as invalid url', () => { @@ -141,7 +142,7 @@ describe('ExternalImage', () => { // Entering invalid url await inputUrlInField(user, invalidUrl); // Entering valid url - const viewModeUrlButton = getExistingUrlButton(invalidUrl); + const viewModeUrlButton = getExistingUrlButton(); await user.click(viewModeUrlButton); await inputUrlInField(user, validImageUrl); expect(onUrlChangeMock).toHaveBeenCalled(); @@ -152,15 +153,16 @@ describe('ExternalImage', () => { const validUrl = 'someValidUrl'; renderExternalImage(); await inputUrlInField(user, validUrl); - const existingUrlButton = getExistingUrlButton(validUrl); + const existingUrlButton = getExistingUrlButton(); expect(existingUrlButton).toBeInTheDocument(); + expect(existingUrlButton).toHaveTextContent(validUrl); }); it('calls onUrlDelete when entering an empty url if there was an original url', async () => { const user = userEvent.setup(); const existingUrl = 'someExistingUrl'; renderExternalImage({ existingImageUrl: existingUrl }); - const viewModeUrlButton = getExistingUrlButton(existingUrl); + const viewModeUrlButton = getExistingUrlButton(); await user.click(viewModeUrlButton); await inputUrlInField(user, undefined); expect(onUrlDeleteMock).toHaveBeenCalledTimes(1); @@ -178,7 +180,7 @@ describe('ExternalImage', () => { const user = userEvent.setup(); const existingUrl = 'someExistingUrl'; renderExternalImage({ existingImageUrl: existingUrl }); - const viewModeUrlButton = getExistingUrlButton(existingUrl); + const viewModeUrlButton = getExistingUrlButton(); await user.click(viewModeUrlButton); await inputUrlInField(user, existingUrl); expect(onUrlDeleteMock).not.toHaveBeenCalled(); @@ -188,12 +190,10 @@ describe('ExternalImage', () => { const user = userEvent.setup(); renderExternalImage(); await inputUrlInField(user, undefined); - const enterUrlButton = getEnterUrlWithPlaceholderButton(); - expect(enterUrlButton).toBeInTheDocument(); - const emptyUrlPlaceholder = screen.getByText( + const enterUrlButton = getExistingUrlButton(); + expect(enterUrlButton).toHaveTextContent( textMock('ux_editor.properties_panel.images.external_url_not_added'), ); - expect(emptyUrlPlaceholder).toBeInTheDocument(); }); it('should show error if validation failed', async () => { @@ -224,11 +224,9 @@ const getInvalidUrlErrorMessage = () => const getNotAnImageErrorMessage = () => screen.getByText(textMock('ux_editor.properties_panel.images.invalid_external_url_not_an_image')); -const getExistingUrlButton = (url: string) => screen.getByRole('button', { name: url }); - -const getEnterUrlWithPlaceholderButton = () => +const getExistingUrlButton = () => screen.getByRole('button', { - name: textMock('ux_editor.properties_panel.images.external_url_not_added'), + name: textMock('ux_editor.properties_panel.images.enter_external_url'), }); const inputUrlInField = async (user: UserEvent, url: string) => { diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx index 3bea5867fc0..6889ffe236b 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditImage/ExternalImage/ExternalImage.tsx @@ -9,7 +9,7 @@ import { ConflictingImageSourceAlert } from '../ConflictingImageSourceAlert'; import { ExternalImageValidationStatus } from './ExternalImageValidationStatus'; export interface ExternalImageProps { - existingImageUrl: string; + existingImageUrl: string | undefined; onUrlChange: (url: string) => void; onUrlDelete: () => void; imageOriginsFromLibrary: boolean; @@ -22,7 +22,7 @@ export const ExternalImage = ({ imageOriginsFromLibrary, }: ExternalImageProps) => { const { org, app } = useStudioEnvironmentParams(); - const [url, setUrl] = useState(existingImageUrl); + const [url, setUrl] = useState(existingImageUrl); const { data: validationResult, status: validationStatus } = useValidateImageExternalUrlQuery( org, app, @@ -66,8 +66,8 @@ export const ExternalImage = ({ }; type EditUrlProps = { - url: string; - existingImageUrl: string; + url: string | undefined; + existingImageUrl: string | undefined; onBlur: (event: React.ChangeEvent) => void; }; @@ -81,14 +81,14 @@ const EditUrl = ({ url, existingImageUrl, onBlur }: EditUrlProps): React.ReactEl return isInitialUrlProvided(url, existingImageUrl) ? ( } label={label} onBlur={onBlur} title={url} value={value} /> ) : ( - + } /> ); }; diff --git a/frontend/packages/ux-editor/src/containers/DesignView/AddItem/ItemInfo/ItemInfo.tsx b/frontend/packages/ux-editor/src/containers/DesignView/AddItem/ItemInfo/ItemInfo.tsx index 42f8372ae90..f8a333fc05f 100644 --- a/frontend/packages/ux-editor/src/containers/DesignView/AddItem/ItemInfo/ItemInfo.tsx +++ b/frontend/packages/ux-editor/src/containers/DesignView/AddItem/ItemInfo/ItemInfo.tsx @@ -55,7 +55,7 @@ export const ItemInfo = ({ item, onAddItem, onCancel, setItem }: ItemInfoProps) description={t('ux_editor.add_item.component_info_generated_id_description')} > } label={t('Komponent ID')} value={item.componentId} onChange={(event: any) => { From 949c55ed818d58daeac6acba7f79b79193698aac Mon Sep 17 00:00:00 2001 From: andreastanderen Date: Fri, 17 Jan 2025 11:40:27 +0100 Subject: [PATCH 7/8] clear queryClient between every test case in editCompnentIdRow --- .../EditComponentIdRow/EditComponentIdRow.test.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.test.tsx b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.test.tsx index 82134d1e5c0..972147ee0d2 100644 --- a/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.test.tsx +++ b/frontend/packages/ux-editor/src/components/Properties/PropertiesHeader/EditComponentIdRow/EditComponentIdRow.test.tsx @@ -6,7 +6,7 @@ import { EditComponentIdRow, type EditComponentIdRowProps } from './EditComponen import userEvent from '@testing-library/user-event'; import { ComponentType } from 'app-shared/types/ComponentType'; import { textMock } from '@studio/testing/mocks/i18nMock'; -import { queryClientMock } from 'app-shared/mocks/queryClientMock'; +import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; import type { IFormLayouts } from '../../../../types/global'; import { QueryKey } from 'app-shared/types/QueryKey'; import { layout1NameMock, layoutMock } from '@altinn/ux-editor/testing/layoutMock'; @@ -18,9 +18,13 @@ const layoutSetName = layoutSet1NameMock; const layouts: IFormLayouts = { [layout1NameMock]: layoutMock, }; +const queryClient = createQueryClientMock(); describe('EditComponentIdRow', () => { - beforeEach(jest.clearAllMocks); + beforeEach(() => { + jest.clearAllMocks(); + queryClient.clear(); + }); it('should render button ', async () => { await renderEditComponentIdRow(); @@ -113,7 +117,7 @@ describe('EditComponentIdRow', () => { it('should show error message when id of an attachment component type has duplicate id', async () => { const user = userEvent.setup(); const idOccupiedByDataType = 'idOccupiedByDataType'; - queryClientMock.setQueryData([QueryKey.AppMetadata, org, app], { + queryClient.setQueryData([QueryKey.AppMetadata, org, app], { dataTypes: [{ id: idOccupiedByDataType }], }); await renderEditComponentIdRow({ @@ -146,6 +150,6 @@ const defaultProps: EditComponentIdRowProps = { const renderEditComponentIdRow = async ( props: Partial = {}, ): Promise => { - queryClientMock.setQueryData([QueryKey.FormLayouts, org, app, layoutSetName], layouts); - return renderWithProviders(); + queryClient.setQueryData([QueryKey.FormLayouts, org, app, layoutSetName], layouts); + return renderWithProviders(, { queryClient }); }; From e446316e7229365ad74d39724ed4411fdea7b52a Mon Sep 17 00:00:00 2001 From: andreastanderen Date: Fri, 17 Jan 2025 11:46:37 +0100 Subject: [PATCH 8/8] fix merge --- .../pages/CodeListPage/CodeListPage.test.tsx | 12 ++++++------ .../ProcessEditorPage/CustomReceiptConfig.ts | 4 ++-- .../ProcessEditorPage/ProcessEditorPage.ts | 18 +++++++++++++----- .../testing/playwright/pages/UiEditorPage.ts | 4 +++- .../process-editor/process-editor.spec.ts | 13 ++++--------- 5 files changed, 28 insertions(+), 23 deletions(-) diff --git a/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeListPage.test.tsx b/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeListPage.test.tsx index 2b0e67cc799..deb146a4298 100644 --- a/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeListPage.test.tsx +++ b/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeListPage.test.tsx @@ -54,7 +54,7 @@ describe('CodeListPage', () => { it('renders the code list accordion', () => { renderCodeListPage(); - const codeListAccordion = getCodeListAccordion(codeListName); + const codeListAccordion = getCodeListAccordion(codeListTitle); expect(codeListAccordion).toBeInTheDocument(); }); @@ -67,8 +67,8 @@ describe('CodeListPage', () => { }); const searchInput = screen.getByRole('searchbox'); await user.type(searchInput, codeListsSearchParam); - [codeListName, codeList2].forEach((codeListTitle) => { - expect(getCodeListAccordion(codeListTitle)).toBeInTheDocument(); + [codeListTitle, codeList2].forEach((title) => { + expect(getCodeListAccordion(title)).toBeInTheDocument(); }); }); @@ -85,7 +85,7 @@ describe('CodeListPage', () => { expect( screen.queryByTitle( textMock('app_content_library.code_lists.code_list_accordion_title', { - codeListTitle: codeListName, + codeListTitle, }), ), ).not.toBeInTheDocument(); @@ -173,10 +173,10 @@ const uploadCodeList = async (user: UserEvent, fileName: string = uploadedCodeLi await user.upload(fileUploaderButton, file); }; -const getCodeListAccordion = (codeListTitle: string) => { +const getCodeListAccordion = (title: string) => { return screen.getByTitle( textMock('app_content_library.code_lists.code_list_accordion_title', { - codeListTitle, + codeListTitle: title, }), ); }; diff --git a/frontend/testing/playwright/pages/ProcessEditorPage/CustomReceiptConfig.ts b/frontend/testing/playwright/pages/ProcessEditorPage/CustomReceiptConfig.ts index 2580ca8361c..81bb80b41b1 100644 --- a/frontend/testing/playwright/pages/ProcessEditorPage/CustomReceiptConfig.ts +++ b/frontend/testing/playwright/pages/ProcessEditorPage/CustomReceiptConfig.ts @@ -55,9 +55,9 @@ export class CustomReceiptConfig extends BasePage { .click(); } - public async waitForEditLayoutSetIdButtonToBeVisible(layoutSetId: string): Promise { + public async waitForEditLayoutSetIdButtonToBeVisible(): Promise { const button = this.page.getByRole('button', { - name: layoutSetId, + name: this.textMock('process_editor.configuration_panel_custom_receipt_textfield_label'), }); await expect(button).toBeVisible(); } diff --git a/frontend/testing/playwright/pages/ProcessEditorPage/ProcessEditorPage.ts b/frontend/testing/playwright/pages/ProcessEditorPage/ProcessEditorPage.ts index 85215226d68..fbf18ad8d88 100644 --- a/frontend/testing/playwright/pages/ProcessEditorPage/ProcessEditorPage.ts +++ b/frontend/testing/playwright/pages/ProcessEditorPage/ProcessEditorPage.ts @@ -73,13 +73,19 @@ export class ProcessEditorPage extends BasePage { } public async getTaskIdFromOpenNewlyAddedTask(): Promise { - const button = this.page.getByRole('button', { name: /^Activity_/ }); + const button = this.page.getByRole('button', { + name: this.textMock('process_editor.configuration_panel_change_task_id_label'), + }); await button.waitFor(); return await this.getFullIdFromButtonSelector(button); } - public async clickOnTaskIdEditButton(id: string): Promise { - await this.page.getByRole('button', { name: id }).click(); + public async clickOnTaskIdEditButton(): Promise { + await this.page + .getByRole('button', { + name: this.textMock('process_editor.configuration_panel_change_task_id_label'), + }) + .click(); } public async waitForEditIdInputFieldToBeVisible(): Promise { @@ -120,8 +126,10 @@ export class ProcessEditorPage extends BasePage { .blur(); } - public async waitForNewTaskIdButtonToBeVisible(id: string): Promise { - const button = this.page.getByRole('button', { name: id }); + public async waitForNewTaskIdButtonToBeVisible(): Promise { + const button = this.page.getByRole('button', { + name: this.textMock('process_editor.configuration_panel_change_task_id_label'), + }); await expect(button).toBeVisible(); } diff --git a/frontend/testing/playwright/pages/UiEditorPage.ts b/frontend/testing/playwright/pages/UiEditorPage.ts index 7294542f91f..eec817039b5 100644 --- a/frontend/testing/playwright/pages/UiEditorPage.ts +++ b/frontend/testing/playwright/pages/UiEditorPage.ts @@ -232,7 +232,9 @@ export class UiEditorPage extends BasePage { } public async deleteOldComponentId(): Promise { - const button = this.page.getByRole('button', { name: /^Input-/ }); + const button = this.page.getByRole('button', { + name: this.textMock('ux_editor.modal_properties_component_change_id'), + }); await button.click(); await this.page .getByLabel(this.textMock('ux_editor.modal_properties_component_change_id')) diff --git a/frontend/testing/playwright/tests/process-editor/process-editor.spec.ts b/frontend/testing/playwright/tests/process-editor/process-editor.spec.ts index 614b75e4656..f1c2ed91bae 100644 --- a/frontend/testing/playwright/tests/process-editor/process-editor.spec.ts +++ b/frontend/testing/playwright/tests/process-editor/process-editor.spec.ts @@ -133,10 +133,8 @@ test('that the user can edit the id of a task and add data-types to sign', async const signingTask = await addNewSigningTaskToProcessEditor(page); - const randomGeneratedId = await processEditorPage.getTaskIdFromOpenNewlyAddedTask(); - const newId: string = 'signing_id'; - await editRandomGeneratedId(processEditorPage, randomGeneratedId, newId); + await editRandomGeneratedId(processEditorPage, newId); await processEditorPage.signingTaskConfig.clickDataTypesToSignCombobox(); const dataTypeToSign: string = 'ref-data-as-pdf'; @@ -184,9 +182,7 @@ test('That it is possible to create a custom receipt', async ({ page, testAppNam await processEditorPage.customReceiptConfig.waitForSaveNewCustomReceiptButtonToBeVisible(); await processEditorPage.customReceiptConfig.clickOnSaveNewCustomReceiptButton(); - await processEditorPage.customReceiptConfig.waitForEditLayoutSetIdButtonToBeVisible( - newLayoutSetId, - ); + await processEditorPage.customReceiptConfig.waitForEditLayoutSetIdButtonToBeVisible(); // --------------------- Check that files are uploaded to Gitea --------------------- await goToGiteaAndNavigateToApplicationMetadataFile(header, giteaPage); @@ -225,16 +221,15 @@ const addNewSigningTaskToProcessEditor = async (page: Page): Promise => const editRandomGeneratedId = async ( processEditorPage: ProcessEditorPage, - randomGeneratedId: string, newId: string, ): Promise => { - await processEditorPage.clickOnTaskIdEditButton(randomGeneratedId); + await processEditorPage.clickOnTaskIdEditButton(); await processEditorPage.waitForEditIdInputFieldToBeVisible(); await processEditorPage.emptyIdTextfield(); await processEditorPage.writeNewId(newId); await processEditorPage.waitForTextBoxToHaveValue(newId); await processEditorPage.saveNewId(); - await processEditorPage.waitForNewTaskIdButtonToBeVisible(newId); + await processEditorPage.waitForNewTaskIdButtonToBeVisible(); }; const goToGiteaAndNavigateToProcessBpmnFile = async (