From d7477054c676d8b596f5bc599496ed7db272ad4a Mon Sep 17 00:00:00 2001 From: William Thorenfeldt <48119543+wrt95@users.noreply.github.com> Date: Sun, 19 Jan 2025 23:52:52 +0100 Subject: [PATCH 1/5] chore: 14277 studio pagination content (#14278) Co-authored-by: David Ovrelid <46874830+framitdavid@users.noreply.github.com> Co-authored-by: davidovrelid.com --- .../StudioPaginatedContent.module.css | 34 +++++++ .../StudioPaginatedContent.stories.tsx | 74 ++++++++++++++ .../StudioPaginatedContent.test.tsx | 98 +++++++++++++++++++ .../StudioPaginatedContent.tsx | 68 +++++++++++++ .../hooks/usePagination.test.tsx | 80 +++++++++++++++ .../hooks/usePagination.ts | 44 +++++++++ .../StudioPaginatedContent/index.ts | 1 + .../types/StudioPaginatedItem.ts | 6 ++ .../types/StudioPaginatedNavigation.ts | 6 ++ 9 files changed, 411 insertions(+) create mode 100644 frontend/libs/studio-components/src/components/StudioPaginatedContent/StudioPaginatedContent.module.css create mode 100644 frontend/libs/studio-components/src/components/StudioPaginatedContent/StudioPaginatedContent.stories.tsx create mode 100644 frontend/libs/studio-components/src/components/StudioPaginatedContent/StudioPaginatedContent.test.tsx create mode 100644 frontend/libs/studio-components/src/components/StudioPaginatedContent/StudioPaginatedContent.tsx create mode 100644 frontend/libs/studio-components/src/components/StudioPaginatedContent/hooks/usePagination.test.tsx create mode 100644 frontend/libs/studio-components/src/components/StudioPaginatedContent/hooks/usePagination.ts create mode 100644 frontend/libs/studio-components/src/components/StudioPaginatedContent/index.ts create mode 100644 frontend/libs/studio-components/src/components/StudioPaginatedContent/types/StudioPaginatedItem.ts create mode 100644 frontend/libs/studio-components/src/components/StudioPaginatedContent/types/StudioPaginatedNavigation.ts diff --git a/frontend/libs/studio-components/src/components/StudioPaginatedContent/StudioPaginatedContent.module.css b/frontend/libs/studio-components/src/components/StudioPaginatedContent/StudioPaginatedContent.module.css new file mode 100644 index 00000000000..6eb7f377974 --- /dev/null +++ b/frontend/libs/studio-components/src/components/StudioPaginatedContent/StudioPaginatedContent.module.css @@ -0,0 +1,34 @@ +.wrapper { + display: flex; + flex-direction: column; + align-items: center; +} + +.buttonWrapper { + display: flex; + gap: var(--fds-spacing-4); + margin-block: var(--fds-spacing-4); +} + +.statusBarContainer { + display: flex; + gap: 4px; + margin-top: 1rem; +} + +.statusBarPiece { + flex: 1; + height: 10px; + background: #e0e0e0; + border-radius: 50%; + width: 10px; + margin: 0; +} + +.statusBarPiece.active { + background: var(--fds-semantic-surface-action-first-default); +} + +.icon { + font-size: var(--fds-sizing-5); +} diff --git a/frontend/libs/studio-components/src/components/StudioPaginatedContent/StudioPaginatedContent.stories.tsx b/frontend/libs/studio-components/src/components/StudioPaginatedContent/StudioPaginatedContent.stories.tsx new file mode 100644 index 00000000000..646abcee233 --- /dev/null +++ b/frontend/libs/studio-components/src/components/StudioPaginatedContent/StudioPaginatedContent.stories.tsx @@ -0,0 +1,74 @@ +import React, { type ChangeEvent, useState } from 'react'; +import type { Meta, StoryFn } from '@storybook/react'; +import { StudioPaginatedContent } from './StudioPaginatedContent'; +import { StudioParagraph } from '../StudioParagraph'; +import { StudioTextfield } from '../StudioTextfield'; +import { usePagination } from './hooks/usePagination'; +import { type StudioPaginatedItem } from './types/StudioPaginatedItem'; + +type ChildrenProps = { + onChange: (event: ChangeEvent) => void; + value: string; +}; +const Children1 = ({ onChange, value }: ChildrenProps) => { + return ( +
+ Children 1 + +
+ ); +}; +const Children2 = () => Children 2; +const Children3 = () => Children 3; +const Children4 = () => Children 4; + +type Story = StoryFn; + +const meta: Meta = { + title: 'Components/StudioPaginatedContent', + component: StudioPaginatedContent, + argTypes: {}, +}; + +export const Preview: Story = () => { + const [inputValue, setInputValue] = useState(''); + + const handleInputChange = (event: React.ChangeEvent) => { + const value = event.target.value; + setInputValue(value); + }; + + const items: StudioPaginatedItem[] = [ + { + pageContent: , + validationRuleForNextButton: inputValue === '3', + }, + { + pageContent: , + }, + { + pageContent: , + }, + { + pageContent: , + }, + ]; + const { currentPage, pages, navigation } = usePagination(items); + + return ( + + ); +}; + +export default meta; diff --git a/frontend/libs/studio-components/src/components/StudioPaginatedContent/StudioPaginatedContent.test.tsx b/frontend/libs/studio-components/src/components/StudioPaginatedContent/StudioPaginatedContent.test.tsx new file mode 100644 index 00000000000..4a11b1b4bc9 --- /dev/null +++ b/frontend/libs/studio-components/src/components/StudioPaginatedContent/StudioPaginatedContent.test.tsx @@ -0,0 +1,98 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { StudioPaginatedContent, type StudioPaginatedContentProps } from './StudioPaginatedContent'; + +const navigationMock: StudioPaginatedContentProps['navigation'] = { + canGoNext: true, + canGoPrevious: true, + onNext: jest.fn(), + onPrevious: jest.fn(), +}; + +const buttonTextsMock: StudioPaginatedContentProps['navigationButtonTexts'] = { + previous: 'Previous', + next: 'Next', +}; + +const defaultProps: StudioPaginatedContentProps = { + totalPages: 5, + currentPageNumber: 2, + componentToRender:
Content
, + navigationButtonTexts: buttonTextsMock, + navigation: navigationMock, +}; + +describe('StudioPaginatedContent', () => { + it('renders the componentToRender', () => { + renderStudioPaginatedContent(); + expect(screen.getByText('Content')).toBeInTheDocument(); + }); + + it('renders the correct number of navigation circles', () => { + renderStudioPaginatedContent(); + + const circles = screen.getAllByRole('status'); + expect(circles.length).toBe(defaultProps.totalPages); + }); + + it('disables the previous button when canGoPrevious is false', () => { + renderStudioPaginatedContent({ + navigation: { ...navigationMock, canGoPrevious: false }, + }); + + expect(screen.getByText('Previous')).toBeDisabled(); + }); + + it('enables the next button when canGoNext is undefined', () => { + renderStudioPaginatedContent({ + navigation: { ...navigationMock, canGoNext: undefined }, + }); + + expect(screen.getByText('Next')).not.toBeDisabled(); + }); + + it('enables the previous button when canGoPrevious is undefined', () => { + renderStudioPaginatedContent({ + navigation: { ...navigationMock, canGoPrevious: undefined }, + }); + + expect(screen.getByText('Next')).not.toBeDisabled(); + }); + + it('disables the next button when canGoNext is false', () => { + renderStudioPaginatedContent({ + navigation: { ...navigationMock, canGoNext: false }, + }); + + expect(screen.getByText('Next')).toBeDisabled(); + }); + + it('calls onPrevious when the previous button is clicked', async () => { + const user = userEvent.setup(); + renderStudioPaginatedContent(); + + await user.click(screen.getByText('Previous')); + expect(defaultProps.navigation.onPrevious).toHaveBeenCalled(); + }); + + it('calls onNext when the next button is clicked', async () => { + const user = userEvent.setup(); + renderStudioPaginatedContent(); + + await user.click(screen.getByText('Next')); + expect(defaultProps.navigation.onNext).toHaveBeenCalled(); + }); + + it('highlights the correct navigation circle based on currentPageNumber', () => { + renderStudioPaginatedContent(); + const activeCircles = screen + .getAllByRole('status') + .filter((circle) => circle.classList.contains('active')); + expect(activeCircles.length).toBe(defaultProps.currentPageNumber + 1); + }); +}); + +const renderStudioPaginatedContent = (props: Partial = {}) => { + return render(); +}; diff --git a/frontend/libs/studio-components/src/components/StudioPaginatedContent/StudioPaginatedContent.tsx b/frontend/libs/studio-components/src/components/StudioPaginatedContent/StudioPaginatedContent.tsx new file mode 100644 index 00000000000..2b4b56ee83b --- /dev/null +++ b/frontend/libs/studio-components/src/components/StudioPaginatedContent/StudioPaginatedContent.tsx @@ -0,0 +1,68 @@ +import React, { type ReactNode, type ReactElement } from 'react'; +import classes from './StudioPaginatedContent.module.css'; +import { StudioButton } from '../StudioButton'; +import { ChevronLeftIcon, ChevronRightIcon } from '@studio/icons'; +import { type StudioPaginatedNavigation } from './types/StudioPaginatedNavigation'; + +type NavigationButtonTexts = { + previous: string; + next: string; +}; + +export type StudioPaginatedContentProps = { + totalPages: number; + currentPageNumber: number; + componentToRender: ReactNode; + navigationButtonTexts: NavigationButtonTexts; + navigation: StudioPaginatedNavigation; +}; + +export const StudioPaginatedContent = ({ + navigation: { canGoNext = true, canGoPrevious = true, onNext, onPrevious }, + totalPages, + componentToRender, + currentPageNumber, + navigationButtonTexts: { previous: previousButtonText, next: nextButtonText }, +}: StudioPaginatedContentProps): ReactElement => { + return ( +
+
{componentToRender}
+
+ + + {previousButtonText} + + + + {nextButtonText} + + +
+
+ ); +}; + +type NavigationCirclesProps = { + totalPages: number; + currentPageNumber: number; +}; + +const NavigationStepIndicator = ({ + totalPages, + currentPageNumber, +}: NavigationCirclesProps): React.ReactElement => { + return ( +
+ {getArrayFromLength(totalPages).map((_, index) => ( +
+ ))} +
+ ); +}; + +const getArrayFromLength = (length: number): number[] => + Array.from({ length }, (_, index) => index); diff --git a/frontend/libs/studio-components/src/components/StudioPaginatedContent/hooks/usePagination.test.tsx b/frontend/libs/studio-components/src/components/StudioPaginatedContent/hooks/usePagination.test.tsx new file mode 100644 index 00000000000..6ad78bfc8f4 --- /dev/null +++ b/frontend/libs/studio-components/src/components/StudioPaginatedContent/hooks/usePagination.test.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { renderHook, act } from '@testing-library/react'; +import { usePagination } from './usePagination'; +import { type StudioPaginatedItem } from '../types/StudioPaginatedItem'; + +const items: StudioPaginatedItem[] = [ + { pageContent:
Page 1
, validationRuleForNextButton: true }, + { pageContent:
Page 2
, validationRuleForNextButton: true }, + { pageContent:
Page 3
, validationRuleForNextButton: false }, +]; + +describe('usePagination', () => { + it('should initialize with the first page', () => { + const { result } = renderHook(() => usePagination(items)); + expect(result.current.currentPage).toBe(0); + expect(result.current.pages).toHaveLength(3); + expect(result.current.navigation.canGoNext).toBe(true); + expect(result.current.navigation.canGoPrevious).toBe(false); + }); + + it('should set canGoNext to true when validationRuleForNextButton is undefined', () => { + const itemsWithoutValidationRuleForNextButton: StudioPaginatedItem[] = [ + { pageContent:
Page 1
}, + { pageContent:
Page 2
}, + ]; + const { result } = renderHook(() => usePagination(itemsWithoutValidationRuleForNextButton)); + + expect(result.current.currentPage).toBe(0); + expect(result.current.pages).toHaveLength(2); + expect(result.current.navigation.canGoNext).toBe(true); + expect(result.current.navigation.canGoPrevious).toBe(false); + }); + + it('should go to the next page if validation rule allows', () => { + const { result } = renderHook(() => usePagination(items)); + act(() => { + result.current.navigation.onNext(); + }); + expect(result.current.currentPage).toBe(1); + expect(result.current.navigation.canGoNext).toBe(true); + expect(result.current.navigation.canGoPrevious).toBe(true); + }); + + it('should not go to the next page if validation rule does not allow', () => { + const { result } = renderHook(() => usePagination(items)); + act(() => { + result.current.navigation.onNext(); + result.current.navigation.onNext(); + }); + expect(result.current.currentPage).toBe(2); + expect(result.current.navigation.canGoNext).toBe(false); + expect(result.current.navigation.canGoPrevious).toBe(true); + }); + + it('should go to the previous page', () => { + const { result } = renderHook(() => usePagination(items)); + act(() => { + result.current.navigation.onNext(); + }); + expect(result.current.currentPage).toBe(1); + + act(() => { + result.current.navigation.onPrevious(); + }); + expect(result.current.currentPage).toBe(0); + + expect(result.current.navigation.canGoNext).toBe(true); + expect(result.current.navigation.canGoPrevious).toBe(false); + }); + + it('should not go to the previous page if already on the first page', () => { + const { result } = renderHook(() => usePagination(items)); + act(() => { + result.current.navigation.onPrevious(); + }); + expect(result.current.currentPage).toBe(0); + expect(result.current.navigation.canGoNext).toBe(true); + expect(result.current.navigation.canGoPrevious).toBe(false); + }); +}); diff --git a/frontend/libs/studio-components/src/components/StudioPaginatedContent/hooks/usePagination.ts b/frontend/libs/studio-components/src/components/StudioPaginatedContent/hooks/usePagination.ts new file mode 100644 index 00000000000..a56fd684578 --- /dev/null +++ b/frontend/libs/studio-components/src/components/StudioPaginatedContent/hooks/usePagination.ts @@ -0,0 +1,44 @@ +import { type ReactNode, useState } from 'react'; +import { type StudioPaginatedNavigation } from '../types/StudioPaginatedNavigation'; +import { type StudioPaginatedItem } from '../types/StudioPaginatedItem'; + +export const usePagination = (items: StudioPaginatedItem[]) => { + const [currentPage, setCurrentPage] = useState(0); + + const hasPreviousPage: boolean = currentPage > 0; + const hasNextPage: boolean = currentPage < items.length - 1; + + const validationRules: boolean[] = mapItemsToValidationRules(items); + const pages: ReactNode[] = mapItemsToPages(items); + + const canGoToNextPage: boolean = validationRules[currentPage] && hasNextPage; + + const goNext = () => { + if (canGoToNextPage) { + setCurrentPage((current: number) => current + 1); + } + }; + + const goPrevious = () => { + if (hasPreviousPage) { + setCurrentPage((current: number) => current - 1); + } + }; + + const navigation: StudioPaginatedNavigation = { + canGoNext: canGoToNextPage, + canGoPrevious: hasPreviousPage, + onNext: goNext, + onPrevious: goPrevious, + }; + + return { currentPage, pages, navigation }; +}; + +const mapItemsToValidationRules = (items: StudioPaginatedItem[]): boolean[] => { + return items.map((item: StudioPaginatedItem) => item?.validationRuleForNextButton ?? true); +}; + +const mapItemsToPages = (items: StudioPaginatedItem[]): ReactNode[] => { + return items.map((item: StudioPaginatedItem) => item.pageContent); +}; diff --git a/frontend/libs/studio-components/src/components/StudioPaginatedContent/index.ts b/frontend/libs/studio-components/src/components/StudioPaginatedContent/index.ts new file mode 100644 index 00000000000..fbbce9d3949 --- /dev/null +++ b/frontend/libs/studio-components/src/components/StudioPaginatedContent/index.ts @@ -0,0 +1 @@ +export { StudioPaginatedContent } from './StudioPaginatedContent'; diff --git a/frontend/libs/studio-components/src/components/StudioPaginatedContent/types/StudioPaginatedItem.ts b/frontend/libs/studio-components/src/components/StudioPaginatedContent/types/StudioPaginatedItem.ts new file mode 100644 index 00000000000..da7815d2402 --- /dev/null +++ b/frontend/libs/studio-components/src/components/StudioPaginatedContent/types/StudioPaginatedItem.ts @@ -0,0 +1,6 @@ +import { type ReactNode } from 'react'; + +export type StudioPaginatedItem = { + pageContent: ReactNode; + validationRuleForNextButton?: boolean; +}; diff --git a/frontend/libs/studio-components/src/components/StudioPaginatedContent/types/StudioPaginatedNavigation.ts b/frontend/libs/studio-components/src/components/StudioPaginatedContent/types/StudioPaginatedNavigation.ts new file mode 100644 index 00000000000..d46b1035826 --- /dev/null +++ b/frontend/libs/studio-components/src/components/StudioPaginatedContent/types/StudioPaginatedNavigation.ts @@ -0,0 +1,6 @@ +export type StudioPaginatedNavigation = { + canGoNext?: boolean; + canGoPrevious?: boolean; + onNext: () => void; + onPrevious: () => void; +}; From 026ec9d3246445f8e5451c7dc1576de2e9b4b00b Mon Sep 17 00:00:00 2001 From: Lars <74791975+lassopicasso@users.noreply.github.com> Date: Mon, 20 Jan 2025 07:42:22 +0100 Subject: [PATCH 2/5] fix: broken codelist links in Studio to Altinn docs (#14447) --- .../src/components/config/editModal/EditCodeList.tsx | 2 +- .../EditOptions/OptionTabs/ReferenceTab/ReferenceTab.tsx | 4 +++- .../editModal/EditOptions/OptionTabs/SelectTab/SelectTab.tsx | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditCodeList.tsx b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditCodeList.tsx index 648115ab9cf..348f18f281e 100644 --- a/frontend/packages/ux-editor-v3/src/components/config/editModal/EditCodeList.tsx +++ b/frontend/packages/ux-editor-v3/src/components/config/editModal/EditCodeList.tsx @@ -82,7 +82,7 @@ export function EditCodeList({ component, handleComponentChange }: IGenericEditC

diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/ReferenceTab/ReferenceTab.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/ReferenceTab/ReferenceTab.tsx index b5941823e09..e4d62b23302 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/ReferenceTab/ReferenceTab.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/ReferenceTab/ReferenceTab.tsx @@ -64,7 +64,9 @@ export function ReferenceTab({

diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/SelectTab/SelectTab.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/SelectTab/SelectTab.tsx index b7b7fb387e9..63f35ed39d5 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/SelectTab/SelectTab.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/SelectTab/SelectTab.tsx @@ -83,7 +83,7 @@ export function SelectTab({ Date: Mon, 20 Jan 2025 09:59:06 +0100 Subject: [PATCH 3/5] feat: show code list title as readOnly displayTile if in use (#14400) --- frontend/language/src/nb.json | 1 + .../CodeListPage/CodeLists/CodeLists.test.tsx | 16 +++ .../CodeListPage/CodeLists/CodeLists.tsx | 8 +- .../CodeLists/EditCodeList/EditCodeList.tsx | 97 +++++++++++++------ 4 files changed, 89 insertions(+), 33 deletions(-) diff --git a/frontend/language/src/nb.json b/frontend/language/src/nb.json index 315e14e48be..17d7812770c 100644 --- a/frontend/language/src/nb.json +++ b/frontend/language/src/nb.json @@ -19,6 +19,7 @@ "app_content_library.code_lists.code_list_delete": "Slett kodeliste", "app_content_library.code_lists.code_list_delete_disabled_title": "Før du kan å slette kodelisten, må du fjerne den fra der den er brukt i appen.", "app_content_library.code_lists.code_list_delete_enabled_title": "Slett kodelisten fra biblioteket.", + "app_content_library.code_lists.code_list_edit_id_disabled_title": "Redigering er ikke tilgjengelig når kodelisten er tatt i bruk.", "app_content_library.code_lists.code_list_edit_id_label": "Navn på kodeliste", "app_content_library.code_lists.code_list_edit_id_title": "Rediger navn på kodelisten {{codeListName}}", "app_content_library.code_lists.code_list_show_usage": "Se hvor kodelisten er tatt i bruk", diff --git a/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/CodeLists.test.tsx b/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/CodeLists.test.tsx index df6c7d6f1bb..9753a136497 100644 --- a/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/CodeLists.test.tsx +++ b/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/CodeLists.test.tsx @@ -197,6 +197,22 @@ describe('CodeLists', () => { expect(onUpdateCodeListIdMock).toHaveBeenLastCalledWith(codeListName, codeListName + '2'); }); + it('renders display tile instead of edit button when the code list is in use', async () => { + renderCodeLists({ + codeListsUsages: [ + { + codeListId: codeListsDataMock[0].title, + codeListIdSources: [{ layoutSetId: '', layoutName: '', componentIds: [''] }], + }, + ], + }); + const codeListId = screen.getByTitle( + textMock('app_content_library.code_lists.code_list_edit_id_disabled_title'), + ); + expect(codeListId).toBeInTheDocument(); + expect(codeListId).not.toHaveAttribute('role', 'button'); + }); + it('shows error message when assigning an invalid id to the code list', async () => { const user = userEvent.setup(); const invalidCodeListName = 'invalidCodeListName'; diff --git a/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/CodeLists.tsx b/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/CodeLists.tsx index 03ed8712b67..f37940e9653 100644 --- a/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/CodeLists.tsx +++ b/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/CodeLists.tsx @@ -111,6 +111,7 @@ type CodeListAccordionContentProps = Omit; function CodeListAccordionContent({ codeListData, + codeListSources, ...rest }: CodeListAccordionContentProps): React.ReactElement { const { t } = useTranslation(); @@ -122,7 +123,12 @@ function CodeListAccordionContent({ {t('app_content_library.code_lists.fetch_error')} ) : ( - + )} ); diff --git a/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/EditCodeList/EditCodeList.tsx b/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/EditCodeList/EditCodeList.tsx index 22dff7435d7..cb7a7539f13 100644 --- a/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/EditCodeList/EditCodeList.tsx +++ b/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeListPage/CodeLists/EditCodeList/EditCodeList.tsx @@ -2,6 +2,7 @@ import type { CodeList, CodeListEditorTexts } from '@studio/components'; import { StudioDeleteButton, StudioModal, + StudioDisplayTile, StudioCodeListEditor, StudioToggleableTextfield, } from '@studio/components'; @@ -35,13 +36,7 @@ export function EditCodeList({ codeListNames, codeListSources, }: EditCodeListProps): React.ReactElement { - const { t } = useTranslation(); const editorTexts: CodeListEditorTexts = useCodeListEditorTexts(); - const getInvalidInputFileNameErrorMessage = useInputCodeListNameErrorMessage(); - - const handleUpdateCodeListId = (newCodeListId: string) => { - if (newCodeListId !== codeListTitle) onUpdateCodeListId(codeListTitle, newCodeListId); - }; const handleCodeListChange = (updatedCodeList: CodeList): void => { const updatedCodeListWithMetadata = updateCodeListWithMetadata( @@ -51,38 +46,18 @@ export function EditCodeList({ onUpdateCodeList(updatedCodeListWithMetadata); }; - const handleValidateCodeListId = (newCodeListId: string) => { - const invalidCodeListNames = ArrayUtils.removeItemByValue(codeListNames, codeListTitle); - const fileNameError = FileNameUtils.findFileNameError(newCodeListId, invalidCodeListNames); - return getInvalidInputFileNameErrorMessage(fileNameError); - }; - const handleDeleteCodeList = (): void => onDeleteCodeList(codeListTitle); const codeListHasUsages = codeListSources.length > 0; + const isCodeListEditable = codeListSources.length === 0; return (

- , - title: t('app_content_library.code_lists.code_list_edit_id_title', { - codeListName: codeListTitle, - }), - value: codeListTitle, - onBlur: (event) => handleUpdateCodeListId(event.target.value), - size: 'small', - }} - viewProps={{ - label: t('app_content_library.code_lists.code_list_edit_id_label'), - children: codeListTitle, - variant: 'tertiary', - title: t('app_content_library.code_lists.code_list_view_id_title', { - codeListName: codeListTitle, - }), - }} + void; +}; + +function EditCodeListTitle({ + codeListTitle, + isCodeListEditable, + codeListNames, + onUpdateCodeListId, +}: EditCodeListTitleProps): React.ReactElement { + const { t } = useTranslation(); + const getInvalidInputFileNameErrorMessage = useInputCodeListNameErrorMessage(); + + const handleUpdateCodeListId = (newCodeListId: string) => { + if (newCodeListId !== codeListTitle) onUpdateCodeListId(codeListTitle, newCodeListId); + }; + + const handleValidateCodeListId = (newCodeListId: string) => { + const invalidCodeListNames = ArrayUtils.removeItemByValue(codeListNames, codeListTitle); + const fileNameError = FileNameUtils.findFileNameError(newCodeListId, invalidCodeListNames); + return getInvalidInputFileNameErrorMessage(fileNameError); + }; + + return isCodeListEditable ? ( + , + title: t('app_content_library.code_lists.code_list_edit_id_title', { + codeListName: codeListTitle, + }), + value: codeListTitle, + onBlur: (event) => handleUpdateCodeListId(event.target.value), + size: 'small', + }} + viewProps={{ + label: t('app_content_library.code_lists.code_list_edit_id_label'), + children: codeListTitle, + variant: 'tertiary', + title: t('app_content_library.code_lists.code_list_view_id_title', { + codeListName: codeListTitle, + }), + }} + /> + ) : ( + } + /> + ); +} + type CodeListButtonsProps = { codeListHasUsages: boolean; codeListSources: CodeListIdSource[]; From b59e9ccf4ab4f5a4dfc2477f2c81d23cf76d50b8 Mon Sep 17 00:00:00 2001 From: Konrad-Simso Date: Mon, 20 Jan 2025 11:01:32 +0100 Subject: [PATCH 4/5] fix: update path used to get option list ids (#14437) Co-authored-by: Erling Hauan Co-authored-by: andreastanderen <71079896+standeren@users.noreply.github.com> --- .../Controllers/AppDevelopmentController.cs | 20 +----- .../GetOptionListIdsTests.cs | 61 ------------------- frontend/packages/shared/src/api/paths.js | 2 +- 3 files changed, 2 insertions(+), 81 deletions(-) delete mode 100644 backend/tests/Designer.Tests/Controllers/AppDevelopmentController/GetOptionListIdsTests.cs diff --git a/backend/src/Designer/Controllers/AppDevelopmentController.cs b/backend/src/Designer/Controllers/AppDevelopmentController.cs index b6d4ee96e1e..4d49ca1b8ee 100644 --- a/backend/src/Designer/Controllers/AppDevelopmentController.cs +++ b/backend/src/Designer/Controllers/AppDevelopmentController.cs @@ -9,7 +9,6 @@ using Altinn.Studio.Designer.Events; using Altinn.Studio.Designer.Filters; using Altinn.Studio.Designer.Helpers; -using Altinn.Studio.Designer.Infrastructure.GitRepository; using Altinn.Studio.Designer.Models; using Altinn.Studio.Designer.Models.Dto; using Altinn.Studio.Designer.Services.Interfaces; @@ -32,7 +31,6 @@ public class AppDevelopmentController : Controller private readonly IAppDevelopmentService _appDevelopmentService; private readonly IRepository _repository; private readonly ISourceControl _sourceControl; - private readonly IAltinnGitRepositoryFactory _altinnGitRepositoryFactory; private readonly ApplicationInsightsSettings _applicationInsightsSettings; private readonly IMediator _mediator; @@ -43,15 +41,13 @@ public class AppDevelopmentController : Controller /// The app development service /// The application repository service /// The source control service. - /// /// An /// - public AppDevelopmentController(IAppDevelopmentService appDevelopmentService, IRepository repositoryService, ISourceControl sourceControl, IAltinnGitRepositoryFactory altinnGitRepositoryFactory, ApplicationInsightsSettings applicationInsightsSettings, IMediator mediator) + public AppDevelopmentController(IAppDevelopmentService appDevelopmentService, IRepository repositoryService, ISourceControl sourceControl, ApplicationInsightsSettings applicationInsightsSettings, IMediator mediator) { _appDevelopmentService = appDevelopmentService; _repository = repositoryService; _sourceControl = sourceControl; - _altinnGitRepositoryFactory = altinnGitRepositoryFactory; _applicationInsightsSettings = applicationInsightsSettings; _mediator = mediator; } @@ -543,20 +539,6 @@ public ActionResult GetWidgetSettings(string org, string app) return Ok(widgetSettings); } - [HttpGet] - [Route("option-list-ids")] - public ActionResult GetOptionListIds(string org, string app) - { - string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); - AltinnAppGitRepository altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, app, developer); - string[] optionListIds = altinnAppGitRepository.GetOptionsListIds(); - if (optionListIds.Length == 0) - { - return NoContent(); - } - return Ok(optionListIds); - } - [HttpGet("app-version")] public VersionResponse GetAppVersion(string org, string app) { diff --git a/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/GetOptionListIdsTests.cs b/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/GetOptionListIdsTests.cs deleted file mode 100644 index ccc4301023b..00000000000 --- a/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/GetOptionListIdsTests.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; -using Designer.Tests.Controllers.ApiTests; -using Designer.Tests.Utils; -using Microsoft.AspNetCore.Mvc.Testing; -using Xunit; - -namespace Designer.Tests.Controllers.AppDevelopmentController -{ - public class GetOptionListIdsTests : DesignerEndpointsTestsBase, IClassFixture> - { - private static string VersionPrefix(string org, string repository) => $"/designer/api/{org}/{repository}/app-development"; - public GetOptionListIdsTests(WebApplicationFactory factory) : base(factory) - { - } - - [Theory] - [InlineData("ttd", "app-with-options", "testUser")] - public async Task GetOptionsListIds_ShouldReturnOk(string org, string app, string developer) - { - string targetRepository = TestDataHelper.GenerateTestRepoName(); - await CopyRepositoryForTest(org, app, developer, targetRepository); - - var expectedOptionsListIds = new List() - { - { "other-options" }, - { "test-options" }, - { "options-with-null-fields" }, - }; - - string url = $"{VersionPrefix(org, targetRepository)}/option-list-ids"; - using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, url); - - using var response = await HttpClient.SendAsync(httpRequestMessage); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string responseContent = await response.Content.ReadAsStringAsync(); - List responseList = JsonSerializer.Deserialize>(responseContent); - Assert.Equal(3, responseList.Count); - foreach (string id in expectedOptionsListIds) - { - Assert.Contains(id, responseList); - } - } - - [Theory] - [InlineData("ttd", "empty-app")] - public async Task GetOptionsListIds_WhenNotExists_ReturnsNotFound(string org, string app) - { - string url = $"{VersionPrefix(org, app)}/option-list-ids"; - using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, url); - - using var response = await HttpClient.SendAsync(httpRequestMessage); - - Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); - } - } -} diff --git a/frontend/packages/shared/src/api/paths.js b/frontend/packages/shared/src/api/paths.js index ba82ff96e56..a8ce17184b9 100644 --- a/frontend/packages/shared/src/api/paths.js +++ b/frontend/packages/shared/src/api/paths.js @@ -45,7 +45,7 @@ export const widgetSettingsPath = (org, app) => `${basePath}/${org}/${app}/app-d export const optionListPath = (org, app, optionsListId) => `${basePath}/${org}/${app}/options/${optionsListId}`; // Get, Delete export const optionListsPath = (org, app) => `${basePath}/${org}/${app}/options/option-lists`; // Get export const optionListReferencesPath = (org, app) => `${basePath}/${org}/${app}/options/usage`; // Get -export const optionListIdsPath = (org, app) => `${basePath}/${org}/${app}/app-development/option-list-ids`; // Get +export const optionListIdsPath = (org, app) => `${basePath}/${org}/${app}/options`; // Get export const optionListUpdatePath = (org, app, optionsListId) => `${basePath}/${org}/${app}/options/${optionsListId}`; // Put export const optionListIdUpdatePath = (org, app, optionsListId) => `${basePath}/${org}/${app}/options/change-name/${optionsListId}`; // Put export const optionListUploadPath = (org, app) => `${basePath}/${org}/${app}/options/upload`; // Post From 1cf0f2e00a7b85bb20ff9c7d3a0f6de923800201 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 20 Jan 2025 12:48:21 +0100 Subject: [PATCH 5/5] feat: Delete all Summary 2.0 components that has a reference to a deleted component (#14126) Co-authored-by: Lars <74791975+lassopicasso@users.noreply.github.com> --- .../Controllers/AppDevelopmentController.cs | 30 +++- .../Controllers/ResourceAdminController.cs | 8 +- backend/src/Designer/Enums/ReferenceType.cs | 28 +-- ...ceSource.cs => ResourceReferenceSource.cs} | 2 +- .../Designer/Enums/ResourceReferenceType.cs | 31 ++++ .../ComponentDeletedLayoutsHandler.cs | 27 +++ .../LayoutPageDeletedLayoutsHandler.cs | 27 +++ .../LayoutSetDeletedComponentRefHandler.cs | 73 -------- .../LayoutSetDeletedLayoutsHandler.cs | 27 +++ .../Designer/Events/ComponentDeletedEvent.cs | 11 ++ .../Designer/Events/LayoutPageDeletedEvent.cs | 11 ++ .../Designer/Events/LayoutSetDeletedEvent.cs | 2 +- .../Designer/Hubs/SyncHub/SyncErrorCodes.cs | 4 +- backend/src/Designer/Models/Reference.cs | 7 + .../src/Designer/Models/ResourceReference.cs | 4 +- .../Implementation/AppDevelopmentService.cs | 159 ++++++++++++++++++ .../Interfaces/IAppDevelopmentService.cs | 8 + .../DeleteFormLayoutTests.cs | 33 ++++ .../DeleteLayoutSetTests.cs | 31 ++++ .../SaveFormLayoutTests.cs | 48 +++++- .../ResourceAdminControllerTestsBaseClass.cs | 4 +- .../App/ui/component/Settings.json | 9 + .../App/ui/component/layouts/Side1.json | 49 ++++++ .../App/ui/component/layouts/Side2.json | 20 +++ .../App/ui/component2/Settings.json | 9 + .../App/ui/component2/layouts/Side1.json | 56 ++++++ .../App/ui/component2/layouts/Side2.json | 49 ++++++ .../App/ui/layout-sets.json | 36 ++++ .../App/ui/layout/Settings.json | 8 + .../App/ui/layout/layouts/Side1.json | 31 ++++ .../App/ui/layout2/Settings.json | 9 + .../App/ui/layout2/layouts/Side1.json | 53 ++++++ .../App/ui/layout2/layouts/Side2.json | 31 ++++ .../App/ui/layoutSet2/Settings.json | 9 + .../App/ui/layoutSet2/layouts/Side1.json | 54 ++++++ .../App/ui/layoutSet2/layouts/Side2.json | 13 ++ .../App/ui/component/Settings.json | 9 + .../App/ui/component/layouts/Side1.json | 64 +++++++ .../App/ui/component/layouts/Side2.json | 27 +++ .../App/ui/component2/Settings.json | 9 + .../App/ui/component2/layouts/Side1.json | 56 ++++++ .../App/ui/component2/layouts/Side2.json | 64 +++++++ .../App/ui/layout-sets.json | 42 +++++ .../App/ui/layout/Settings.json | 9 + .../App/ui/layout/layouts/Side1.json | 49 ++++++ .../App/ui/layout/layouts/Side2.json | 20 +++ .../App/ui/layout2/Settings.json | 9 + .../App/ui/layout2/layouts/Side1.json | 53 ++++++ .../App/ui/layout2/layouts/Side2.json | 49 ++++++ .../App/ui/layoutSet/Settings.json | 9 + .../App/ui/layoutSet/layouts/Side1.json | 13 ++ .../App/ui/layoutSet/layouts/Side2.json | 20 +++ .../App/ui/layoutSet2/Settings.json | 9 + .../App/ui/layoutSet2/layouts/Side1.json | 54 ++++++ .../App/ui/layoutSet2/layouts/Side2.json | 49 ++++++ frontend/language/src/nb.json | 4 +- .../SupportedContextPadProvider.js | 2 +- 57 files changed, 1513 insertions(+), 118 deletions(-) rename backend/src/Designer/Enums/{ReferenceSource.cs => ResourceReferenceSource.cs} (93%) create mode 100644 backend/src/Designer/Enums/ResourceReferenceType.cs create mode 100644 backend/src/Designer/EventHandlers/ComponentDeleted/ComponentDeletedLayoutsHandler.cs create mode 100644 backend/src/Designer/EventHandlers/LayoutPageDeleted/LayoutPageDeletedLayoutsHandler.cs delete mode 100644 backend/src/Designer/EventHandlers/LayoutSetDeleted/LayoutSetDeletedComponentRefHandler.cs create mode 100644 backend/src/Designer/EventHandlers/LayoutSetDeleted/LayoutSetDeletedLayoutsHandler.cs create mode 100644 backend/src/Designer/Events/ComponentDeletedEvent.cs create mode 100644 backend/src/Designer/Events/LayoutPageDeletedEvent.cs create mode 100644 backend/src/Designer/Models/Reference.cs create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component/Settings.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component/layouts/Side1.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component/layouts/Side2.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component2/Settings.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component2/layouts/Side1.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component2/layouts/Side2.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout-sets.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout/Settings.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout/layouts/Side1.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout2/Settings.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout2/layouts/Side1.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout2/layouts/Side2.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layoutSet2/Settings.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layoutSet2/layouts/Side1.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layoutSet2/layouts/Side2.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component/Settings.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component/layouts/Side1.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component/layouts/Side2.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component2/Settings.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component2/layouts/Side1.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component2/layouts/Side2.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout-sets.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout/Settings.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout/layouts/Side1.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout/layouts/Side2.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout2/Settings.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout2/layouts/Side1.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout2/layouts/Side2.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet/Settings.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet/layouts/Side1.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet/layouts/Side2.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet2/Settings.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet2/layouts/Side1.json create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet2/layouts/Side2.json diff --git a/backend/src/Designer/Controllers/AppDevelopmentController.cs b/backend/src/Designer/Controllers/AppDevelopmentController.cs index 4d49ca1b8ee..5f8d9e884ca 100644 --- a/backend/src/Designer/Controllers/AppDevelopmentController.cs +++ b/backend/src/Designer/Controllers/AppDevelopmentController.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text.Json.Nodes; using System.Threading; using System.Threading.Tasks; @@ -119,8 +120,17 @@ public async Task SaveFormLayout(string org, string app, [FromQuer if (formLayoutPayload.ComponentIdsChange is not null && !string.IsNullOrEmpty(layoutSetName)) { - foreach (var componentIdChange in formLayoutPayload.ComponentIdsChange) + foreach (var componentIdChange in formLayoutPayload.ComponentIdsChange.Where((componentIdChange) => componentIdChange.OldComponentId != componentIdChange.NewComponentId)) { + if (componentIdChange.NewComponentId == null) + { + await _mediator.Publish(new ComponentDeletedEvent + { + ComponentId = componentIdChange.OldComponentId, + LayoutSetName = layoutSetName, + EditingContext = editingContext + }, cancellationToken); + } await _mediator.Publish(new ComponentIdChangedEvent { OldComponentId = componentIdChange.OldComponentId, @@ -155,16 +165,26 @@ await _mediator.Publish(new LayoutPageAddedEvent /// Application identifier which is unique within an organisation. /// The name of the layout set the specific layout belongs to /// The form layout to be deleted + /// A that observes if operation is cancelled. /// A success message if the save was successful [HttpDelete] [Route("form-layout/{layoutName}")] - public ActionResult DeleteFormLayout(string org, string app, [FromQuery] string layoutSetName, [FromRoute] string layoutName) + public async Task DeleteFormLayout(string org, string app, [FromQuery] string layoutSetName, [FromRoute] string layoutName, CancellationToken cancellationToken) { try { string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); var editingContext = AltinnRepoEditingContext.FromOrgRepoDeveloper(org, app, developer); + + await _mediator.Publish(new LayoutPageDeletedEvent + { + EditingContext = editingContext, + LayoutSetName = layoutSetName, + LayoutName = layoutName, + }, cancellationToken); + _appDevelopmentService.DeleteFormLayout(editingContext, layoutSetName, layoutName); + return Ok(); } catch (FileNotFoundException exception) @@ -398,13 +418,15 @@ public async Task DeleteLayoutSet(string org, string app, [FromRou { string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); var editingContext = AltinnRepoEditingContext.FromOrgRepoDeveloper(org, app, developer); - LayoutSets layoutSets = await _appDevelopmentService.DeleteLayoutSet(editingContext, layoutSetIdToUpdate, cancellationToken); await _mediator.Publish(new LayoutSetDeletedEvent { EditingContext = editingContext, - LayoutSetId = layoutSetIdToUpdate + LayoutSetName = layoutSetIdToUpdate }, cancellationToken); + + LayoutSets layoutSets = await _appDevelopmentService.DeleteLayoutSet(editingContext, layoutSetIdToUpdate, cancellationToken); + return Ok(layoutSets); } diff --git a/backend/src/Designer/Controllers/ResourceAdminController.cs b/backend/src/Designer/Controllers/ResourceAdminController.cs index 1601cd5c06a..1e00a003ca9 100644 --- a/backend/src/Designer/Controllers/ResourceAdminController.cs +++ b/backend/src/Designer/Controllers/ResourceAdminController.cs @@ -487,7 +487,7 @@ public async Task>> GetAltinn2LinkServices(s foreach (ServiceResource resource in allResources) { if (resource?.HasCompetentAuthority.Orgcode != null - && resource.ResourceReferences != null && resource.ResourceReferences.Exists(r => r.ReferenceType != null && r.ReferenceType.Equals(ReferenceType.ServiceCode)) + && resource.ResourceReferences != null && resource.ResourceReferences.Exists(r => r.ReferenceType != null && r.ReferenceType.Equals(ResourceReferenceType.ServiceCode)) && resource.ResourceType == ResourceType.Altinn2Service) { AvailableService service = new AvailableService(); @@ -496,8 +496,8 @@ public async Task>> GetAltinn2LinkServices(s service.ServiceName = resource.Title["nb"]; } - service.ExternalServiceCode = resource.ResourceReferences.First(r => r.ReferenceType.Equals(ReferenceType.ServiceCode)).Reference; - service.ExternalServiceEditionCode = Convert.ToInt32(resource.ResourceReferences.First(r => r.ReferenceType.Equals(ReferenceType.ServiceEditionCode)).Reference); + service.ExternalServiceCode = resource.ResourceReferences.First(r => r.ReferenceType.Equals(ResourceReferenceType.ServiceCode)).Reference; + service.ExternalServiceEditionCode = Convert.ToInt32(resource.ResourceReferences.First(r => r.ReferenceType.Equals(ResourceReferenceType.ServiceEditionCode)).Reference); service.ServiceOwnerCode = resource.HasCompetentAuthority.Orgcode; unfiltered.Add(service); } @@ -551,7 +551,7 @@ private ValidationProblemDetails ValidateResource(ServiceResource resource) if (resource.ResourceType == ResourceType.MaskinportenSchema) { - if (resource.ResourceReferences == null || !resource.ResourceReferences.Any((x) => x.ReferenceType == ReferenceType.MaskinportenScope)) + if (resource.ResourceReferences == null || !resource.ResourceReferences.Any((x) => x.ReferenceType == ResourceReferenceType.MaskinportenScope)) { ModelState.AddModelError($"{resource.Identifier}.resourceReferences", "resourceerror.missingmaskinportenscope"); } diff --git a/backend/src/Designer/Enums/ReferenceType.cs b/backend/src/Designer/Enums/ReferenceType.cs index de88d575e3e..e53ab497980 100644 --- a/backend/src/Designer/Enums/ReferenceType.cs +++ b/backend/src/Designer/Enums/ReferenceType.cs @@ -1,31 +1,9 @@ -using System.Runtime.Serialization; - namespace Altinn.Studio.Designer.Enums { - /// - /// Enum for reference types of resources in the resource registry - /// public enum ReferenceType { - [EnumMember(Value = "Default")] - Default = 0, - - [EnumMember(Value = "Uri")] - Uri = 1, - - [EnumMember(Value = "DelegationSchemeId")] - DelegationSchemeId = 2, - - [EnumMember(Value = "MaskinportenScope")] - MaskinportenScope = 3, - - [EnumMember(Value = "ServiceCode")] - ServiceCode = 4, - - [EnumMember(Value = "ServiceEditionCode")] - ServiceEditionCode = 5, - - [EnumMember(Value = "ApplicationId")] - ApplicationId = 6, + LayoutSet, + Layout, + Component } } diff --git a/backend/src/Designer/Enums/ReferenceSource.cs b/backend/src/Designer/Enums/ResourceReferenceSource.cs similarity index 93% rename from backend/src/Designer/Enums/ReferenceSource.cs rename to backend/src/Designer/Enums/ResourceReferenceSource.cs index f7d81958834..502d82cad96 100644 --- a/backend/src/Designer/Enums/ReferenceSource.cs +++ b/backend/src/Designer/Enums/ResourceReferenceSource.cs @@ -5,7 +5,7 @@ namespace Altinn.Studio.Designer.Enums /// /// Enum for the different reference sources for resources in the resource registry /// - public enum ReferenceSource + public enum ResourceReferenceSource { [EnumMember(Value = "Default")] Default = 0, diff --git a/backend/src/Designer/Enums/ResourceReferenceType.cs b/backend/src/Designer/Enums/ResourceReferenceType.cs new file mode 100644 index 00000000000..69b35c7f1d1 --- /dev/null +++ b/backend/src/Designer/Enums/ResourceReferenceType.cs @@ -0,0 +1,31 @@ +using System.Runtime.Serialization; + +namespace Altinn.Studio.Designer.Enums +{ + /// + /// Enum for reference types of resources in the resource registry + /// + public enum ResourceReferenceType + { + [EnumMember(Value = "Default")] + Default = 0, + + [EnumMember(Value = "Uri")] + Uri = 1, + + [EnumMember(Value = "DelegationSchemeId")] + DelegationSchemeId = 2, + + [EnumMember(Value = "MaskinportenScope")] + MaskinportenScope = 3, + + [EnumMember(Value = "ServiceCode")] + ServiceCode = 4, + + [EnumMember(Value = "ServiceEditionCode")] + ServiceEditionCode = 5, + + [EnumMember(Value = "ApplicationId")] + ApplicationId = 6, + } +} diff --git a/backend/src/Designer/EventHandlers/ComponentDeleted/ComponentDeletedLayoutsHandler.cs b/backend/src/Designer/EventHandlers/ComponentDeleted/ComponentDeletedLayoutsHandler.cs new file mode 100644 index 00000000000..f2d893f2886 --- /dev/null +++ b/backend/src/Designer/EventHandlers/ComponentDeleted/ComponentDeletedLayoutsHandler.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Altinn.Studio.Designer.Enums; +using Altinn.Studio.Designer.Events; +using Altinn.Studio.Designer.Hubs.SyncHub; +using Altinn.Studio.Designer.Models; +using Altinn.Studio.Designer.Services.Interfaces; +using MediatR; + +namespace Altinn.Studio.Designer.EventHandlers.ComponentDeleted; + +public class ComponentDeletedLayoutsHandler(IFileSyncHandlerExecutor fileSyncHandlerExecutor, IAppDevelopmentService appDevelopmentService) : INotificationHandler +{ + public async Task Handle(ComponentDeletedEvent notification, CancellationToken cancellationToken) + { + await fileSyncHandlerExecutor.ExecuteWithExceptionHandlingAndConditionalNotification( + notification.EditingContext, + SyncErrorCodes.ComponentDeletedLayoutsSyncError, + "layouts", + async () => + { + List referencesToDelete = [new Reference(ReferenceType.Component, notification.LayoutSetName, notification.ComponentId)]; + return await appDevelopmentService.UpdateLayoutReferences(notification.EditingContext, referencesToDelete, cancellationToken); + }); + } +} diff --git a/backend/src/Designer/EventHandlers/LayoutPageDeleted/LayoutPageDeletedLayoutsHandler.cs b/backend/src/Designer/EventHandlers/LayoutPageDeleted/LayoutPageDeletedLayoutsHandler.cs new file mode 100644 index 00000000000..60c60cbd66b --- /dev/null +++ b/backend/src/Designer/EventHandlers/LayoutPageDeleted/LayoutPageDeletedLayoutsHandler.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Altinn.Studio.Designer.Enums; +using Altinn.Studio.Designer.Events; +using Altinn.Studio.Designer.Hubs.SyncHub; +using Altinn.Studio.Designer.Models; +using Altinn.Studio.Designer.Services.Interfaces; +using MediatR; + +namespace Altinn.Studio.Designer.EventHandlers.LayoutPageDeleted; + +public class LayoutPageDeletedLayoutsHandler(IFileSyncHandlerExecutor fileSyncHandlerExecutor, IAppDevelopmentService appDevelopmentService) : INotificationHandler +{ + public async Task Handle(LayoutPageDeletedEvent notification, CancellationToken cancellationToken) + { + await fileSyncHandlerExecutor.ExecuteWithExceptionHandlingAndConditionalNotification( + notification.EditingContext, + SyncErrorCodes.LayoutPageDeletedLayoutsSyncError, + "layouts", + async () => + { + List referencesToDelete = [new Reference(ReferenceType.Layout, notification.LayoutSetName, notification.LayoutName)]; + return await appDevelopmentService.UpdateLayoutReferences(notification.EditingContext, referencesToDelete, cancellationToken); + }); + } +} diff --git a/backend/src/Designer/EventHandlers/LayoutSetDeleted/LayoutSetDeletedComponentRefHandler.cs b/backend/src/Designer/EventHandlers/LayoutSetDeleted/LayoutSetDeletedComponentRefHandler.cs deleted file mode 100644 index 95b38685a47..00000000000 --- a/backend/src/Designer/EventHandlers/LayoutSetDeleted/LayoutSetDeletedComponentRefHandler.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Nodes; -using System.Threading; -using System.Threading.Tasks; -using Altinn.App.Core.Helpers; -using Altinn.Studio.Designer.Events; -using Altinn.Studio.Designer.Hubs.SyncHub; -using Altinn.Studio.Designer.Infrastructure.GitRepository; -using Altinn.Studio.Designer.Services.Interfaces; -using MediatR; - -namespace Altinn.Studio.Designer.EventHandlers.LayoutSetDeleted; - -public class LayoutSetDeletedComponentRefHandler(IAltinnGitRepositoryFactory altinnGitRepositoryFactory, IFileSyncHandlerExecutor fileSyncHandlerExecutor) : INotificationHandler -{ - public async Task Handle(LayoutSetDeletedEvent notification, CancellationToken cancellationToken) - { - AltinnAppGitRepository altinnAppGitRepository = altinnGitRepositoryFactory.GetAltinnAppGitRepository( - notification.EditingContext.Org, - notification.EditingContext.Repo, - notification.EditingContext.Developer); - - string[] layoutSetNames = altinnAppGitRepository.GetLayoutSetNames(); - - await fileSyncHandlerExecutor.ExecuteWithExceptionHandlingAndConditionalNotification( - notification.EditingContext, - SyncErrorCodes.LayoutSetSubLayoutSyncError, - "layouts", - async () => - { - bool hasChanges = false; - foreach (string layoutSetName in layoutSetNames) - { - Dictionary formLayouts = await altinnAppGitRepository.GetFormLayouts(layoutSetName, cancellationToken); - foreach (var formLayout in formLayouts) - { - hasChanges |= await RemoveComponentsReferencingLayoutSet( - notification, - altinnAppGitRepository, - layoutSetName, - formLayout, - cancellationToken); - } - } - return hasChanges; - }); - } - - private static async Task RemoveComponentsReferencingLayoutSet(LayoutSetDeletedEvent notification, AltinnAppGitRepository altinnAppGitRepository, string layoutSetName, KeyValuePair formLayout, CancellationToken cancellationToken) - { - if (formLayout.Value["data"] is not JsonObject data || data["layout"] is not JsonArray layoutArray) - { - return false; - } - - bool hasChanges = false; - layoutArray.RemoveAll(jsonNode => - { - if (jsonNode["layoutSet"]?.GetValue() == notification.LayoutSetId) - { - hasChanges = true; - return true; - } - return false; - }); - - if (hasChanges) - { - await altinnAppGitRepository.SaveLayout(layoutSetName, $"{formLayout.Key}.json", formLayout.Value, cancellationToken); - } - return hasChanges; - } -} diff --git a/backend/src/Designer/EventHandlers/LayoutSetDeleted/LayoutSetDeletedLayoutsHandler.cs b/backend/src/Designer/EventHandlers/LayoutSetDeleted/LayoutSetDeletedLayoutsHandler.cs new file mode 100644 index 00000000000..c30309362e0 --- /dev/null +++ b/backend/src/Designer/EventHandlers/LayoutSetDeleted/LayoutSetDeletedLayoutsHandler.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Altinn.Studio.Designer.Enums; +using Altinn.Studio.Designer.Events; +using Altinn.Studio.Designer.Hubs.SyncHub; +using Altinn.Studio.Designer.Models; +using Altinn.Studio.Designer.Services.Interfaces; +using MediatR; + +namespace Altinn.Studio.Designer.EventHandlers.LayoutSetDeleted; + +public class LayoutSetDeletedLayoutsHandler(IFileSyncHandlerExecutor fileSyncHandlerExecutor, IAppDevelopmentService appDevelopmentService) : INotificationHandler +{ + public async Task Handle(LayoutSetDeletedEvent notification, CancellationToken cancellationToken) + { + await fileSyncHandlerExecutor.ExecuteWithExceptionHandlingAndConditionalNotification( + notification.EditingContext, + SyncErrorCodes.LayoutSetDeletedLayoutsSyncError, + "layouts", + async () => + { + List referencesToDelete = [new Reference(ReferenceType.LayoutSet, notification.LayoutSetName, notification.LayoutSetName)]; + return await appDevelopmentService.UpdateLayoutReferences(notification.EditingContext, referencesToDelete, cancellationToken); + }); + } +} diff --git a/backend/src/Designer/Events/ComponentDeletedEvent.cs b/backend/src/Designer/Events/ComponentDeletedEvent.cs new file mode 100644 index 00000000000..a6897eee55d --- /dev/null +++ b/backend/src/Designer/Events/ComponentDeletedEvent.cs @@ -0,0 +1,11 @@ +using Altinn.Studio.Designer.Models; +using MediatR; + +namespace Altinn.Studio.Designer.Events; + +public class ComponentDeletedEvent : INotification +{ + public AltinnRepoEditingContext EditingContext { get; set; } + public string LayoutSetName { get; set; } + public string ComponentId { get; set; } +} diff --git a/backend/src/Designer/Events/LayoutPageDeletedEvent.cs b/backend/src/Designer/Events/LayoutPageDeletedEvent.cs new file mode 100644 index 00000000000..8411d583169 --- /dev/null +++ b/backend/src/Designer/Events/LayoutPageDeletedEvent.cs @@ -0,0 +1,11 @@ +using Altinn.Studio.Designer.Models; +using MediatR; + +namespace Altinn.Studio.Designer.Events; + +public class LayoutPageDeletedEvent : INotification +{ + public AltinnRepoEditingContext EditingContext { get; set; } + public string LayoutSetName { get; set; } + public string LayoutName { get; set; } +} diff --git a/backend/src/Designer/Events/LayoutSetDeletedEvent.cs b/backend/src/Designer/Events/LayoutSetDeletedEvent.cs index aadbd44a4c0..00bf4a926cf 100644 --- a/backend/src/Designer/Events/LayoutSetDeletedEvent.cs +++ b/backend/src/Designer/Events/LayoutSetDeletedEvent.cs @@ -5,6 +5,6 @@ namespace Altinn.Studio.Designer.Events; public class LayoutSetDeletedEvent : INotification { - public string LayoutSetId { get; set; } public AltinnRepoEditingContext EditingContext { get; set; } + public string LayoutSetName { get; set; } } diff --git a/backend/src/Designer/Hubs/SyncHub/SyncErrorCodes.cs b/backend/src/Designer/Hubs/SyncHub/SyncErrorCodes.cs index 23556793c7b..04178d4eb7e 100644 --- a/backend/src/Designer/Hubs/SyncHub/SyncErrorCodes.cs +++ b/backend/src/Designer/Hubs/SyncHub/SyncErrorCodes.cs @@ -9,8 +9,10 @@ public static class SyncErrorCodes public const string ApplicationMetadataDataTypeSyncError = nameof(ApplicationMetadataDataTypeSyncError); public const string LayoutSetsDataTypeSyncError = nameof(LayoutSetsDataTypeSyncError); public const string LayoutSetComponentIdSyncError = nameof(LayoutSetComponentIdSyncError); - public const string LayoutSetSubLayoutSyncError = nameof(LayoutSetSubLayoutSyncError); + public const string LayoutSetDeletedLayoutsSyncError = nameof(LayoutSetDeletedLayoutsSyncError); public const string LayoutSetSubFormButtonSyncError = nameof(LayoutSetSubFormButtonSyncError); public const string SettingsComponentIdSyncError = nameof(SettingsComponentIdSyncError); public const string LayoutPageAddSyncError = nameof(LayoutPageAddSyncError); + public const string ComponentDeletedLayoutsSyncError = nameof(ComponentDeletedLayoutsSyncError); + public const string LayoutPageDeletedLayoutsSyncError = nameof(LayoutPageDeletedLayoutsSyncError); } diff --git a/backend/src/Designer/Models/Reference.cs b/backend/src/Designer/Models/Reference.cs new file mode 100644 index 00000000000..6ba9d9a9c99 --- /dev/null +++ b/backend/src/Designer/Models/Reference.cs @@ -0,0 +1,7 @@ + +using Altinn.Studio.Designer.Enums; + +namespace Altinn.Studio.Designer.Models +{ + public record Reference(ReferenceType Type, string LayoutSetName, string Id, string NewId = null); +} diff --git a/backend/src/Designer/Models/ResourceReference.cs b/backend/src/Designer/Models/ResourceReference.cs index af730c5064a..7dcb6fdb735 100644 --- a/backend/src/Designer/Models/ResourceReference.cs +++ b/backend/src/Designer/Models/ResourceReference.cs @@ -13,7 +13,7 @@ public class ResourceReference /// The source the reference identifier points to /// [JsonConverter(typeof(JsonStringEnumConverter))] - public ReferenceSource? ReferenceSource { get; set; } + public ResourceReferenceSource? ReferenceSource { get; set; } /// /// The reference identifier @@ -24,6 +24,6 @@ public class ResourceReference /// The reference type /// [JsonConverter(typeof(JsonStringEnumConverter))] - public ReferenceType? ReferenceType { get; set; } + public ResourceReferenceType? ReferenceType { get; set; } } } diff --git a/backend/src/Designer/Services/Implementation/AppDevelopmentService.cs b/backend/src/Designer/Services/Implementation/AppDevelopmentService.cs index 33940606ae8..4d0bd99c357 100644 --- a/backend/src/Designer/Services/Implementation/AppDevelopmentService.cs +++ b/backend/src/Designer/Services/Implementation/AppDevelopmentService.cs @@ -9,6 +9,7 @@ using Altinn.App.Core.Internal.Process.Elements; using Altinn.App.Core.Models; using Altinn.Studio.DataModeling.Metamodel; +using Altinn.Studio.Designer.Enums; using Altinn.Studio.Designer.Exceptions.AppDevelopment; using Altinn.Studio.Designer.Helpers; using Altinn.Studio.Designer.Infrastructure.GitRepository; @@ -593,5 +594,163 @@ public async Task AddComponentToLayout( layoutArray.Add(component); await SaveFormLayout(editingContext, layoutSetName, layoutName, formLayout, cancellationToken); } + + public async Task UpdateLayoutReferences(AltinnRepoEditingContext editingContext, List referencesToUpdate, CancellationToken cancellationToken) + { + AltinnAppGitRepository altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository( + editingContext.Org, + editingContext.Repo, + editingContext.Developer); + + LayoutSets layoutSets = await altinnAppGitRepository.GetLayoutSetsFile(cancellationToken); + + return await UpdateLayoutReferences(altinnAppGitRepository, layoutSets.Sets, referencesToUpdate, cancellationToken); + } + + private async Task UpdateLayoutReferences(AltinnAppGitRepository altinnAppGitRepository, List layoutSets, List referencesToUpdate, CancellationToken cancellationToken) + { + List referencesToDelete = []; + bool hasChanges = false; + + var deletedReferences = referencesToUpdate.Where(item => string.IsNullOrEmpty(item.NewId)).ToList(); + + var deletedLayoutsSetIds = deletedReferences.Where(item => item.Type == ReferenceType.LayoutSet).Select(item => item.Id).ToList(); + var deletedLayouts = deletedReferences.Where(item => item.Type == ReferenceType.Layout).ToList(); + var deletedComponents = deletedReferences.Where(item => item.Type == ReferenceType.Component).ToList(); + + foreach (LayoutSetConfig layoutSet in layoutSets ?? [new() { Id = null }]) + { + bool isLayoutSetDeleted = deletedLayoutsSetIds.Contains(layoutSet.Id); + + Dictionary layouts = await altinnAppGitRepository.GetFormLayouts(layoutSet.Id, cancellationToken); + + var deletedLayoutIdsFromCurrentLayoutSet = deletedLayouts.Where(item => item.LayoutSetName == layoutSet.Id && string.IsNullOrEmpty(item.NewId)).Select(item => item.Id).ToList(); + foreach (KeyValuePair layout in layouts) + { + bool isLayoutDeleted = deletedLayoutIdsFromCurrentLayoutSet.Contains(layout.Key); + bool hasLayoutChanges = false; + + // TODO : https://github.com/Altinn/altinn-studio/issues/14073 + if (layout.Value["data"] is not JsonObject data) + { + continue; + } + + var deletedComponentIdsFromCurrentLayoutSet = deletedComponents.Where(item => item.LayoutSetName == layoutSet.Id && string.IsNullOrEmpty(item.NewId)).Select(item => item.Id).ToList(); + + if (data["layout"] is JsonArray componentList) + { + for (int i = componentList.Count - 1; i >= 0; i--) + { + JsonNode componentNode = componentList[i]; + if (componentNode is not JsonObject component) + { + continue; + } + + string componentId = component["id"]?.GetValue(); + if (string.IsNullOrEmpty(componentId)) + { + continue; + } + + bool isComponentDeleted = deletedComponentIdsFromCurrentLayoutSet.Contains(componentId); + + if (isComponentDeleted) + { + componentList.RemoveAt(i); + hasLayoutChanges = true; + } + + if (isLayoutSetDeleted || isLayoutDeleted || isComponentDeleted) + { + if (!isComponentDeleted) + { + referencesToDelete.Add(new Reference(ReferenceType.Component, layoutSet.Id, componentId)); + } + + continue; + } + + string componentType = component["type"]?.GetValue(); + switch (componentType) + { + case "Subform": + string subformLayoutSet = component["layoutSet"]?.GetValue(); + if (deletedLayoutsSetIds.Contains(subformLayoutSet)) + { + referencesToDelete.Add(new Reference(ReferenceType.Component, layoutSet.Id, componentId)); + componentList.RemoveAt(i); + hasLayoutChanges = true; + } + break; + case "Summary2": + if (component["target"] is JsonObject target) + { + string type = target["type"]?.GetValue(); + string id = target["id"]?.GetValue(); + string taskId = target["taskId"]?.GetValue(); + string layoutSetId = string.IsNullOrEmpty(taskId) ? layoutSet.Id : layoutSets?.FirstOrDefault(item => item.Tasks?.Contains(taskId) ?? false)?.Id; + + if ( + (type == "page" && deletedLayouts.Exists(item => item.LayoutSetName == layoutSetId && item.Id == id)) + || (type == "component" && deletedComponents.Exists(item => item.LayoutSetName == layoutSetId && item.Id == id)) + || deletedLayoutsSetIds.Contains(layoutSetId) + ) + { + referencesToDelete.Add(new Reference(ReferenceType.Component, layoutSet.Id, componentId)); + componentList.RemoveAt(i); + hasLayoutChanges = true; + } + + if (component["overrides"] is JsonArray overrideList) + { + for (int j = overrideList.Count - 1; j >= 0; j--) + { + JsonNode overrideItem = overrideList[j]; + string overrideComponentId = overrideItem["componentId"]?.GetValue(); + if (deletedComponents.Exists(item => item.LayoutSetName == layoutSetId && item.Id == overrideComponentId)) + { + overrideList.RemoveAt(j); + hasLayoutChanges = true; + } + + if (overrideList.Count == 0) + { + component.Remove("overrides"); + } + } + } + } + break; + } + } + } + + if (isLayoutSetDeleted || isLayoutDeleted) + { + if (!isLayoutDeleted) + { + referencesToDelete.Add(new Reference(ReferenceType.Layout, layoutSet.Id, layout.Key)); + } + + continue; + } + + if (hasLayoutChanges) + { + await altinnAppGitRepository.SaveLayout(layoutSet.Id, $"{layout.Key}.json", layout.Value, cancellationToken); + hasChanges = true; + } + } + } + + if (referencesToDelete.Count > 0) + { + hasChanges |= await UpdateLayoutReferences(altinnAppGitRepository, layoutSets, referencesToDelete, cancellationToken); + } + + return hasChanges; + } } } diff --git a/backend/src/Designer/Services/Interfaces/IAppDevelopmentService.cs b/backend/src/Designer/Services/Interfaces/IAppDevelopmentService.cs index e175fba77d0..11e656b1ec0 100644 --- a/backend/src/Designer/Services/Interfaces/IAppDevelopmentService.cs +++ b/backend/src/Designer/Services/Interfaces/IAppDevelopmentService.cs @@ -213,5 +213,13 @@ public Task GetModelMetadata( /// The component to add. /// An that observes if operation is cancelled. public Task AddComponentToLayout(AltinnRepoEditingContext altinnRepoEditingContext, string layoutSetName, string layoutName, object component, CancellationToken cancellationToken = default); + + /// + /// Update layout references + /// + /// An . + /// The references to update. + /// An that observes if operation is cancelled. + public Task UpdateLayoutReferences(AltinnRepoEditingContext altinnRepoEditingContext, List referencesToUpdate, CancellationToken cancellationToken); } } diff --git a/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/DeleteFormLayoutTests.cs b/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/DeleteFormLayoutTests.cs index 566829ff8bd..7cbae75c438 100644 --- a/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/DeleteFormLayoutTests.cs +++ b/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/DeleteFormLayoutTests.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; @@ -6,6 +7,7 @@ using Designer.Tests.Utils; using JetBrains.Annotations; using Microsoft.AspNetCore.Mvc.Testing; +using SharedResources.Tests; using Xunit; namespace Designer.Tests.Controllers.AppDevelopmentController @@ -54,5 +56,36 @@ public async Task DeleteFormLayout_NonExistingFile_Should_AndReturnNotFound(stri using var response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } + + [Theory] + [InlineData("ttd", "testUser", "layout", "Side2")] + public async Task DeleteFormLayout_DeletesAssociatedSummary2Components_ReturnsOk(string org, string developer, string layoutSetName, string layoutName) + { + string actualApp = "app-with-summary2-components"; + string app = TestDataHelper.GenerateTestRepoName(); + await CopyRepositoryForTest(org, actualApp, developer, app); + + string url = $"{VersionPrefix(org, app)}/form-layout/{layoutName}?layoutSetName={layoutSetName}"; + using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Delete, url); + + using var response = await HttpClient.SendAsync(httpRequestMessage); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + string expectedApp = "app-with-summary2-components-after-deleting-references"; + + string[] layoutPaths = [ + "layout/layouts/Side1.json", + "layout/layouts/Side2.json", + "layout2/layouts/Side1.json", + "layout2/layouts/Side2.json", + ]; + + layoutPaths.ToList().ForEach(file => + { + string actual = TestDataHelper.GetFileFromRepo(org, app, developer, $"App/ui/{file}"); + string expected = TestDataHelper.GetFileFromRepo(org, expectedApp, developer, $"App/ui/{file}"); + Assert.True(JsonUtils.DeepEquals(actual, expected)); + }); + } } } diff --git a/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/DeleteLayoutSetTests.cs b/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/DeleteLayoutSetTests.cs index affef320ace..9e233eb9a03 100644 --- a/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/DeleteLayoutSetTests.cs +++ b/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/DeleteLayoutSetTests.cs @@ -161,6 +161,37 @@ public async Task DeleteLayoutSet_RemovesComponentsReferencingLayoutSet(string o } + [Theory] + [InlineData("ttd", "testUser", "layoutSet")] + public async Task DeleteLayoutSet_DeletesAssociatedSummary2Components_ReturnsOk(string org, string developer, string layoutSetName) + { + string actualApp = "app-with-summary2-components"; + string app = TestDataHelper.GenerateTestRepoName(); + await CopyRepositoryForTest(org, actualApp, developer, app); + + string url = $"{VersionPrefix(org, app)}/layout-set/{layoutSetName}"; + using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Delete, url); + + using var response = await HttpClient.SendAsync(httpRequestMessage); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + string expectedApp = "app-with-summary2-components-after-deleting-references"; + + string[] layoutPaths = [ + "layoutSet/layouts/Side1.json", + "layoutSet/layouts/Side2.json", + "layoutSet2/layouts/Side1.json", + "layoutSet2/layouts/Side2.json" + ]; + + layoutPaths.ToList().ForEach(file => + { + string actual = TestDataHelper.GetFileFromRepo(org, app, developer, $"App/ui/{file}"); + string expected = TestDataHelper.GetFileFromRepo(org, expectedApp, developer, $"App/ui/{file}"); + Assert.True(JsonUtils.DeepEquals(actual, expected)); + }); + } + private async Task GetLayoutSetsFile(string org, string app, string developer) { AltinnGitRepositoryFactory altinnGitRepositoryFactory = diff --git a/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/SaveFormLayoutTests.cs b/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/SaveFormLayoutTests.cs index 03371622cc5..ae9ee078025 100644 --- a/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/SaveFormLayoutTests.cs +++ b/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/SaveFormLayoutTests.cs @@ -1,4 +1,6 @@ -using System.Net; +using System; +using System.Linq; +using System.Net; using System.Net.Http; using System.Net.Mime; using System.Text; @@ -92,6 +94,50 @@ public async Task SaveFormLayoutWithComponentIdsChange_ReturnsOk(string org, str Assert.True(JsonUtils.DeepEquals(layout, savedLayout)); } + [Theory] + [InlineData("ttd", "testUser", "component", "Side2", "Input-Om7N3y")] + public async Task SaveFormLayoutWithDeletedComponent_DeletesAssociatedSummary2Components_ReturnsOk(string org, string developer, string layoutSetName, string layoutName, string componentId) + { + string actualApp = "app-with-summary2-components"; + string app = TestDataHelper.GenerateTestRepoName(); + await CopyRepositoryForTest(org, actualApp, developer, app); + + string layout = TestDataHelper.GetFileFromRepo(org, app, developer, $"App/ui/{layoutSetName}/layouts/{layoutName}.json"); + JsonNode layoutWithDeletedComponent = JsonNode.Parse(layout); + JsonArray layoutArray = layoutWithDeletedComponent["data"]["layout"] as JsonArray; + layoutArray?.RemoveAt(0); + + string url = $"{VersionPrefix(org, app)}/form-layout/{layoutName}?layoutSetName={layoutSetName}"; + var payload = new JsonObject + { + ["componentIdsChange"] = new JsonArray() { + new JsonObject + { + ["oldComponentId"] = componentId, + } + }, + ["layout"] = layoutWithDeletedComponent + }; + HttpResponseMessage response = await SendHttpRequest(url, payload); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + string expectedApp = "app-with-summary2-components-after-deleting-references"; + + string[] layoutPaths = [ + "component/layouts/Side1.json", + "component/layouts/Side2.json", + "component2/layouts/Side1.json", + "component2/layouts/Side2.json" + ]; + + layoutPaths.ToList().ForEach(file => + { + string actual = TestDataHelper.GetFileFromRepo(org, app, developer, $"App/ui/{file}"); + string expected = TestDataHelper.GetFileFromRepo(org, expectedApp, developer, $"App/ui/{file}"); + Assert.True(JsonUtils.DeepEquals(actual, expected)); + }); + } + [Theory] [InlineData("ttd", "app-with-layoutsets", "testUser", "testLayout", "layoutSet1", "TestData/App/ui/layoutWithUnknownProperties.json")] public async Task SaveFormLayoutWithNewPageLanguageUpdate_ReturnsOk(string org, string app, string developer, string layoutName, string layoutSetName, string layoutPath) diff --git a/backend/tests/Designer.Tests/Controllers/ResourceAdminController/ResourceAdminControllerTestsBaseClass.cs b/backend/tests/Designer.Tests/Controllers/ResourceAdminController/ResourceAdminControllerTestsBaseClass.cs index eea732572e7..7e55e8b0be6 100644 --- a/backend/tests/Designer.Tests/Controllers/ResourceAdminController/ResourceAdminControllerTestsBaseClass.cs +++ b/backend/tests/Designer.Tests/Controllers/ResourceAdminController/ResourceAdminControllerTestsBaseClass.cs @@ -1,11 +1,9 @@ using System.Collections.Generic; -using Altinn.Studio.Designer.Configuration; using Altinn.Studio.Designer.Enums; using Altinn.Studio.Designer.Models; using Altinn.Studio.Designer.Services.Interfaces; using Altinn.Studio.Designer.TypedHttpClients.Altinn2Metadata; using Designer.Tests.Controllers.ApiTests; -using Designer.Tests.Mocks; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; using Moq; @@ -38,7 +36,7 @@ protected static List GetTestResourceReferences() { List resourceReferences = new List { - new ResourceReference { Reference = string.Empty, ReferenceSource = ReferenceSource.Default, ReferenceType = ReferenceType.Default } + new ResourceReference { Reference = string.Empty, ReferenceSource = ResourceReferenceSource.Default, ReferenceType = ResourceReferenceType.Default } }; return resourceReferences; diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component/Settings.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component/Settings.json new file mode 100644 index 00000000000..6bb44cf6db8 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component/Settings.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://altinncdn.no/schemas/json/layout/layoutSettings.schema.v1.json", + "pages": { + "order": [ + "Side1", + "Side2" + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component/layouts/Side1.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component/layouts/Side1.json new file mode 100644 index 00000000000..880092a7380 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component/layouts/Side1.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "target": { + "type": "layoutSet", + "id": "", + "taskId": "" + }, + "id": "Summary2-9iG1lB", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side1", + "taskId": "" + }, + "id": "Summary2-R8HsuB", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side2", + "taskId": "" + }, + "id": "Summary2-mL8NjJ", + "type": "Summary2" + }, + { + "target": { + "type": "component", + "id": "Input-qWr0oa", + "taskId": "" + }, + "id": "Summary2-0BV88Q", + "type": "Summary2" + }, + { + "id": "NavigationButtons-DfcNol", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component/layouts/Side2.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component/layouts/Side2.json new file mode 100644 index 00000000000..1fc7b9c90e0 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component/layouts/Side2.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "dataModelBindings": { + "simpleBinding": "" + }, + "id": "Input-qWr0oa", + "type": "Input" + }, + { + "id": "NavigationButtons-GAW8Dx", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component2/Settings.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component2/Settings.json new file mode 100644 index 00000000000..6bb44cf6db8 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component2/Settings.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://altinncdn.no/schemas/json/layout/layoutSettings.schema.v1.json", + "pages": { + "order": [ + "Side1", + "Side2" + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component2/layouts/Side1.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component2/layouts/Side1.json new file mode 100644 index 00000000000..8489464261c --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component2/layouts/Side1.json @@ -0,0 +1,56 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "dataModelBindings": { + "simpleBinding": "" + }, + "id": "Input-LCr3oK", + "type": "Input" + }, + { + "target": { + "type": "layoutSet", + "id": "", + "taskId": "" + }, + "id": "Summary2-eoT5QK", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side1", + "taskId": "" + }, + "id": "Summary2-NJfE92", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side2", + "taskId": "" + }, + "id": "Summary2-BGHvph", + "type": "Summary2" + }, + { + "target": { + "type": "component", + "id": "Input-LCr3oK", + "taskId": "" + }, + "id": "Summary2-rb2ml2", + "type": "Summary2" + }, + { + "id": "NavigationButtons-t7UTGJ", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component2/layouts/Side2.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component2/layouts/Side2.json new file mode 100644 index 00000000000..5ebc0f7df8b --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/component2/layouts/Side2.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "target": { + "type": "layoutSet", + "id": "", + "taskId": "component" + }, + "id": "Summary2-2FSqcf", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side1", + "taskId": "component" + }, + "id": "Summary2-eGrDpP", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side2", + "taskId": "component" + }, + "id": "Summary2-uJDgMu", + "type": "Summary2" + }, + { + "target": { + "type": "component", + "id": "Input-qWr0oa", + "taskId": "component" + }, + "id": "Summary2-2JiT3P", + "type": "Summary2" + }, + { + "id": "NavigationButtons-BYcvaT", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout-sets.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout-sets.json new file mode 100644 index 00000000000..f916f692bec --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout-sets.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout-sets.schema.v1.json", + "sets": [ + { + "id": "component", + "tasks": [ + "component" + ] + }, + { + "id": "layout", + "tasks": [ + "layout" + ] + }, + { + "id": "component2", + "tasks": [ + "component2" + ] + }, + { + "id": "layout2", + "tasks": [ + "layout2" + ] + }, + { + "id": "layoutSet2", + "tasks": [ + "layoutSet2" + ] + } + ], + "uiSettings": {} +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout/Settings.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout/Settings.json new file mode 100644 index 00000000000..b40e33a3211 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout/Settings.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://altinncdn.no/schemas/json/layout/layoutSettings.schema.v1.json", + "pages": { + "order": [ + "Side1" + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout/layouts/Side1.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout/layouts/Side1.json new file mode 100644 index 00000000000..2f467af518a --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout/layouts/Side1.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "target": { + "type": "layoutSet", + "id": "", + "taskId": "" + }, + "id": "Summary2-FEI1HC", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side1", + "taskId": "" + }, + "id": "Summary2-e2yYpk", + "type": "Summary2" + }, + { + "id": "NavigationButtons-7g3XcW", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout2/Settings.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout2/Settings.json new file mode 100644 index 00000000000..6bb44cf6db8 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout2/Settings.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://altinncdn.no/schemas/json/layout/layoutSettings.schema.v1.json", + "pages": { + "order": [ + "Side1", + "Side2" + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout2/layouts/Side1.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout2/layouts/Side1.json new file mode 100644 index 00000000000..2530921cc4e --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout2/layouts/Side1.json @@ -0,0 +1,53 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "dataModelBindings": { + "simpleBinding": "" + }, + "id": "Input-QCSonu", + "type": "Input" + }, + { + "target": { + "type": "layoutSet", + "id": "" + }, + "id": "Summary2-18hFaH", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side1" + }, + "id": "Summary2-iZJ80j", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side2", + "taskId": "" + }, + "id": "Summary2-V55C6Q", + "type": "Summary2" + }, + { + "target": { + "type": "component", + "id": "Input-QCSonu" + }, + "id": "Summary2-aI91Tv", + "type": "Summary2" + }, + { + "id": "NavigationButtons-2hGPi1", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout2/layouts/Side2.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout2/layouts/Side2.json new file mode 100644 index 00000000000..edd72d878e9 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layout2/layouts/Side2.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "target": { + "type": "layoutSet", + "id": "", + "taskId": "layout" + }, + "id": "Summary2-MfRiX8", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side1", + "taskId": "layout" + }, + "id": "Summary2-66YavC", + "type": "Summary2" + }, + { + "id": "NavigationButtons-h0g3Wt", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layoutSet2/Settings.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layoutSet2/Settings.json new file mode 100644 index 00000000000..6bb44cf6db8 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layoutSet2/Settings.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://altinncdn.no/schemas/json/layout/layoutSettings.schema.v1.json", + "pages": { + "order": [ + "Side1", + "Side2" + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layoutSet2/layouts/Side1.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layoutSet2/layouts/Side1.json new file mode 100644 index 00000000000..60fc1029c15 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layoutSet2/layouts/Side1.json @@ -0,0 +1,54 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "dataModelBindings": { + "simpleBinding": "" + }, + "id": "Input-wrspcN", + "type": "Input" + }, + { + "target": { + "type": "layoutSet", + "id": "" + }, + "id": "Summary2-00SFBO", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side1" + }, + "id": "Summary2-4069IB", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side2", + "taskId": "" + }, + "id": "Summary2-Orxmu1", + "type": "Summary2" + }, + { + "target": { + "type": "component", + "id": "Input-wrspcN", + "taskId": "" + }, + "id": "Summary2-2YoJGY", + "type": "Summary2" + }, + { + "id": "NavigationButtons-KnXA9y", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layoutSet2/layouts/Side2.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layoutSet2/layouts/Side2.json new file mode 100644 index 00000000000..ba9b66dff54 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components-after-deleting-references/App/ui/layoutSet2/layouts/Side2.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "id": "NavigationButtons-5ukC2N", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component/Settings.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component/Settings.json new file mode 100644 index 00000000000..6bb44cf6db8 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component/Settings.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://altinncdn.no/schemas/json/layout/layoutSettings.schema.v1.json", + "pages": { + "order": [ + "Side1", + "Side2" + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component/layouts/Side1.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component/layouts/Side1.json new file mode 100644 index 00000000000..112caa4c318 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component/layouts/Side1.json @@ -0,0 +1,64 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "target": { + "type": "layoutSet", + "id": "", + "taskId": "" + }, + "id": "Summary2-9iG1lB", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side1", + "taskId": "" + }, + "id": "Summary2-R8HsuB", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side2", + "taskId": "" + }, + "id": "Summary2-mL8NjJ", + "type": "Summary2", + "overrides": [ + { + "componentId": "Input-Om7N3y", + "displayType": "string" + } + ] + }, + { + "target": { + "type": "component", + "id": "Input-qWr0oa", + "taskId": "" + }, + "id": "Summary2-0BV88Q", + "type": "Summary2" + }, + { + "target": { + "type": "component", + "id": "Input-Om7N3y", + "taskId": "" + }, + "id": "Summary2-dTepe0", + "type": "Summary2" + }, + { + "id": "NavigationButtons-DfcNol", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component/layouts/Side2.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component/layouts/Side2.json new file mode 100644 index 00000000000..3989fafc6be --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component/layouts/Side2.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "dataModelBindings": { + "simpleBinding": "" + }, + "id": "Input-Om7N3y", + "type": "Input" + }, + { + "dataModelBindings": { + "simpleBinding": "" + }, + "id": "Input-qWr0oa", + "type": "Input" + }, + { + "id": "NavigationButtons-GAW8Dx", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component2/Settings.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component2/Settings.json new file mode 100644 index 00000000000..6bb44cf6db8 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component2/Settings.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://altinncdn.no/schemas/json/layout/layoutSettings.schema.v1.json", + "pages": { + "order": [ + "Side1", + "Side2" + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component2/layouts/Side1.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component2/layouts/Side1.json new file mode 100644 index 00000000000..8489464261c --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component2/layouts/Side1.json @@ -0,0 +1,56 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "dataModelBindings": { + "simpleBinding": "" + }, + "id": "Input-LCr3oK", + "type": "Input" + }, + { + "target": { + "type": "layoutSet", + "id": "", + "taskId": "" + }, + "id": "Summary2-eoT5QK", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side1", + "taskId": "" + }, + "id": "Summary2-NJfE92", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side2", + "taskId": "" + }, + "id": "Summary2-BGHvph", + "type": "Summary2" + }, + { + "target": { + "type": "component", + "id": "Input-LCr3oK", + "taskId": "" + }, + "id": "Summary2-rb2ml2", + "type": "Summary2" + }, + { + "id": "NavigationButtons-t7UTGJ", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component2/layouts/Side2.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component2/layouts/Side2.json new file mode 100644 index 00000000000..b754c0caf49 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/component2/layouts/Side2.json @@ -0,0 +1,64 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "target": { + "type": "layoutSet", + "id": "", + "taskId": "component" + }, + "id": "Summary2-2FSqcf", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side1", + "taskId": "component" + }, + "id": "Summary2-eGrDpP", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side2", + "taskId": "component" + }, + "id": "Summary2-uJDgMu", + "type": "Summary2", + "overrides": [ + { + "componentId": "Input-Om7N3y", + "displayType": "string" + } + ] + }, + { + "target": { + "type": "component", + "id": "Input-qWr0oa", + "taskId": "component" + }, + "id": "Summary2-2JiT3P", + "type": "Summary2" + }, + { + "target": { + "type": "component", + "id": "Input-Om7N3y", + "taskId": "component" + }, + "id": "Summary2-vIXkWO", + "type": "Summary2" + }, + { + "id": "NavigationButtons-BYcvaT", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout-sets.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout-sets.json new file mode 100644 index 00000000000..536ae9da2c2 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout-sets.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout-sets.schema.v1.json", + "sets": [ + { + "id": "component", + "tasks": [ + "component" + ] + }, + { + "id": "layout", + "tasks": [ + "layout" + ] + }, + { + "id": "layoutSet", + "tasks": [ + "layoutSet" + ] + }, + { + "id": "component2", + "tasks": [ + "component2" + ] + }, + { + "id": "layout2", + "tasks": [ + "layout2" + ] + }, + { + "id": "layoutSet2", + "tasks": [ + "layoutSet2" + ] + } + ], + "uiSettings": {} +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout/Settings.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout/Settings.json new file mode 100644 index 00000000000..6bb44cf6db8 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout/Settings.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://altinncdn.no/schemas/json/layout/layoutSettings.schema.v1.json", + "pages": { + "order": [ + "Side1", + "Side2" + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout/layouts/Side1.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout/layouts/Side1.json new file mode 100644 index 00000000000..a7e9078479d --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout/layouts/Side1.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "target": { + "type": "layoutSet", + "id": "", + "taskId": "" + }, + "id": "Summary2-FEI1HC", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side1", + "taskId": "" + }, + "id": "Summary2-e2yYpk", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side2", + "taskId": "" + }, + "id": "Summary2-qOn2O1", + "type": "Summary2" + }, + { + "target": { + "type": "component", + "id": "Input-AVRSNf", + "taskId": "" + }, + "id": "Summary2-da0Tq6", + "type": "Summary2" + }, + { + "id": "NavigationButtons-7g3XcW", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout/layouts/Side2.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout/layouts/Side2.json new file mode 100644 index 00000000000..8b8ea337e26 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout/layouts/Side2.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "dataModelBindings": { + "simpleBinding": "" + }, + "id": "Input-AVRSNf", + "type": "Input" + }, + { + "id": "NavigationButtons-wDmNQu", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout2/Settings.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout2/Settings.json new file mode 100644 index 00000000000..6bb44cf6db8 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout2/Settings.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://altinncdn.no/schemas/json/layout/layoutSettings.schema.v1.json", + "pages": { + "order": [ + "Side1", + "Side2" + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout2/layouts/Side1.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout2/layouts/Side1.json new file mode 100644 index 00000000000..2530921cc4e --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout2/layouts/Side1.json @@ -0,0 +1,53 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "dataModelBindings": { + "simpleBinding": "" + }, + "id": "Input-QCSonu", + "type": "Input" + }, + { + "target": { + "type": "layoutSet", + "id": "" + }, + "id": "Summary2-18hFaH", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side1" + }, + "id": "Summary2-iZJ80j", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side2", + "taskId": "" + }, + "id": "Summary2-V55C6Q", + "type": "Summary2" + }, + { + "target": { + "type": "component", + "id": "Input-QCSonu" + }, + "id": "Summary2-aI91Tv", + "type": "Summary2" + }, + { + "id": "NavigationButtons-2hGPi1", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout2/layouts/Side2.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout2/layouts/Side2.json new file mode 100644 index 00000000000..6f3ad889e8f --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layout2/layouts/Side2.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "target": { + "type": "layoutSet", + "id": "", + "taskId": "layout" + }, + "id": "Summary2-MfRiX8", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side1", + "taskId": "layout" + }, + "id": "Summary2-66YavC", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side2", + "taskId": "layout" + }, + "id": "Summary2-bmqvUb", + "type": "Summary2" + }, + { + "target": { + "type": "component", + "id": "Input-AVRSNf", + "taskId": "layout" + }, + "id": "Summary2-uuKXTD", + "type": "Summary2" + }, + { + "id": "NavigationButtons-h0g3Wt", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet/Settings.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet/Settings.json new file mode 100644 index 00000000000..6bb44cf6db8 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet/Settings.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://altinncdn.no/schemas/json/layout/layoutSettings.schema.v1.json", + "pages": { + "order": [ + "Side1", + "Side2" + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet/layouts/Side1.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet/layouts/Side1.json new file mode 100644 index 00000000000..48f8a1f9cf7 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet/layouts/Side1.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "id": "NavigationButtons-n2jUp6", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet/layouts/Side2.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet/layouts/Side2.json new file mode 100644 index 00000000000..6fa6e9455ff --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet/layouts/Side2.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "dataModelBindings": { + "simpleBinding": "" + }, + "id": "Input-hqcYqo", + "type": "Input" + }, + { + "id": "NavigationButtons-QzkEka", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet2/Settings.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet2/Settings.json new file mode 100644 index 00000000000..6bb44cf6db8 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet2/Settings.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://altinncdn.no/schemas/json/layout/layoutSettings.schema.v1.json", + "pages": { + "order": [ + "Side1", + "Side2" + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet2/layouts/Side1.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet2/layouts/Side1.json new file mode 100644 index 00000000000..60fc1029c15 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet2/layouts/Side1.json @@ -0,0 +1,54 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "dataModelBindings": { + "simpleBinding": "" + }, + "id": "Input-wrspcN", + "type": "Input" + }, + { + "target": { + "type": "layoutSet", + "id": "" + }, + "id": "Summary2-00SFBO", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side1" + }, + "id": "Summary2-4069IB", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side2", + "taskId": "" + }, + "id": "Summary2-Orxmu1", + "type": "Summary2" + }, + { + "target": { + "type": "component", + "id": "Input-wrspcN", + "taskId": "" + }, + "id": "Summary2-2YoJGY", + "type": "Summary2" + }, + { + "id": "NavigationButtons-KnXA9y", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet2/layouts/Side2.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet2/layouts/Side2.json new file mode 100644 index 00000000000..c49447d2904 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-summary2-components/App/ui/layoutSet2/layouts/Side2.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "target": { + "type": "layoutSet", + "id": "", + "taskId": "layoutSet" + }, + "id": "Summary2-6VQ3LC", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side1", + "taskId": "layoutSet" + }, + "id": "Summary2-LZxPWb", + "type": "Summary2" + }, + { + "target": { + "type": "page", + "id": "Side2", + "taskId": "layoutSet" + }, + "id": "Summary2-El9z2Y", + "type": "Summary2" + }, + { + "target": { + "type": "component", + "id": "Input-hqcYqo", + "taskId": "layoutSet" + }, + "id": "Summary2-SMqpYV", + "type": "Summary2" + }, + { + "id": "NavigationButtons-5ukC2N", + "showBackButton": true, + "textResourceBindings": {}, + "type": "NavigationButtons" + } + ] + } +} \ No newline at end of file diff --git a/frontend/language/src/nb.json b/frontend/language/src/nb.json index 17d7812770c..766987f430e 100644 --- a/frontend/language/src/nb.json +++ b/frontend/language/src/nb.json @@ -1206,7 +1206,7 @@ "ux_editor.component_category.select": "Flervalg", "ux_editor.component_category.text": "Tekst", "ux_editor.component_deletion_confirm": "Ja, slett komponenten", - "ux_editor.component_deletion_text": "Er du sikker på at du vil slette denne komponenten?", + "ux_editor.component_deletion_text": "Er du sikker på at du vil slette denne komponenten?\nAlle Summary2-komponenter knyttet til denne komponenten vil også bli slettet.", "ux_editor.component_dropdown_set_preselected": "Sett forhåndsvalgt verdi for nedtrekksliste", "ux_editor.component_group_deletion_text": "Er du sikker på at du vil slette denne gruppen?\nAlle komponenter i denne gruppen blir også slettet", "ux_editor.component_help_text.Accordion": "Med Trekkspilliste kan du presentere mye innhold på liten plass, i en eller flere rader. Brukerne kan klikke på hele raden for å vise eller skjule innholdet under.", @@ -1808,7 +1808,7 @@ "ux_editor.page_config_pdf_delete_existing_pdf": "Slett eksisterende PDF", "ux_editor.page_config_pdf_exclude_components_from_default_pdf": "Velg hvilke komponenter fra siden som skal skjules i standard PDF", "ux_editor.page_config_pdf_exclude_page_from_default_pdf": "Ekskluder siden fra standard PDF", - "ux_editor.page_delete_text": "Er du sikker på at du vil slette denne siden?\nAlt innholdet på siden vil bli fjernet.", + "ux_editor.page_delete_text": "Er du sikker på at du vil slette denne siden?\nAlt innholdet på siden vil bli fjernet.\nAlle Summary2-komponenter knyttet til denne siden vil også bli slettet.", "ux_editor.page_menu_down": "Flytt ned", "ux_editor.page_menu_edit": "Gi nytt navn", "ux_editor.page_menu_up": "Flytt opp", diff --git a/frontend/packages/process-editor/src/bpmnProviders/SupportedContextPadProvider.js b/frontend/packages/process-editor/src/bpmnProviders/SupportedContextPadProvider.js index 0971159f330..b124370dec0 100644 --- a/frontend/packages/process-editor/src/bpmnProviders/SupportedContextPadProvider.js +++ b/frontend/packages/process-editor/src/bpmnProviders/SupportedContextPadProvider.js @@ -16,7 +16,7 @@ class SupportedContextPadProvider { } const isConfirmed = confirm( - 'Prosess-steget du vil slette kan være knyttet til en sidegruppe. Den kan inneholde visningsoppsett eller skjema du har satt opp. Hvis du sletter steget, sletter du også hele sidegruppen og alt som hører til.', + 'Prosess-steget du vil slette kan være knyttet til en sidegruppe. Den kan inneholde visningsoppsett eller skjema du har satt opp. Hvis du sletter steget, sletter du også hele sidegruppen og alt som hører til.\nAlle Summary2-komponenter knyttet til dette prosess-steget vil også bli slettet.', ); if (isConfirmed) {