Repository: Altinn/altinn-studio
Pull request #14952: refactor: Split tests for OptionListEditor and move into child components (1)
Author: login: Konrad-Simso
Labels: name: area/ui-editor
description: Area: Related to the designer tool for assembling app UI in Altinn Studio.
name: quality/testing
description: Tests that are missing, needs to be created or could be improved.
name: frontend
name: team/studio-domain1
name: skip-manual-testing
description: Deprecated: Use skip-second-approval instead.
Created: 2025-03-12T11:26:51Z | Merged: 2025-03-14T13:12:02Z

## Description
This PR refactors tests which were in the parent component `OptionListEditor`. New changes are coming to both child components, and therefore it's good to split up these tests. It was a mistake to use a common test file to begin with.

## Related Issue
- #14835

## Verification
- [x] **Your** code builds clean without any errors or warnings
- [x] Manual testing done (required)
- [x] Relevant automated test added (if you find this hard, leave it and we'll help out)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **Tests**
  - Added comprehensive test suites for the `LibraryOptionsEditor` and `ManualOptionsEditor` components, ensuring smooth interactions like opening and closing dialogs, editing entries, and handling deletions.
  - Restructured tests in the `OptionListEditor` component to focus on essential functionality and improve clarity.

- **Refactor**
  - Updated exported type declarations for `LibraryOptionsEditorProps` and `ManualOptionsEditorProps` to enhance accessibility and maintainability.
  
- **Chore**
  - Streamlined test cases and rendering logic for a more consistent and reliable option editing experience.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/LibraryOptionsEditor/LibraryOptionsEditor.test.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/LibraryOptionsEditor/LibraryOptionsEditor.test.tsx
new file mode 100644
index 00000000000..b16e9c7f1b8
--- /dev/null
+++ b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/LibraryOptionsEditor/LibraryOptionsEditor.test.tsx
@@ -0,0 +1,154 @@
+import React from 'react';
+import { screen, waitFor } from '@testing-library/react';
+import { app, org } from '@studio/testing/testids';
+import { componentMocks } from '../../../../../../../../testing/componentMocks';
+import { queriesMock } from 'app-shared/mocks/queriesMock';
+import { textMock } from '@studio/testing/mocks/i18nMock';
+import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
+import { renderWithProviders } from '../../../../../../../../testing/mocks';
+import { ComponentType } from 'app-shared/types/ComponentType';
+import { ObjectUtils } from '@studio/pure-functions';
+import { QueryKey } from 'app-shared/types/QueryKey';
+import userEvent from '@testing-library/user-event';
+import type { OptionList } from 'app-shared/types/OptionList';
+import type { Option } from 'app-shared/types/Option';
+import type { QueryClient } from '@tanstack/react-query';
+import { LibraryOptionsEditor, type LibraryOptionsEditorProps } from './LibraryOptionsEditor';
+
+// Test data:
+const mockComponent = componentMocks[ComponentType.RadioButtons];
+const optionListId = 'someId';
+const componentWithOptionsId = { ...mockComponent, options: undefined, optionsId: optionListId };
+const handleDelete = jest.fn();
+const doReloadPreview = jest.fn();
+const optionList: OptionList = [
+  { value: 'value 1', label: 'label 1', description: 'description', helpText: 'help text' },
+  { value: 'value 2', label: 'label 2', description: null, helpText: null },
+  { value: 'value 3', label: '', description: null, helpText: null },
+];
+
+describe('LibraryOptionEditor', () => {
+  afterEach(jest.clearAllMocks);
+
+  it('should render the open Dialog button', async () => {
+    renderLibraryOptionsEditorWithData();
+    expect(getEditButton()).toBeInTheDocument();
+  });
+
+  it('should open Dialog', async () => {
+    const user = userEvent.setup();
+    renderLibraryOptionsEditorWithData();
+
+    await user.click(getEditButton());
+
+    expect(screen.getByRole('dialog')).toBeInTheDocument();
+    expect(
+      screen.getByText(textMock('ux_editor.options.modal_header_library_code_list')),
+    ).toBeInTheDocument();
+  });
+
+  it('should close Dialog', async () => {
+    const user = userEvent.setup();
+    renderLibraryOptionsEditorWithData();
+    await user.click(getEditButton());
+
+    expect(screen.getByRole('dialog')).toBeInTheDocument();
+    await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
+    expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+  });
+
+  it('should call doReloadPreview when editing', async () => {
+    const user = userEvent.setup();
+    renderLibraryOptionsEditorWithData();
+
+    await user.click(getEditButton());
+    const textBox = getDescriptionInput(2);
+    await user.type(textBox, 'test');
+    await user.tab();
+
+    await waitFor(() => expect(doReloadPreview).toHaveBeenCalledTimes(1));
+  });
+
+  it('should call updateOptionList with correct parameters when closing Dialog', async () => {
+    const user = userEvent.setup();
+    renderLibraryOptionsEditorWithData();
+    const expectedResultAfterEdit: Option[] = ObjectUtils.deepCopy(optionList);
+    expectedResultAfterEdit[1].description = 'test';
+
+    await user.click(getEditButton());
+    const textBox = getDescriptionInput(2);
+    await user.type(textBox, 'test');
+    await user.tab();
+
+    await waitFor(() => expect(queriesMock.updateOptionList).toHaveBeenCalledTimes(1));
+    expect(queriesMock.updateOptionList).toHaveBeenCalledWith(
+      org,
+      app,
+      componentWithOptionsId.optionsId,
+      expectedResultAfterEdit,
+    );
+  });
+
+  it('should show placeholder for option label when option list label is empty', async () => {
+    renderLibraryOptionsEditorWithData();
+
+    expect(
+      screen.getByText(textMock('general.empty_string'), { exact: false }),
+    ).toBeInTheDocument();
+  });
+
+  it('should call handleDelete when removing chosen options', async () => {
+    const user = userEvent.setup();
+    renderLibraryOptionsEditorWithData();
+
+    await user.click(getDeleteButton());
+
+    expect(handleDelete).toHaveBeenCalledTimes(1);
+  });
+});
+
+function getEditButton() {
+  return screen.getByRole('button', {
+    name: textMock('general.edit'),
+  });
+}
+
+function getDescriptionInput(number: number) {
+  return screen.getByRole('textbox', {
+    name: textMock('code_list_editor.description_item', { number }),
+  });
+}
+
+function getDeleteButton() {
+  return screen.getByRole('button', {
+    name: textMock('general.delete'),
+  });
+}
+
+const defaultProps: LibraryOptionsEditorProps = {
+  handleDelete: handleDelete,
+  optionListId: optionListId,
+};
+
+function renderLibraryOptionsEditor({
+  queries = {},
+  props = {},
+  queryClient = createQueryClientMock(),
+} = {}) {
+  renderWithProviders(<LibraryOptionsEditor {...defaultProps} {...props} />, {
+    queries,
+    queryClient,
+    previewContextProps: { doReloadPreview },
+  });
+}
+
+function renderLibraryOptionsEditorWithData({ queries = {}, props = {} } = {}) {
+  const queryClient = createQueryClientWithData();
+  renderLibraryOptionsEditor({ queries, props, queryClient });
+}
+
+function createQueryClientWithData(): QueryClient {
+  const queryClient = createQueryClientMock();
+  queryClient.setQueryData([QueryKey.OptionList, org, app, optionListId], optionList);
+  return queryClient;
+}
diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/LibraryOptionsEditor/LibraryOptionsEditor.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/LibraryOptionsEditor/LibraryOptionsEditor.tsx
index 975b4e8aff5..2005d388e73 100644
--- a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/LibraryOptionsEditor/LibraryOptionsEditor.tsx
+++ b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/LibraryOptionsEditor/LibraryOptionsEditor.tsx
@@ -13,7 +13,7 @@ import { useOptionListQuery } from 'app-shared/hooks/queries';
 import classes from './LibraryOptionsEditor.module.css';
 import type { OptionList } from 'app-shared/types/OptionList';
 
-type LibraryOptionsEditorProps = {
+export type LibraryOptionsEditorProps = {
   handleDelete: () => void;
   optionListId: string;
 };
diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/ManualOptionsEditor/ManualOptionsEditor.test.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/ManualOptionsEditor/ManualOptionsEditor.test.tsx
new file mode 100644
index 00000000000..cc9baf58dc3
--- /dev/null
+++ b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/ManualOptionsEditor/ManualOptionsEditor.test.tsx
@@ -0,0 +1,132 @@
+import React from 'react';
+import { screen } from '@testing-library/react';
+import { ComponentType } from 'app-shared/types/ComponentType';
+import { ObjectUtils } from '@studio/pure-functions';
+import { textMock } from '@studio/testing/mocks/i18nMock';
+import { renderWithProviders } from '../../../../../../../../testing/mocks';
+import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
+import userEvent from '@testing-library/user-event';
+import { componentMocks } from '../../../../../../../../testing/componentMocks';
+import { ManualOptionsEditor, type ManualOptionsEditorProps } from './ManualOptionsEditor';
+
+// Test data:
+const mockComponent = componentMocks[ComponentType.RadioButtons];
+const handleDelete = jest.fn();
+const handleComponentChange = jest.fn();
+
+describe('ManualOptionEditor', () => {
+  afterEach(jest.clearAllMocks);
+
+  it('should render the open Dialog button', () => {
+    renderManualOptionsEditor();
+    expect(getEditButton()).toBeInTheDocument();
+  });
+
+  it('should open Dialog', async () => {
+    const user = userEvent.setup();
+    renderManualOptionsEditor();
+
+    await user.click(getEditButton());
+
+    expect(screen.getByRole('dialog')).toBeInTheDocument();
+    expect(
+      screen.getByText(textMock('ux_editor.options.modal_header_manual_code_list')),
+    ).toBeInTheDocument();
+  });
+
+  it('should close Dialog', async () => {
+    const user = userEvent.setup();
+    renderManualOptionsEditor();
+    await user.click(getEditButton());
+
+    expect(screen.getByRole('dialog')).toBeInTheDocument();
+    await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
+    expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+  });
+
+  it('should call handleComponentChange with correct parameters when closing Dialog and options is empty', async () => {
+    const user = userEvent.setup();
+    renderManualOptionsEditor({
+      props: { component: { ...mockComponent, options: [], optionsId: undefined } },
+    });
+    const expectedArgs = ObjectUtils.deepCopy(mockComponent);
+    expectedArgs.options = undefined;
+    expectedArgs.optionsId = undefined;
+
+    await user.click(getEditButton());
+    await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
+
+    expect(handleComponentChange).toHaveBeenCalledTimes(1);
+    expect(handleComponentChange).toHaveBeenCalledWith(expectedArgs);
+  });
+
+  it('should call handleComponentChange with correct parameters when editing', async () => {
+    const user = userEvent.setup();
+    renderManualOptionsEditor();
+    const text = 'test';
+    const expectedArgs = ObjectUtils.deepCopy(mockComponent);
+    expectedArgs.optionsId = undefined;
+    expectedArgs.options[0].description = text;
+
+    await user.click(getEditButton());
+    const textBox = getDescriptionInput(1);
+    await user.type(textBox, text);
+    await user.tab();
+
+    expect(handleComponentChange).toHaveBeenCalledTimes(1);
+    expect(handleComponentChange).toHaveBeenCalledWith(expectedArgs);
+  });
+
+  it('should show placeholder for option label when option list label is empty', () => {
+    renderManualOptionsEditor({
+      props: {
+        component: {
+          ...mockComponent,
+          options: [{ value: 1, label: '' }],
+        },
+      },
+    });
+
+    expect(screen.getByText(textMock('general.empty_string'))).toBeInTheDocument();
+  });
+
+  it('should call handleDelete when removing chosen options', async () => {
+    const user = userEvent.setup();
+    renderManualOptionsEditor();
+
+    await user.click(getDeleteButton());
+
+    expect(handleDelete).toHaveBeenCalledTimes(1);
+  });
+});
+
+function getEditButton() {
+  return screen.getByRole('button', {
+    name: textMock('general.edit'),
+  });
+}
+
+function getDescriptionInput(number: number) {
+  return screen.getByRole('textbox', {
+    name: textMock('code_list_editor.description_item', { number }),
+  });
+}
+
+function getDeleteButton() {
+  return screen.getByRole('button', {
+    name: textMock('general.delete'),
+  });
+}
+
+const defaultProps: ManualOptionsEditorProps = {
+  handleDelete: handleDelete,
+  handleComponentChange: handleComponentChange,
+  component: mockComponent,
+};
+
+function renderManualOptionsEditor({ queries = {}, props = {} } = {}) {
+  renderWithProviders(<ManualOptionsEditor {...defaultProps} {...props} />, {
+    queries,
+    queryClient: createQueryClientMock(),
+  });
+}
diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/ManualOptionsEditor/ManualOptionsEditor.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/ManualOptionsEditor/ManualOptionsEditor.tsx
index 1b0f2596143..0fc2d3ac520 100644
--- a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/ManualOptionsEditor/ManualOptionsEditor.tsx
+++ b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/ManualOptionsEditor/ManualOptionsEditor.tsx
@@ -15,7 +15,7 @@ import { OptionListButtons } from '../OptionListButtons';
 import type { Option } from 'app-shared/types/Option';
 import classes from './ManualOptionsEditor.module.css';
 
-type ManualOptionsEditorProps = {
+export type ManualOptionsEditorProps = {
   handleDelete: () => void;
 } & Pick<IGenericEditComponent<SelectionComponentType>, 'component' | 'handleComponentChange'>;
 
diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/OptionListEditor.test.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/OptionListEditor.test.tsx
index 9b3d6feee94..0a06dad991b 100644
--- a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/OptionListEditor.test.tsx
+++ b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/OptionListEditor.test.tsx
@@ -1,298 +1,137 @@
 import React from 'react';
-import { screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
+import { screen, waitForElementToBeRemoved } from '@testing-library/react';
 import type { OptionList } from 'app-shared/types/OptionList';
-import type { Option } from 'app-shared/types/Option';
 import { ComponentType } from 'app-shared/types/ComponentType';
 import type { OptionListEditorProps } from './OptionListEditor';
-import { ObjectUtils } from '@studio/pure-functions';
 import { OptionListEditor } from './OptionListEditor';
 import { textMock } from '@studio/testing/mocks/i18nMock';
 import { renderWithProviders } from '../../../../../../../testing/mocks';
 import userEvent from '@testing-library/user-event';
 import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
-import { queriesMock } from 'app-shared/mocks/queriesMock';
 import { app, org } from '@studio/testing/testids';
 import { componentMocks } from '../../../../../../../testing/componentMocks';
+import type { QueryClient } from '@tanstack/react-query';
+import { QueryKey } from 'app-shared/types/QueryKey';
 
 // Test data:
 const mockComponent = componentMocks[ComponentType.RadioButtons];
+const optionListId = 'someId';
+const componentWithOptionsId = { ...mockComponent, options: undefined, optionsId: optionListId };
 const handleComponentChange = jest.fn();
-const apiResult: OptionList = [
+const optionList: OptionList = [
   { value: 'value 1', label: 'label 1', description: 'description', helpText: 'help text' },
   { value: 'value 2', label: 'label 2', description: null, helpText: null },
   { value: 'value 3', label: 'label 3', description: null, helpText: null },
 ];
-const getOptionListMock = jest
-  .fn()
-  .mockImplementation(() => Promise.resolve<OptionList>(apiResult));
-const componentWithOptionsId = { ...mockComponent, options: undefined, optionsId: 'some-id' };
 
 describe('OptionListEditor', () => {
-  afterEach(() => jest.clearAllMocks());
+  afterEach(jest.clearAllMocks);
 
-  describe('ManualOptionEditor', () => {
-    it('should render the open Dialog button', () => {
-      renderOptionListEditor();
-      expect(getOptionModalButton()).toBeInTheDocument();
-    });
-
-    it('should open Dialog', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-
-      await user.click(getOptionModalButton());
-
-      expect(screen.getByRole('dialog')).toBeInTheDocument();
-      expect(
-        screen.getByText(textMock('ux_editor.options.modal_header_manual_code_list')),
-      ).toBeInTheDocument();
-    });
-
-    it('should close Dialog', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-      await user.click(getOptionModalButton());
-
-      expect(screen.getByRole('dialog')).toBeInTheDocument();
-      await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
-      expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
-    });
-
-    it('should call handleComponentChange with correct parameters when closing Dialog and options is empty', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor({
-        props: { component: { ...mockComponent, options: [], optionsId: undefined } },
-      });
-      const expectedArgs = ObjectUtils.deepCopy(mockComponent);
-      expectedArgs.options = undefined;
-      expectedArgs.optionsId = undefined;
-
-      await user.click(getOptionModalButton());
-      await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
-
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-      expect(handleComponentChange).toHaveBeenCalledWith(expectedArgs);
-    });
-
-    it('should call handleComponentChange with correct parameters when editing', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-      const text = 'test';
-      const expectedArgs = ObjectUtils.deepCopy(mockComponent);
-      expectedArgs.optionsId = undefined;
-      expectedArgs.options[0].description = text;
-
-      await user.click(getOptionModalButton());
-      const textBox = getDescriptionInput(1);
-      await user.type(textBox, text);
-      await user.tab();
-
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-      expect(handleComponentChange).toHaveBeenCalledWith(expectedArgs);
-    });
-
-    it('should show placeholder for option label when option list label is empty', () => {
-      renderOptionListEditor({
-        props: {
-          component: {
-            ...mockComponent,
-            options: [{ value: 2, label: '', description: 'test', helpText: null }],
-          },
-        },
-      });
-
-      expect(screen.getByText(textMock('general.empty_string'))).toBeInTheDocument();
-    });
+  it('should render ManualOptionsEditor when component has options property', async () => {
+    const user = userEvent.setup();
+    renderOptionListEditor();
 
-    it('should call handleComponentChange with correct parameters when removing chosen options', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-      const expectedResult = ObjectUtils.deepCopy(mockComponent);
-      expectedResult.options = undefined;
-      expectedResult.optionsId = undefined;
+    await user.click(getEditButton());
 
-      await user.click(getDeleteButton());
-
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-      expect(handleComponentChange).toHaveBeenCalledWith(expectedResult);
-    });
+    expect(screen.getByRole('dialog')).toBeInTheDocument();
+    expect(
+      screen.getByText(textMock('ux_editor.options.modal_header_manual_code_list')),
+    ).toBeInTheDocument();
   });
 
-  describe('LibraryOptionEditor', () => {
-    it('should render a spinner when there is no data', () => {
-      renderOptionListEditor({
-        queries: {
-          getOptionList: jest.fn().mockImplementation(() => Promise.resolve<OptionList>([])),
-        },
-        props: { component: componentWithOptionsId },
-      });
-
-      expect(
-        screen.getByText(textMock('ux_editor.modal_properties_code_list_spinner_title')),
-      ).toBeInTheDocument();
-    });
+  it('should render LibraryOptionsEditor when component has optionId property', async () => {
+    const user = userEvent.setup();
+    renderOptionListEditorWithData();
 
-    it('should render an error message when getOptionList throws an error', async () => {
-      await renderOptionListEditorAndWaitForSpinnerToBeRemoved({
-        queries: {
-          getOptionList: jest.fn().mockImplementation(() => Promise.reject()),
-        },
-      });
-
-      expect(
-        screen.getByText(textMock('ux_editor.modal_properties_fetch_option_list_error_message')),
-      ).toBeInTheDocument();
-    });
-
-    it('should be able to remove binding to optionList if getOptionList throws an error', async () => {
-      const user = userEvent.setup();
-      await renderOptionListEditorAndWaitForSpinnerToBeRemoved({
-        queries: {
-          getOptionList: jest.fn().mockImplementation(() => Promise.reject()),
-        },
-      });
-
-      expect(getDeleteButton()).toBeInTheDocument();
-      await user.click(getDeleteButton());
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-    });
-
-    it('should render the open Dialog button', async () => {
-      await renderOptionListEditorAndWaitForSpinnerToBeRemoved();
-      expect(getOptionModalButton()).toBeInTheDocument();
-    });
+    await user.click(getEditButton());
 
-    it('should open Dialog', async () => {
-      const user = userEvent.setup();
-      await renderOptionListEditorAndWaitForSpinnerToBeRemoved();
-
-      await user.click(getOptionModalButton());
-
-      expect(screen.getByRole('dialog')).toBeInTheDocument();
-    });
-
-    it('should close Dialog', async () => {
-      const user = userEvent.setup();
-      await renderOptionListEditorAndWaitForSpinnerToBeRemoved();
-      await user.click(getOptionModalButton());
-
-      expect(screen.getByRole('dialog')).toBeInTheDocument();
-      await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
-      expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
-    });
-
-    it('should call doReloadPreview when editing', async () => {
-      const user = userEvent.setup();
-      const doReloadPreview = jest.fn();
-      await renderOptionListEditorAndWaitForSpinnerToBeRemoved({
-        previewContextProps: { doReloadPreview },
-      });
-
-      await user.click(getOptionModalButton());
-      const textBox = getDescriptionInput(2);
-      await user.type(textBox, 'test');
-      await user.tab();
+    expect(screen.getByRole('dialog')).toBeInTheDocument();
+    expect(
+      screen.getByText(textMock('ux_editor.options.modal_header_library_code_list')),
+    ).toBeInTheDocument();
+  });
 
-      await waitFor(() => expect(doReloadPreview).toHaveBeenCalledTimes(1));
+  it('should render a spinner when there is no data', () => {
+    renderOptionListEditor({
+      queries: {
+        getOptionList: jest.fn().mockImplementation(() => Promise.resolve<OptionList>([])),
+      },
+      props: { component: componentWithOptionsId },
     });
 
-    it('should call updateOptionList with correct parameters when closing Dialog', async () => {
-      const user = userEvent.setup();
-      await renderOptionListEditorAndWaitForSpinnerToBeRemoved();
-      const expectedResultAfterEdit: Option[] = [
-        { value: 'value 1', label: 'label 1', description: 'description', helpText: 'help text' },
-        { value: 'value 2', label: 'label 2', description: 'test', helpText: null },
-        { value: 'value 3', label: 'label 3', description: null, helpText: null },
-      ];
-
-      await user.click(getOptionModalButton());
-      const textBox = getDescriptionInput(2);
-      await user.type(textBox, 'test');
-      await user.tab();
+    expect(
+      screen.getByText(textMock('ux_editor.modal_properties_code_list_spinner_title')),
+    ).toBeInTheDocument();
+  });
 
-      await waitFor(() => expect(queriesMock.updateOptionList).toHaveBeenCalledTimes(1));
-      expect(queriesMock.updateOptionList).toHaveBeenCalledWith(
-        org,
-        app,
-        componentWithOptionsId.optionsId,
-        expectedResultAfterEdit,
-      );
+  it('should render an error message when getOptionList throws an error', async () => {
+    renderOptionListEditor({
+      queries: {
+        getOptionList: jest.fn().mockImplementation(() => Promise.reject()),
+      },
+      props: { component: componentWithOptionsId },
     });
+    await waitForElementToBeRemoved(() =>
+      screen.queryByText(textMock('ux_editor.modal_properties_code_list_spinner_title')),
+    );
 
-    it('should show placeholder for option label when option list label is empty', async () => {
-      const apiResultWithEmptyLabel: OptionList = [
-        { value: true, label: '', description: null, helpText: null },
-      ];
-      await renderOptionListEditorAndWaitForSpinnerToBeRemoved({
-        queries: {
-          getOptionList: jest
-            .fn()
-            .mockImplementation(() => Promise.resolve<OptionList>(apiResultWithEmptyLabel)),
-        },
-      });
+    expect(
+      screen.getByText(textMock('ux_editor.modal_properties_fetch_option_list_error_message')),
+    ).toBeInTheDocument();
+  });
 
-      expect(screen.getByText(textMock('general.empty_string'))).toBeInTheDocument();
+  it('should be able to remove binding to optionList if getOptionList throws an error', async () => {
+    const user = userEvent.setup();
+    renderOptionListEditorWithData({
+      queries: {
+        getOptionList: jest.fn().mockImplementation(() => Promise.reject()),
+      },
     });
 
-    it('should call handleComponentChange with correct parameters when removing chosen options', async () => {
-      const user = userEvent.setup();
-      await renderOptionListEditorAndWaitForSpinnerToBeRemoved();
-
-      await user.click(getDeleteButton());
-
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-      expect(handleComponentChange).toHaveBeenCalledWith({
-        ...mockComponent,
-        options: undefined,
-        optionsId: undefined,
-      });
-    });
+    expect(getDeleteButton()).toBeInTheDocument();
+    await user.click(getDeleteButton());
+    expect(handleComponentChange).toHaveBeenCalledTimes(1);
   });
 });
 
-function getOptionModalButton() {
+function getEditButton() {
   return screen.getByRole('button', {
     name: textMock('general.edit'),
   });
 }
 
-function getDescriptionInput(number: number) {
-  return screen.getByRole('textbox', {
-    name: textMock('code_list_editor.description_item', { number }),
-  });
-}
-
 function getDeleteButton() {
   return screen.getByRole('button', {
     name: textMock('general.delete'),
   });
 }
 
-const renderOptionListEditor = ({ previewContextProps = {}, queries = {}, props = {} } = {}) => {
-  const defaultProps: OptionListEditorProps = {
-    component: mockComponent,
-    handleComponentChange,
-  };
-
-  return renderWithProviders(<OptionListEditor {...defaultProps} {...props} />, {
-    queries: { getOptionList: getOptionListMock, ...queries },
-    queryClient: createQueryClientMock(),
-    previewContextProps,
-  });
+const defaultProps: OptionListEditorProps = {
+  component: mockComponent,
+  handleComponentChange,
 };
 
-const renderOptionListEditorAndWaitForSpinnerToBeRemoved = async ({
-  previewContextProps = {},
+function renderOptionListEditor({
   queries = {},
-  props = { component: componentWithOptionsId },
-} = {}) => {
-  const view = renderOptionListEditor({
-    previewContextProps,
+  props = {},
+  queryClient = createQueryClientMock(),
+} = {}) {
+  renderWithProviders(<OptionListEditor {...defaultProps} {...props} />, {
     queries,
-    props,
+    queryClient,
   });
-  await waitForElementToBeRemoved(() =>
-    screen.queryByText(textMock('ux_editor.modal_properties_code_list_spinner_title')),
-  );
-  return view;
-};
+}
+
+function renderOptionListEditorWithData({
+  queries = {},
+  props = { component: componentWithOptionsId },
+} = {}) {
+  const queryClient = createQueryClientWithData();
+  renderOptionListEditor({ queries, props, queryClient });
+}
+
+function createQueryClientWithData(): QueryClient {
+  const queryClient = createQueryClientMock();
+  queryClient.setQueryData([QueryKey.OptionList, org, app, optionListId], optionList);
+  return queryClient;
+}


Reviews:
user: login: ErlingHauan
body: Nice work!
I have a few comments below 😊 
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T13:06:46Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 236453665c5d122954491d8fc3738e2749fecdde and c715d219235c86f0f85e7c1b238328f596f8cac3.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/LibraryOptionsEditor/LibraryOptionsEditor.test.tsx` (1 hunks)
* `frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/ManualOptionsEditor/ManualOptionsEditor.test.tsx` (1 hunks)
* `frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/OptionListEditor.test.tsx` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/LibraryOptionsEditor/LibraryOptionsEditor.test.tsx

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (3)</summary>

* GitHub Check: Build environment and run e2e test
* GitHub Check: CodeQL
* GitHub Check: Testing

</details>

<details>
<summary>🔇 Additional comments (20)</summary><blockquote>

<details>
<summary>frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/ManualOptionsEditor/ManualOptionsEditor.test.tsx (7)</summary>

`1-10`: **Imports look good and complete.**

All necessary imports are included for testing the ManualOptionsEditor component. Good job importing the component type explicitly.

---

`12-16`: **Test data setup looks clean.**

Good approach using component mocks and setting up jest function mocks before tests.

---

`17-19`: **Proper test structure.**

The test structure follows best practices with a clear describe block and afterEach cleanup.

---

`43-43`: **TODO comment should be addressed.**

The TODO comment about replacing "close modal" with defaultDialogProps.closeButtonTitle is left in the code.

Is there a ticket to track this TODO? If not, consider creating one or addressing it in a follow-up PR.

---

`103-107`: **Helper function implementation follows best practices.**

The getEditButton function implementation is clean and follows the recommended approach from previous review comments.

---

`109-119`: **Good use of helper functions for test inputs.**

The helper functions for retrieving UI elements make the tests more readable and maintainable.

---

`121-132`: **Well-structured test helper function.**

The renderManualOptionsEditor function is well-designed, allowing for flexible test setup while providing sensible defaults.

</details>
<details>
<summary>frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/OptionListEditor.test.tsx (13)</summary>

`2-2`: **Appropriate imports added for enhanced functionality.**

Added imports for waitForElementToBeRemoved, QueryClient, and QueryKey support the new testing approach.



Also applies to: 13-14

---

`18-19`: **Good test data setup for different component states.**

Creating separate variables for optionListId and componentWithOptionsId makes the test intent clearer.

---

`21-25`: **Clear test data structure.**

The optionList variable provides a clean representation of the expected data structure.

---

`28-28`: **Simplified afterEach cleanup.**

Good simplification of the afterEach function to directly reference jest.clearAllMocks.

---

`30-40`: **Clear test for ManualOptionsEditor rendering.**

This test clearly verifies when the ManualOptionsEditor is rendered based on the component's options property.

---

`42-52`: **Clear test for LibraryOptionsEditor rendering.**

This test effectively verifies when the LibraryOptionsEditor is rendered based on the component's optionsId property.

---

`54-65`: **Proper loading state testing.**

Good coverage of the loading state with spinner verification.

---

`67-81`: **Effective error handling testing.**

This test properly verifies the error message display when data fetching fails.

---

`83-94`: **Good test for component interaction during error state.**

This test verifies that users can still remove binding to an option list even when there's a data fetching error.

---

`97-101`: **Helper function follows best practices.**

The getEditButton function implementation is clean and consistent with the ManualOptionsEditor tests.

---

`114-123`: **Enhanced rendering helper function.**

Good enhancement to the renderOptionListEditor function to accept a queryClient parameter, improving testing flexibility.

---

`125-131`: **Useful helper for data-populated tests.**

The renderOptionListEditorWithData function is a great addition that simplifies test setup for data-dependent scenarios.

---

`133-137`: **Well-structured query client factory.**

The createQueryClientWithData function provides a clean way to initialize test data, following good testing practices.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T13:34:02Z
user: login: ErlingHauan
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T13:38:39Z
user: login: Konrad-Simso
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T14:19:08Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T14:19:44Z
user: login: github-actions[bot]
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T13:11:57Z

Review comments:
diff_hunk: @@ -0,0 +1,135 @@
+import React from 'react';
+import { screen } from '@testing-library/react';
+import { ComponentType } from 'app-shared/types/ComponentType';
+import { ObjectUtils } from '@studio/pure-functions';
+import { textMock } from '@studio/testing/mocks/i18nMock';
+import { renderWithProviders } from '../../../../../../../../testing/mocks';
+import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
+import userEvent from '@testing-library/user-event';
+import { componentMocks } from '../../../../../../../../testing/componentMocks';
+import { ManualOptionsEditor, type ManualOptionsEditorProps } from './ManualOptionsEditor';
+
+// Test data:
+const mockComponent = componentMocks[ComponentType.RadioButtons];
+const handleDelete = jest.fn();
+const handleComponentChange = jest.fn();
+
+describe('OptionListEditor', () => {
+  afterEach(jest.clearAllMocks);
path: frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/ManualOptionsEditor/ManualOptionsEditor.test.tsx
user: login: ErlingHauan
body: The outer describe block can be removed, and `clearAllMocks` can be put right below `describe('ManualOptionEditor')`.
created_at: 2025-03-12T12:39:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,154 @@
+import React from 'react';
+import { screen, waitFor } from '@testing-library/react';
+import { app, org } from '@studio/testing/testids';
+import { componentMocks } from '../../../../../../../../testing/componentMocks';
+import { queriesMock } from 'app-shared/mocks/queriesMock';
+import { textMock } from '@studio/testing/mocks/i18nMock';
+import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
+import { renderWithProviders } from '../../../../../../../../testing/mocks';
+import { ComponentType } from 'app-shared/types/ComponentType';
+import { ObjectUtils } from '@studio/pure-functions';
+import { QueryKey } from 'app-shared/types/QueryKey';
+import userEvent from '@testing-library/user-event';
+import type { OptionList } from 'app-shared/types/OptionList';
+import type { Option } from 'app-shared/types/Option';
+import type { QueryClient } from '@tanstack/react-query';
+import { LibraryOptionsEditor, type LibraryOptionsEditorProps } from './LibraryOptionsEditor';
+
+// Test data:
+const mockComponent = componentMocks[ComponentType.RadioButtons];
+const optionListId = 'someId';
+const componentWithOptionsId = { ...mockComponent, options: undefined, optionsId: optionListId };
+const handleDelete = jest.fn();
+const doReloadPreview = jest.fn();
+const optionList: OptionList = [
+  { value: 'value 1', label: 'label 1', description: 'description', helpText: 'help text' },
+  { value: 'value 2', label: 'label 2', description: null, helpText: null },
+  { value: 'value 3', label: '', description: null, helpText: null },
+];
+
+describe('LibraryOptionEditor', () => {
+  afterEach(jest.clearAllMocks);
+
+  it('should render the open Dialog button', async () => {
+    renderLibraryOptionsEditorWithData();
+    expect(getOptionModalButton()).toBeInTheDocument();
+  });
+
+  it('should open Dialog', async () => {
+    const user = userEvent.setup();
+    renderLibraryOptionsEditorWithData();
+
+    await user.click(getOptionModalButton());
+
+    expect(screen.getByRole('dialog')).toBeInTheDocument();
+    expect(
+      screen.getByText(textMock('ux_editor.options.modal_header_library_code_list')),
+    ).toBeInTheDocument();
+  });
+
+  it('should close Dialog', async () => {
+    const user = userEvent.setup();
+    renderLibraryOptionsEditorWithData();
+    await user.click(getOptionModalButton());
+
+    expect(screen.getByRole('dialog')).toBeInTheDocument();
+    await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
+    expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+  });
+
+  it('should call doReloadPreview when editing', async () => {
+    const user = userEvent.setup();
+    renderLibraryOptionsEditorWithData();
+
+    await user.click(getOptionModalButton());
+    const textBox = getDescriptionInput(2);
+    await user.type(textBox, 'test');
+    await user.tab();
+
+    await waitFor(() => expect(doReloadPreview).toHaveBeenCalledTimes(1));
+  });
+
+  it('should call updateOptionList with correct parameters when closing Dialog', async () => {
+    const user = userEvent.setup();
+    renderLibraryOptionsEditorWithData();
+    const expectedResultAfterEdit: Option[] = ObjectUtils.deepCopy(optionList);
+    expectedResultAfterEdit[1].description = 'test';
+
+    await user.click(getOptionModalButton());
+    const textBox = getDescriptionInput(2);
+    await user.type(textBox, 'test');
+    await user.tab();
+
+    await waitFor(() => expect(queriesMock.updateOptionList).toHaveBeenCalledTimes(1));
+    expect(queriesMock.updateOptionList).toHaveBeenCalledWith(
+      org,
+      app,
+      componentWithOptionsId.optionsId,
+      expectedResultAfterEdit,
+    );
+  });
+
+  it('should show placeholder for option label when option list label is empty', async () => {
+    renderLibraryOptionsEditorWithData();
+
+    expect(
+      screen.getByText(textMock('general.empty_string'), { exact: false }),
+    ).toBeInTheDocument();
+  });
+
+  it('should call handleDelete when removing chosen options', async () => {
+    const user = userEvent.setup();
+    renderLibraryOptionsEditorWithData();
+
+    await user.click(getDeleteButton());
+
+    expect(handleDelete).toHaveBeenCalledTimes(1);
+  });
+});
+
+function getOptionModalButton() {
+  return screen.getByRole('button', {
+    name: textMock('general.edit'),
+  });
+}
path: frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/LibraryOptionsEditor/LibraryOptionsEditor.test.tsx
user: login: ErlingHauan
body: I think we can change the name of this function:
```suggestion
function getEditButton() {
  return screen.getByRole('button', {
    name: textMock('general.edit'),
  });
}
```

This can also be done in `ManualOptionsEditor` 🙂 
created_at: 2025-03-12T12:42:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,252 +1,101 @@
 import React from 'react';
-import { screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
+import { screen, waitForElementToBeRemoved } from '@testing-library/react';
 import type { OptionList } from 'app-shared/types/OptionList';
-import type { Option } from 'app-shared/types/Option';
 import { ComponentType } from 'app-shared/types/ComponentType';
 import type { OptionListEditorProps } from './OptionListEditor';
-import { ObjectUtils } from '@studio/pure-functions';
 import { OptionListEditor } from './OptionListEditor';
 import { textMock } from '@studio/testing/mocks/i18nMock';
 import { renderWithProviders } from '../../../../../../../testing/mocks';
 import userEvent from '@testing-library/user-event';
 import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
-import { queriesMock } from 'app-shared/mocks/queriesMock';
 import { app, org } from '@studio/testing/testids';
 import { componentMocks } from '../../../../../../../testing/componentMocks';
+import type { QueryClient } from '@tanstack/react-query';
+import { QueryKey } from 'app-shared/types/QueryKey';
 
 // Test data:
 const mockComponent = componentMocks[ComponentType.RadioButtons];
+const optionListId = 'someId';
+const componentWithOptionsId = { ...mockComponent, options: undefined, optionsId: optionListId };
 const handleComponentChange = jest.fn();
-const apiResult: OptionList = [
+const optionList: OptionList = [
   { value: 'value 1', label: 'label 1', description: 'description', helpText: 'help text' },
   { value: 'value 2', label: 'label 2', description: null, helpText: null },
   { value: 'value 3', label: 'label 3', description: null, helpText: null },
 ];
-const getOptionListMock = jest
-  .fn()
-  .mockImplementation(() => Promise.resolve<OptionList>(apiResult));
-const componentWithOptionsId = { ...mockComponent, options: undefined, optionsId: 'some-id' };
 
 describe('OptionListEditor', () => {
-  afterEach(() => jest.clearAllMocks());
+  afterEach(jest.clearAllMocks);
 
-  describe('ManualOptionEditor', () => {
-    it('should render the open Dialog button', () => {
-      renderOptionListEditor();
-      expect(getOptionModalButton()).toBeInTheDocument();
-    });
-
-    it('should open Dialog', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-
-      await user.click(getOptionModalButton());
-
-      expect(screen.getByRole('dialog')).toBeInTheDocument();
-      expect(
-        screen.getByText(textMock('ux_editor.options.modal_header_manual_code_list')),
-      ).toBeInTheDocument();
-    });
-
-    it('should close Dialog', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-      await user.click(getOptionModalButton());
-
-      expect(screen.getByRole('dialog')).toBeInTheDocument();
-      await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
-      expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
-    });
-
-    it('should call handleComponentChange with correct parameters when closing Dialog and options is empty', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor({
-        props: { component: { ...mockComponent, options: [], optionsId: undefined } },
-      });
-      const expectedArgs = ObjectUtils.deepCopy(mockComponent);
-      expectedArgs.options = undefined;
-      expectedArgs.optionsId = undefined;
-
-      await user.click(getOptionModalButton());
-      await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
-
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-      expect(handleComponentChange).toHaveBeenCalledWith(expectedArgs);
-    });
-
-    it('should call handleComponentChange with correct parameters when editing', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-      const text = 'test';
-      const expectedArgs = ObjectUtils.deepCopy(mockComponent);
-      expectedArgs.optionsId = undefined;
-      expectedArgs.options[0].description = text;
-
-      await user.click(getOptionModalButton());
-      const textBox = getDescriptionInput(1);
-      await user.type(textBox, text);
-      await user.tab();
-
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-      expect(handleComponentChange).toHaveBeenCalledWith(expectedArgs);
-    });
-
-    it('should show placeholder for option label when option list label is empty', () => {
-      renderOptionListEditor({
-        props: {
-          component: {
-            ...mockComponent,
-            options: [{ value: 2, label: '', description: 'test', helpText: null }],
-          },
-        },
-      });
-
-      expect(screen.getByText(textMock('general.empty_string'))).toBeInTheDocument();
-    });
-
-    it('should call handleComponentChange with correct parameters when removing chosen options', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-      const expectedResult = ObjectUtils.deepCopy(mockComponent);
-      expectedResult.options = undefined;
-      expectedResult.optionsId = undefined;
-
-      await user.click(getDeleteButton());
-
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-      expect(handleComponentChange).toHaveBeenCalledWith(expectedResult);
-    });
+  it('should render the open Dialog button', () => {
path: frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/OptionListEditor.test.tsx
user: login: ErlingHauan
body: This test can be removed, since this rendering is tested in `ManualOptionsEditor`.
created_at: 2025-03-12T12:53:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,252 +1,101 @@
 import React from 'react';
-import { screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
+import { screen, waitForElementToBeRemoved } from '@testing-library/react';
 import type { OptionList } from 'app-shared/types/OptionList';
-import type { Option } from 'app-shared/types/Option';
 import { ComponentType } from 'app-shared/types/ComponentType';
 import type { OptionListEditorProps } from './OptionListEditor';
-import { ObjectUtils } from '@studio/pure-functions';
 import { OptionListEditor } from './OptionListEditor';
 import { textMock } from '@studio/testing/mocks/i18nMock';
 import { renderWithProviders } from '../../../../../../../testing/mocks';
 import userEvent from '@testing-library/user-event';
 import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
-import { queriesMock } from 'app-shared/mocks/queriesMock';
 import { app, org } from '@studio/testing/testids';
 import { componentMocks } from '../../../../../../../testing/componentMocks';
+import type { QueryClient } from '@tanstack/react-query';
+import { QueryKey } from 'app-shared/types/QueryKey';
 
 // Test data:
 const mockComponent = componentMocks[ComponentType.RadioButtons];
+const optionListId = 'someId';
+const componentWithOptionsId = { ...mockComponent, options: undefined, optionsId: optionListId };
 const handleComponentChange = jest.fn();
-const apiResult: OptionList = [
+const optionList: OptionList = [
   { value: 'value 1', label: 'label 1', description: 'description', helpText: 'help text' },
   { value: 'value 2', label: 'label 2', description: null, helpText: null },
   { value: 'value 3', label: 'label 3', description: null, helpText: null },
 ];
-const getOptionListMock = jest
-  .fn()
-  .mockImplementation(() => Promise.resolve<OptionList>(apiResult));
-const componentWithOptionsId = { ...mockComponent, options: undefined, optionsId: 'some-id' };
 
 describe('OptionListEditor', () => {
-  afterEach(() => jest.clearAllMocks());
+  afterEach(jest.clearAllMocks);
 
-  describe('ManualOptionEditor', () => {
-    it('should render the open Dialog button', () => {
-      renderOptionListEditor();
-      expect(getOptionModalButton()).toBeInTheDocument();
-    });
-
-    it('should open Dialog', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-
-      await user.click(getOptionModalButton());
-
-      expect(screen.getByRole('dialog')).toBeInTheDocument();
-      expect(
-        screen.getByText(textMock('ux_editor.options.modal_header_manual_code_list')),
-      ).toBeInTheDocument();
-    });
-
-    it('should close Dialog', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-      await user.click(getOptionModalButton());
-
-      expect(screen.getByRole('dialog')).toBeInTheDocument();
-      await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
-      expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
-    });
-
-    it('should call handleComponentChange with correct parameters when closing Dialog and options is empty', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor({
-        props: { component: { ...mockComponent, options: [], optionsId: undefined } },
-      });
-      const expectedArgs = ObjectUtils.deepCopy(mockComponent);
-      expectedArgs.options = undefined;
-      expectedArgs.optionsId = undefined;
-
-      await user.click(getOptionModalButton());
-      await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
-
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-      expect(handleComponentChange).toHaveBeenCalledWith(expectedArgs);
-    });
-
-    it('should call handleComponentChange with correct parameters when editing', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-      const text = 'test';
-      const expectedArgs = ObjectUtils.deepCopy(mockComponent);
-      expectedArgs.optionsId = undefined;
-      expectedArgs.options[0].description = text;
-
-      await user.click(getOptionModalButton());
-      const textBox = getDescriptionInput(1);
-      await user.type(textBox, text);
-      await user.tab();
-
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-      expect(handleComponentChange).toHaveBeenCalledWith(expectedArgs);
-    });
-
-    it('should show placeholder for option label when option list label is empty', () => {
-      renderOptionListEditor({
-        props: {
-          component: {
-            ...mockComponent,
-            options: [{ value: 2, label: '', description: 'test', helpText: null }],
-          },
-        },
-      });
-
-      expect(screen.getByText(textMock('general.empty_string'))).toBeInTheDocument();
-    });
-
-    it('should call handleComponentChange with correct parameters when removing chosen options', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-      const expectedResult = ObjectUtils.deepCopy(mockComponent);
-      expectedResult.options = undefined;
-      expectedResult.optionsId = undefined;
-
-      await user.click(getDeleteButton());
-
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-      expect(handleComponentChange).toHaveBeenCalledWith(expectedResult);
-    });
+  it('should render the open Dialog button', () => {
+    renderOptionListEditor();
+    expect(getOptionModalButton()).toBeInTheDocument();
   });
 
-  describe('LibraryOptionEditor', () => {
-    it('should render a spinner when there is no data', () => {
-      renderOptionListEditor({
-        queries: {
-          getOptionList: jest.fn().mockImplementation(() => Promise.resolve<OptionList>([])),
-        },
-        props: { component: componentWithOptionsId },
-      });
+  it('should render ManualOptionsEditor', async () => {
path: frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/OptionListEditor.test.tsx
user: login: ErlingHauan
body: Opportunity to further explain how the component works:

```suggestion
  it('should render ManualOptionsEditor when component has options property', async () => {
```
created_at: 2025-03-12T12:56:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,252 +1,101 @@
 import React from 'react';
-import { screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
+import { screen, waitForElementToBeRemoved } from '@testing-library/react';
 import type { OptionList } from 'app-shared/types/OptionList';
-import type { Option } from 'app-shared/types/Option';
 import { ComponentType } from 'app-shared/types/ComponentType';
 import type { OptionListEditorProps } from './OptionListEditor';
-import { ObjectUtils } from '@studio/pure-functions';
 import { OptionListEditor } from './OptionListEditor';
 import { textMock } from '@studio/testing/mocks/i18nMock';
 import { renderWithProviders } from '../../../../../../../testing/mocks';
 import userEvent from '@testing-library/user-event';
 import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
-import { queriesMock } from 'app-shared/mocks/queriesMock';
 import { app, org } from '@studio/testing/testids';
 import { componentMocks } from '../../../../../../../testing/componentMocks';
+import type { QueryClient } from '@tanstack/react-query';
+import { QueryKey } from 'app-shared/types/QueryKey';
 
 // Test data:
 const mockComponent = componentMocks[ComponentType.RadioButtons];
+const optionListId = 'someId';
+const componentWithOptionsId = { ...mockComponent, options: undefined, optionsId: optionListId };
 const handleComponentChange = jest.fn();
-const apiResult: OptionList = [
+const optionList: OptionList = [
   { value: 'value 1', label: 'label 1', description: 'description', helpText: 'help text' },
   { value: 'value 2', label: 'label 2', description: null, helpText: null },
   { value: 'value 3', label: 'label 3', description: null, helpText: null },
 ];
-const getOptionListMock = jest
-  .fn()
-  .mockImplementation(() => Promise.resolve<OptionList>(apiResult));
-const componentWithOptionsId = { ...mockComponent, options: undefined, optionsId: 'some-id' };
 
 describe('OptionListEditor', () => {
-  afterEach(() => jest.clearAllMocks());
+  afterEach(jest.clearAllMocks);
 
-  describe('ManualOptionEditor', () => {
-    it('should render the open Dialog button', () => {
-      renderOptionListEditor();
-      expect(getOptionModalButton()).toBeInTheDocument();
-    });
-
-    it('should open Dialog', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-
-      await user.click(getOptionModalButton());
-
-      expect(screen.getByRole('dialog')).toBeInTheDocument();
-      expect(
-        screen.getByText(textMock('ux_editor.options.modal_header_manual_code_list')),
-      ).toBeInTheDocument();
-    });
-
-    it('should close Dialog', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-      await user.click(getOptionModalButton());
-
-      expect(screen.getByRole('dialog')).toBeInTheDocument();
-      await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
-      expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
-    });
-
-    it('should call handleComponentChange with correct parameters when closing Dialog and options is empty', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor({
-        props: { component: { ...mockComponent, options: [], optionsId: undefined } },
-      });
-      const expectedArgs = ObjectUtils.deepCopy(mockComponent);
-      expectedArgs.options = undefined;
-      expectedArgs.optionsId = undefined;
-
-      await user.click(getOptionModalButton());
-      await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
-
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-      expect(handleComponentChange).toHaveBeenCalledWith(expectedArgs);
-    });
-
-    it('should call handleComponentChange with correct parameters when editing', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-      const text = 'test';
-      const expectedArgs = ObjectUtils.deepCopy(mockComponent);
-      expectedArgs.optionsId = undefined;
-      expectedArgs.options[0].description = text;
-
-      await user.click(getOptionModalButton());
-      const textBox = getDescriptionInput(1);
-      await user.type(textBox, text);
-      await user.tab();
-
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-      expect(handleComponentChange).toHaveBeenCalledWith(expectedArgs);
-    });
-
-    it('should show placeholder for option label when option list label is empty', () => {
-      renderOptionListEditor({
-        props: {
-          component: {
-            ...mockComponent,
-            options: [{ value: 2, label: '', description: 'test', helpText: null }],
-          },
-        },
-      });
-
-      expect(screen.getByText(textMock('general.empty_string'))).toBeInTheDocument();
-    });
-
-    it('should call handleComponentChange with correct parameters when removing chosen options', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-      const expectedResult = ObjectUtils.deepCopy(mockComponent);
-      expectedResult.options = undefined;
-      expectedResult.optionsId = undefined;
-
-      await user.click(getDeleteButton());
-
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-      expect(handleComponentChange).toHaveBeenCalledWith(expectedResult);
-    });
+  it('should render the open Dialog button', () => {
+    renderOptionListEditor();
+    expect(getOptionModalButton()).toBeInTheDocument();
   });
 
-  describe('LibraryOptionEditor', () => {
-    it('should render a spinner when there is no data', () => {
-      renderOptionListEditor({
-        queries: {
-          getOptionList: jest.fn().mockImplementation(() => Promise.resolve<OptionList>([])),
-        },
-        props: { component: componentWithOptionsId },
-      });
+  it('should render ManualOptionsEditor', async () => {
+    const user = userEvent.setup();
+    renderOptionListEditor();
 
-      expect(
-        screen.getByText(textMock('ux_editor.modal_properties_code_list_spinner_title')),
-      ).toBeInTheDocument();
-    });
-
-    it('should render an error message when getOptionList throws an error', async () => {
-      await renderOptionListEditorAndWaitForSpinnerToBeRemoved({
-        queries: {
-          getOptionList: jest.fn().mockImplementation(() => Promise.reject()),
-        },
-      });
-
-      expect(
-        screen.getByText(textMock('ux_editor.modal_properties_fetch_option_list_error_message')),
-      ).toBeInTheDocument();
-    });
-
-    it('should be able to remove binding to optionList if getOptionList throws an error', async () => {
-      const user = userEvent.setup();
-      await renderOptionListEditorAndWaitForSpinnerToBeRemoved({
-        queries: {
-          getOptionList: jest.fn().mockImplementation(() => Promise.reject()),
-        },
-      });
-
-      expect(getDeleteButton()).toBeInTheDocument();
-      await user.click(getDeleteButton());
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-    });
+    await user.click(getOptionModalButton());
 
-    it('should render the open Dialog button', async () => {
-      await renderOptionListEditorAndWaitForSpinnerToBeRemoved();
-      expect(getOptionModalButton()).toBeInTheDocument();
-    });
-
-    it('should open Dialog', async () => {
-      const user = userEvent.setup();
-      await renderOptionListEditorAndWaitForSpinnerToBeRemoved();
-
-      await user.click(getOptionModalButton());
-
-      expect(screen.getByRole('dialog')).toBeInTheDocument();
-    });
-
-    it('should close Dialog', async () => {
-      const user = userEvent.setup();
-      await renderOptionListEditorAndWaitForSpinnerToBeRemoved();
-      await user.click(getOptionModalButton());
+    expect(screen.getByRole('dialog')).toBeInTheDocument();
+    expect(
+      screen.getByText(textMock('ux_editor.options.modal_header_manual_code_list')),
+    ).toBeInTheDocument();
+  });
 
-      expect(screen.getByRole('dialog')).toBeInTheDocument();
-      await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
-      expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
-    });
+  it('should render LibraryOptionsEditor when data has loaded', async () => {
path: frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/OptionListEditor.test.tsx
user: login: ErlingHauan
body: Suggest highlighting the difference between `LibraryOptionsEditor` and `ManualOptionsEditor` here:

```suggestion
  it('should render LibraryOptionsEditor when component has optionId property', async () => {
```
created_at: 2025-03-12T12:59:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,252 +1,101 @@
 import React from 'react';
-import { screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
+import { screen, waitForElementToBeRemoved } from '@testing-library/react';
 import type { OptionList } from 'app-shared/types/OptionList';
-import type { Option } from 'app-shared/types/Option';
 import { ComponentType } from 'app-shared/types/ComponentType';
 import type { OptionListEditorProps } from './OptionListEditor';
-import { ObjectUtils } from '@studio/pure-functions';
 import { OptionListEditor } from './OptionListEditor';
 import { textMock } from '@studio/testing/mocks/i18nMock';
 import { renderWithProviders } from '../../../../../../../testing/mocks';
 import userEvent from '@testing-library/user-event';
 import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
-import { queriesMock } from 'app-shared/mocks/queriesMock';
 import { app, org } from '@studio/testing/testids';
 import { componentMocks } from '../../../../../../../testing/componentMocks';
+import type { QueryClient } from '@tanstack/react-query';
+import { QueryKey } from 'app-shared/types/QueryKey';
 
 // Test data:
 const mockComponent = componentMocks[ComponentType.RadioButtons];
+const optionListId = 'someId';
+const componentWithOptionsId = { ...mockComponent, options: undefined, optionsId: optionListId };
 const handleComponentChange = jest.fn();
-const apiResult: OptionList = [
+const optionList: OptionList = [
   { value: 'value 1', label: 'label 1', description: 'description', helpText: 'help text' },
   { value: 'value 2', label: 'label 2', description: null, helpText: null },
   { value: 'value 3', label: 'label 3', description: null, helpText: null },
 ];
-const getOptionListMock = jest
-  .fn()
-  .mockImplementation(() => Promise.resolve<OptionList>(apiResult));
-const componentWithOptionsId = { ...mockComponent, options: undefined, optionsId: 'some-id' };
 
 describe('OptionListEditor', () => {
-  afterEach(() => jest.clearAllMocks());
+  afterEach(jest.clearAllMocks);
 
-  describe('ManualOptionEditor', () => {
-    it('should render the open Dialog button', () => {
-      renderOptionListEditor();
-      expect(getOptionModalButton()).toBeInTheDocument();
-    });
-
-    it('should open Dialog', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-
-      await user.click(getOptionModalButton());
-
-      expect(screen.getByRole('dialog')).toBeInTheDocument();
-      expect(
-        screen.getByText(textMock('ux_editor.options.modal_header_manual_code_list')),
-      ).toBeInTheDocument();
-    });
-
-    it('should close Dialog', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-      await user.click(getOptionModalButton());
-
-      expect(screen.getByRole('dialog')).toBeInTheDocument();
-      await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
-      expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
-    });
-
-    it('should call handleComponentChange with correct parameters when closing Dialog and options is empty', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor({
-        props: { component: { ...mockComponent, options: [], optionsId: undefined } },
-      });
-      const expectedArgs = ObjectUtils.deepCopy(mockComponent);
-      expectedArgs.options = undefined;
-      expectedArgs.optionsId = undefined;
-
-      await user.click(getOptionModalButton());
-      await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
-
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-      expect(handleComponentChange).toHaveBeenCalledWith(expectedArgs);
-    });
-
-    it('should call handleComponentChange with correct parameters when editing', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-      const text = 'test';
-      const expectedArgs = ObjectUtils.deepCopy(mockComponent);
-      expectedArgs.optionsId = undefined;
-      expectedArgs.options[0].description = text;
-
-      await user.click(getOptionModalButton());
-      const textBox = getDescriptionInput(1);
-      await user.type(textBox, text);
-      await user.tab();
-
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-      expect(handleComponentChange).toHaveBeenCalledWith(expectedArgs);
-    });
-
-    it('should show placeholder for option label when option list label is empty', () => {
-      renderOptionListEditor({
-        props: {
-          component: {
-            ...mockComponent,
-            options: [{ value: 2, label: '', description: 'test', helpText: null }],
-          },
-        },
-      });
-
-      expect(screen.getByText(textMock('general.empty_string'))).toBeInTheDocument();
-    });
-
-    it('should call handleComponentChange with correct parameters when removing chosen options', async () => {
-      const user = userEvent.setup();
-      renderOptionListEditor();
-      const expectedResult = ObjectUtils.deepCopy(mockComponent);
-      expectedResult.options = undefined;
-      expectedResult.optionsId = undefined;
-
-      await user.click(getDeleteButton());
-
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-      expect(handleComponentChange).toHaveBeenCalledWith(expectedResult);
-    });
+  it('should render the open Dialog button', () => {
+    renderOptionListEditor();
+    expect(getOptionModalButton()).toBeInTheDocument();
   });
 
-  describe('LibraryOptionEditor', () => {
-    it('should render a spinner when there is no data', () => {
-      renderOptionListEditor({
-        queries: {
-          getOptionList: jest.fn().mockImplementation(() => Promise.resolve<OptionList>([])),
-        },
-        props: { component: componentWithOptionsId },
-      });
+  it('should render ManualOptionsEditor', async () => {
+    const user = userEvent.setup();
+    renderOptionListEditor();
 
-      expect(
-        screen.getByText(textMock('ux_editor.modal_properties_code_list_spinner_title')),
-      ).toBeInTheDocument();
-    });
-
-    it('should render an error message when getOptionList throws an error', async () => {
-      await renderOptionListEditorAndWaitForSpinnerToBeRemoved({
-        queries: {
-          getOptionList: jest.fn().mockImplementation(() => Promise.reject()),
-        },
-      });
-
-      expect(
-        screen.getByText(textMock('ux_editor.modal_properties_fetch_option_list_error_message')),
-      ).toBeInTheDocument();
-    });
-
-    it('should be able to remove binding to optionList if getOptionList throws an error', async () => {
-      const user = userEvent.setup();
-      await renderOptionListEditorAndWaitForSpinnerToBeRemoved({
-        queries: {
-          getOptionList: jest.fn().mockImplementation(() => Promise.reject()),
-        },
-      });
-
-      expect(getDeleteButton()).toBeInTheDocument();
-      await user.click(getDeleteButton());
-      expect(handleComponentChange).toHaveBeenCalledTimes(1);
-    });
+    await user.click(getOptionModalButton());
path: frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/OptionListEditor.test.tsx
user: login: ErlingHauan
body: Changing the function's name to `getEditButton` better matches the UI, making it easier to see what is what in the future:
```suggestion
    await user.click(getEditButton());
```
created_at: 2025-03-12T13:03:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,135 @@
+import React from 'react';
+import { screen } from '@testing-library/react';
+import { ComponentType } from 'app-shared/types/ComponentType';
+import { ObjectUtils } from '@studio/pure-functions';
+import { textMock } from '@studio/testing/mocks/i18nMock';
+import { renderWithProviders } from '../../../../../../../../testing/mocks';
+import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
+import userEvent from '@testing-library/user-event';
+import { componentMocks } from '../../../../../../../../testing/componentMocks';
+import { ManualOptionsEditor, type ManualOptionsEditorProps } from './ManualOptionsEditor';
+
+// Test data:
+const mockComponent = componentMocks[ComponentType.RadioButtons];
+const handleDelete = jest.fn();
+const handleComponentChange = jest.fn();
+
+describe('OptionListEditor', () => {
+  afterEach(jest.clearAllMocks);
+
+  describe('ManualOptionEditor', () => {
+    it('should render the open Dialog button', () => {
+      renderManualOptionsEditor();
+
+      expect(getOptionModalButton()).toBeInTheDocument();
+    });
+
+    it('should open Dialog', async () => {
+      const user = userEvent.setup();
+      renderManualOptionsEditor();
+
+      await user.click(getOptionModalButton());
+
+      expect(screen.getByRole('dialog')).toBeInTheDocument();
+      expect(
+        screen.getByText(textMock('ux_editor.options.modal_header_manual_code_list')),
+      ).toBeInTheDocument();
+    });
+
+    it('should close Dialog', async () => {
+      const user = userEvent.setup();
+      renderManualOptionsEditor();
+      await user.click(getOptionModalButton());
+
+      expect(screen.getByRole('dialog')).toBeInTheDocument();
+      await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
+      expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+    });
+
+    it('should call handleComponentChange with correct parameters when closing Dialog and options is empty', async () => {
+      const user = userEvent.setup();
+      renderManualOptionsEditor({
+        props: { component: { ...mockComponent, options: [], optionsId: undefined } },
+      });
+      const expectedArgs = ObjectUtils.deepCopy(mockComponent);
+      expectedArgs.options = undefined;
+      expectedArgs.optionsId = undefined;
+
+      await user.click(getOptionModalButton());
+      await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
+
+      expect(handleComponentChange).toHaveBeenCalledTimes(1);
+      expect(handleComponentChange).toHaveBeenCalledWith(expectedArgs);
+    });
+
+    it('should call handleComponentChange with correct parameters when editing', async () => {
+      const user = userEvent.setup();
+      renderManualOptionsEditor();
+      const text = 'test';
+      const expectedArgs = ObjectUtils.deepCopy(mockComponent);
+      expectedArgs.optionsId = undefined;
+      expectedArgs.options[0].description = text;
+
+      await user.click(getOptionModalButton());
+      const textBox = getDescriptionInput(1);
+      await user.type(textBox, text);
+      await user.tab();
+
+      expect(handleComponentChange).toHaveBeenCalledTimes(1);
+      expect(handleComponentChange).toHaveBeenCalledWith(expectedArgs);
+    });
+
+    it('should show placeholder for option label when option list label is empty', () => {
+      renderManualOptionsEditor({
+        props: {
+          component: {
+            ...mockComponent,
+            options: [{ value: 1, label: '' }],
+          },
+        },
+      });
+
+      expect(screen.getByText(textMock('general.empty_string'))).toBeInTheDocument();
+    });
+
+    it('should call handleDelete when removing chosen options', async () => {
+      const user = userEvent.setup();
+      renderManualOptionsEditor();
+
+      await user.click(getDeleteButton());
+
+      expect(handleDelete).toHaveBeenCalledTimes(1);
+    });
+  });
+});
+
+function getOptionModalButton() {
+  return screen.getByRole('button', {
+    name: textMock('general.edit'),
+  });
+}
path: frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/ManualOptionsEditor/ManualOptionsEditor.test.tsx
user: login: ErlingHauan
body: This might be more accurate:

```suggestion
function getEditButton() {
  return screen.getByRole('button', {
    name: textMock('general.edit'),
  });
}
```
created_at: 2025-03-12T13:05:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,132 @@
+import React from 'react';
+import { screen } from '@testing-library/react';
+import { ComponentType } from 'app-shared/types/ComponentType';
+import { ObjectUtils } from '@studio/pure-functions';
+import { textMock } from '@studio/testing/mocks/i18nMock';
+import { renderWithProviders } from '../../../../../../../../testing/mocks';
+import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
+import userEvent from '@testing-library/user-event';
+import { componentMocks } from '../../../../../../../../testing/componentMocks';
+import { ManualOptionsEditor, type ManualOptionsEditorProps } from './ManualOptionsEditor';
+
+// Test data:
+const mockComponent = componentMocks[ComponentType.RadioButtons];
+const handleDelete = jest.fn();
+const handleComponentChange = jest.fn();
+
+describe('ManualOptionEditor', () => {
+  afterEach(jest.clearAllMocks);
+
+  it('should render the open Dialog button', () => {
+    renderManualOptionsEditor();
+    expect(getEditButton()).toBeInTheDocument();
+  });
+
+  it('should open Dialog', async () => {
+    const user = userEvent.setup();
+    renderManualOptionsEditor();
+
+    await user.click(getEditButton());
+
+    expect(screen.getByRole('dialog')).toBeInTheDocument();
+    expect(
+      screen.getByText(textMock('ux_editor.options.modal_header_manual_code_list')),
+    ).toBeInTheDocument();
+  });
+
+  it('should close Dialog', async () => {
+    const user = userEvent.setup();
+    renderManualOptionsEditor();
+    await user.click(getEditButton());
+
+    expect(screen.getByRole('dialog')).toBeInTheDocument();
+    await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
+    expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+  });
+
+  it('should call handleComponentChange with correct parameters when closing Dialog and options is empty', async () => {
+    const user = userEvent.setup();
+    renderManualOptionsEditor({
+      props: { component: { ...mockComponent, options: [], optionsId: undefined } },
+    });
+    const expectedArgs = ObjectUtils.deepCopy(mockComponent);
+    expectedArgs.options = undefined;
+    expectedArgs.optionsId = undefined;
+
+    await user.click(getEditButton());
+    await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
path: frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/ManualOptionsEditor/ManualOptionsEditor.test.tsx
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**TODO comment repeated.**

The same TODO comment appears again. Consider consolidating these references to a single constant if they're used in multiple places.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if there are other instances of this TODO comment in the codebase
rg "Todo: Replace \"close modal\" with defaultDialogProps.closeButtonTitle" --type tsx
```

Length of output: 114

---

Below is a revised shell script that uses a file glob for TSX files. This will help us re-verify if there are additional occurrences of the TODO comment in the repository:




Please run this script so we can confirm whether the TODO comment is repeated elsewhere.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check for other instances of the TODO comment in *.tsx files using a file glob
rg 'Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle' -g "*.tsx"
```

Length of output: 1407

---



</details>

**Action Required: Consolidate Duplicate TODO Comments**

The same TODO comment regarding replacing `"close modal"` with `defaultDialogProps.closeButtonTitle` is found in several test files. In addition to its appearance in _ManualOptionsEditor.test.tsx_, it also appears in:

- `frontend/libs/studio-components/src/components/StudioModal/StudioModal.test.tsx`  
- `frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/LibraryOptionsEditor/LibraryOptionsEditor.test.tsx`  

To simplify maintenance, please refactor these occurrences by consolidating them into a single shared constant (or similar mechanism) if they represent the same functionality. This change will avoid redundancy and ease future updates.

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-12T13:34:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,132 @@
+import React from 'react';
+import { screen } from '@testing-library/react';
+import { ComponentType } from 'app-shared/types/ComponentType';
+import { ObjectUtils } from '@studio/pure-functions';
+import { textMock } from '@studio/testing/mocks/i18nMock';
+import { renderWithProviders } from '../../../../../../../../testing/mocks';
+import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
+import userEvent from '@testing-library/user-event';
+import { componentMocks } from '../../../../../../../../testing/componentMocks';
+import { ManualOptionsEditor, type ManualOptionsEditorProps } from './ManualOptionsEditor';
+
+// Test data:
+const mockComponent = componentMocks[ComponentType.RadioButtons];
+const handleDelete = jest.fn();
+const handleComponentChange = jest.fn();
+
+describe('ManualOptionEditor', () => {
+  afterEach(jest.clearAllMocks);
+
+  it('should render the open Dialog button', () => {
+    renderManualOptionsEditor();
+    expect(getEditButton()).toBeInTheDocument();
+  });
+
+  it('should open Dialog', async () => {
+    const user = userEvent.setup();
+    renderManualOptionsEditor();
+
+    await user.click(getEditButton());
+
+    expect(screen.getByRole('dialog')).toBeInTheDocument();
+    expect(
+      screen.getByText(textMock('ux_editor.options.modal_header_manual_code_list')),
+    ).toBeInTheDocument();
+  });
+
+  it('should close Dialog', async () => {
+    const user = userEvent.setup();
+    renderManualOptionsEditor();
+    await user.click(getEditButton());
+
+    expect(screen.getByRole('dialog')).toBeInTheDocument();
+    await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
+    expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+  });
+
+  it('should call handleComponentChange with correct parameters when closing Dialog and options is empty', async () => {
+    const user = userEvent.setup();
+    renderManualOptionsEditor({
+      props: { component: { ...mockComponent, options: [], optionsId: undefined } },
+    });
+    const expectedArgs = ObjectUtils.deepCopy(mockComponent);
+    expectedArgs.options = undefined;
+    expectedArgs.optionsId = undefined;
+
+    await user.click(getEditButton());
+    await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
path: frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/ManualOptionsEditor/ManualOptionsEditor.test.tsx
user: login: Konrad-Simso
body: Your suggestion will not be implemented as we want each test file to include it's own required test data, if not in the file itself, then in a common test data file. As stated in the Todo, it will be replaced once we upgrade to v1 of a package. This is currently being worked on.
created_at: 2025-03-12T14:19:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,132 @@
+import React from 'react';
+import { screen } from '@testing-library/react';
+import { ComponentType } from 'app-shared/types/ComponentType';
+import { ObjectUtils } from '@studio/pure-functions';
+import { textMock } from '@studio/testing/mocks/i18nMock';
+import { renderWithProviders } from '../../../../../../../../testing/mocks';
+import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
+import userEvent from '@testing-library/user-event';
+import { componentMocks } from '../../../../../../../../testing/componentMocks';
+import { ManualOptionsEditor, type ManualOptionsEditorProps } from './ManualOptionsEditor';
+
+// Test data:
+const mockComponent = componentMocks[ComponentType.RadioButtons];
+const handleDelete = jest.fn();
+const handleComponentChange = jest.fn();
+
+describe('ManualOptionEditor', () => {
+  afterEach(jest.clearAllMocks);
+
+  it('should render the open Dialog button', () => {
+    renderManualOptionsEditor();
+    expect(getEditButton()).toBeInTheDocument();
+  });
+
+  it('should open Dialog', async () => {
+    const user = userEvent.setup();
+    renderManualOptionsEditor();
+
+    await user.click(getEditButton());
+
+    expect(screen.getByRole('dialog')).toBeInTheDocument();
+    expect(
+      screen.getByText(textMock('ux_editor.options.modal_header_manual_code_list')),
+    ).toBeInTheDocument();
+  });
+
+  it('should close Dialog', async () => {
+    const user = userEvent.setup();
+    renderManualOptionsEditor();
+    await user.click(getEditButton());
+
+    expect(screen.getByRole('dialog')).toBeInTheDocument();
+    await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
+    expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+  });
+
+  it('should call handleComponentChange with correct parameters when closing Dialog and options is empty', async () => {
+    const user = userEvent.setup();
+    renderManualOptionsEditor({
+      props: { component: { ...mockComponent, options: [], optionsId: undefined } },
+    });
+    const expectedArgs = ObjectUtils.deepCopy(mockComponent);
+    expectedArgs.options = undefined;
+    expectedArgs.optionsId = undefined;
+
+    await user.click(getEditButton());
+    await user.click(screen.getByRole('button', { name: 'close modal' })); // Todo: Replace "close modal" with defaultDialogProps.closeButtonTitle when we upgrade to Designsystemet v1
path: frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/ManualOptionsEditor/ManualOptionsEditor.test.tsx
user: login: coderabbitai[bot]
body: @Konrad-Simso I understand your approach. Keeping each test file self-contained with its own test data makes sense, especially when the TODOs will be addressed during your upcoming package upgrade to Designsystemet v1. Thanks for explaining your testing strategy.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Konrad-Simso
PR: Altinn/altinn-studio#14952
File: frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/ManualOptionsEditor/ManualOptionsEditor.test.tsx:57-57
Timestamp: 2025-03-12T14:19:41.785Z
Learning: Each test file in the Altinn Studio project should include its own required test data, either in the file itself or in a common test data file. This is the preferred testing approach for maintaining independence and clarity of tests.
```

---

```
Learnt from: Konrad-Simso
PR: Altinn/altinn-studio#14952
File: frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/ManualOptionsEditor/ManualOptionsEditor.test.tsx:57-57
Timestamp: 2025-03-12T14:19:41.785Z
Learning: TODO comments in the codebase mentioning "Todo: Replace 'close modal' with defaultDialogProps.closeButtonTitle" will be addressed when upgrading to Designsystemet v1, which is currently being worked on.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-12T14:19:44Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-03-12T11:26:58Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

<details>
<summary>📝 Walkthrough</summary>

## Walkthrough
The pull request introduces new test files for the `LibraryOptionsEditor` and `ManualOptionsEditor` components, validating dialog interactions, option editing, and deletion using React Testing Library. Additionally, the type declarations for `LibraryOptionsEditorProps` and `ManualOptionsEditorProps` have been modified to be exported, allowing external access. The `OptionListEditor` test suite has been refactored to consolidate test cases, remove redundancies, and introduce utility functions for clearer test setup and rendering.

## Changes

| File | Change Summary |
| --- | --- |
| `frontend/.../LibraryOptionsEditor.test.tsx`, `frontend/.../ManualOptionsEditor.test.tsx` | Added new test suites using React Testing Library to validate dialog operations, option editing, placeholder handling, and deletion for their respective components. |
| `frontend/.../LibraryOptionsEditor/LibraryOptionsEditor.tsx`, `frontend/.../ManualOptionsEditor/ManualOptionsEditor.tsx` | Updated type declarations by exporting `LibraryOptionsEditorProps` and `ManualOptionsEditorProps` for external use. |
| `frontend/.../OptionListEditor.test.tsx` | Refactored tests by consolidating cases, removing redundant tests, and introducing new utility functions (`renderOptionListEditorWithData`, `createQueryClientWithData`) for streamlined rendering. |

## Suggested labels
`skip-releasenotes`, `skip-manual-testing`, `skip-documentation`

## Suggested reviewers
- github-actions
- ErlingHauan

</details>

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Altinn/altinn-studio&utm_content=14952):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: codecov[bot]
created_at: 2025-03-12T11:37:59Z
body: ## [Codecov](https://app.codecov.io/gh/Altinn/altinn-studio/pull/14952?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Altinn) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 95.85%. Comparing base [(`42a10b6`)](https://app.codecov.io/gh/Altinn/altinn-studio/commit/42a10b644de9aca55a6fdf6fee4c927ebcb7d2f8?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Altinn) to head [(`2364536`)](https://app.codecov.io/gh/Altinn/altinn-studio/commit/236453665c5d122954491d8fc3738e2749fecdde?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Altinn).
> Report is 1 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main   #14952   +/-   ##
=======================================
  Coverage   95.85%   95.85%           
=======================================
  Files        1959     1959           
  Lines       25546    25546           
  Branches     2888     2888           
=======================================
  Hits        24486    24486           
  Misses        799      799           
  Partials      261      261           
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/Altinn/altinn-studio/pull/14952?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Altinn).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Altinn).

<details><summary>🚀 New features to boost your workflow: </summary>

- ❄ [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- 📦 [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE