From 2b4ad09f85fd69f9fe83eb260bd500547643d2d4 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 20 Oct 2022 15:57:21 +0200 Subject: [PATCH 1/2] Fixed openByDefault to use filter --- .../form/containers/GroupContainer.tsx | 32 +++++------- .../layout/update/updateFormLayoutSagas.ts | 14 +++-- .../src/utils/formLayout.ts | 51 +++++++++++++++++++ 3 files changed, 74 insertions(+), 23 deletions(-) diff --git a/src/altinn-app-frontend/src/features/form/containers/GroupContainer.tsx b/src/altinn-app-frontend/src/features/form/containers/GroupContainer.tsx index a40128720e..a910d693a6 100644 --- a/src/altinn-app-frontend/src/features/form/containers/GroupContainer.tsx +++ b/src/altinn-app-frontend/src/features/form/containers/GroupContainer.tsx @@ -11,7 +11,10 @@ import { RepeatingGroupTable } from 'src/features/form/containers/RepeatingGroup import { FormLayoutActions } from 'src/features/form/layout/formLayoutSlice'; import { makeGetHidden } from 'src/selectors/getLayoutData'; import { Triggers } from 'src/types'; -import { createRepeatingGroupComponents } from 'src/utils/formLayout'; +import { + createRepeatingGroupComponents, + getRepeatingGroupFilteredIndices, +} from 'src/utils/formLayout'; import { getHiddenFieldsForGroup } from 'src/utils/layout'; import { renderValidationMessagesForComponent } from 'src/utils/render'; import { repeatingGroupHasValidations } from 'src/utils/validation'; @@ -152,26 +155,15 @@ export function GroupContainer({ ); React.useEffect(() => { - if (container.edit?.filter && container.edit.filter.length > 0) { - container.edit.filter.forEach((rule) => { - const formDataKeys: string[] = Object.keys(formData).filter((key) => { - const keyWithoutIndex = key.replaceAll(/\[\d*\]/g, ''); - return keyWithoutIndex === rule.key && formData[key] === rule.value; - }); - if (formDataKeys && formDataKeys.length > 0) { - const filtered = formDataKeys.map((key) => { - const match = key.match(/\[(\d*)\]/g); - const currentIndex = match[match.length - 1]; - return parseInt( - currentIndex.substring(1, currentIndex.indexOf(']')), - 10, - ); - }); - setFilteredIndexList(filtered); - } - }); + const filteredIndexList = getRepeatingGroupFilteredIndices( + repeatingGroupIndex, + formData, + container.edit?.filter, + ); + if (filteredIndexList) { + setFilteredIndexList(filteredIndexList); } - }, [formData, container]); + }, [repeatingGroupIndex, formData, container]); const onClickAdd = useCallback(() => { dispatch(FormLayoutActions.updateRepeatingGroups({ layoutElementId: id })); diff --git a/src/altinn-app-frontend/src/features/form/layout/update/updateFormLayoutSagas.ts b/src/altinn-app-frontend/src/features/form/layout/update/updateFormLayoutSagas.ts index 659348664b..60aa5e76b0 100644 --- a/src/altinn-app-frontend/src/features/form/layout/update/updateFormLayoutSagas.ts +++ b/src/altinn-app-frontend/src/features/form/layout/update/updateFormLayoutSagas.ts @@ -38,6 +38,7 @@ import { } from 'src/utils/databindings'; import { findChildren, + getRepeatingGroupFilteredIndices, getRepeatingGroups, mapFileUploadersWithTag, removeRepeatingGroupFromUIConfig, @@ -675,12 +676,19 @@ export function* initRepeatingGroupsSaga(): SagaIterator { const container = groupContainers.find( (element) => element.id === key, ) as ILayoutGroup; - if (container && group.index >= 0) { + const filteredIndexList = getRepeatingGroupFilteredIndices( + group.index, + formDataState.formData, + container.edit?.filter, + ); + if (container.edit?.openByDefault === 'first') { - group.editIndex = 0; + group.editIndex = filteredIndexList ? filteredIndexList[0] : 0; } else if (container.edit?.openByDefault === 'last') { - group.editIndex = group.index; + group.editIndex = filteredIndexList + ? filteredIndexList.at(-1) + : group.index; } } }); diff --git a/src/altinn-app-frontend/src/utils/formLayout.ts b/src/altinn-app-frontend/src/utils/formLayout.ts index 30c7c7a9d4..d208a0cdd4 100644 --- a/src/altinn-app-frontend/src/utils/formLayout.ts +++ b/src/altinn-app-frontend/src/utils/formLayout.ts @@ -1,7 +1,9 @@ import { INDEX_KEY_INDICATOR_REGEX } from 'src/utils/databindings'; +import type { IFormData } from 'src/features/form/data'; import type { ComponentTypes, IGroupEditProperties, + IGroupFilter, ILayout, ILayoutComponent, ILayoutGroup, @@ -555,3 +557,52 @@ export function behavesLikeDataTask( ): boolean { return layoutSets?.sets.some((set) => set.tasks?.includes(task)); } + +/** + * Returns a list of remaining repeating group element indices after all filters are applied. Returns undefined if no filters are present. + * @param repeatingGroupIndex IRepeatingGroup.index for the repeating group. + * @param formData IFormData + * @param filter IGroupEditProperties.filter or undefined. + * @returns a list of indices for repeating group elements after applying filters, or null if no filters are provided. + */ +export function getRepeatingGroupFilteredIndices( + repeatingGroupIndex: number, + formData: IFormData, + filter?: IGroupFilter[], +): number[] | null { + if (filter && filter.length > 0) { + let filteredIndicies = Array.from(Array(repeatingGroupIndex + 1).keys()); + + filter.forEach((rule) => { + const formDataKeys: string[] = Object.keys(formData); + + if (formDataKeys && formDataKeys.length > 0) { + const matchingSet = new Set(); + + formDataKeys + .filter((key) => { + const keyWithoutIndex = key.replaceAll(/\[\d*\]/g, ''); + return keyWithoutIndex === rule.key && formData[key] === rule.value; + }) + .forEach((key) => { + const match = key.match(/\[(\d*)\]/g); + const currentIndex = match[match.length - 1]; + const matchingIndex = parseInt( + currentIndex.substring(1, currentIndex.indexOf(']')), + 10, + ); + matchingSet.add(matchingIndex); + }); + + filteredIndicies = filteredIndicies.filter((index) => + matchingSet.has(index), + ); + return Array.from(matchingSet); + } + }); + + return filteredIndicies; + } + + return null; +} From ef0bb9a29e6c9a09968c84f4b43c6b67e1d38b44 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 21 Oct 2022 11:19:02 +0200 Subject: [PATCH 2/2] Preserve backward compatibility --- .../form/containers/GroupContainer.tsx | 3 +- .../layout/update/updateFormLayoutSagas.ts | 1 - .../src/utils/formLayout.ts | 53 +++++++------------ 3 files changed, 19 insertions(+), 38 deletions(-) diff --git a/src/altinn-app-frontend/src/features/form/containers/GroupContainer.tsx b/src/altinn-app-frontend/src/features/form/containers/GroupContainer.tsx index a910d693a6..5baf74c7e2 100644 --- a/src/altinn-app-frontend/src/features/form/containers/GroupContainer.tsx +++ b/src/altinn-app-frontend/src/features/form/containers/GroupContainer.tsx @@ -156,14 +156,13 @@ export function GroupContainer({ React.useEffect(() => { const filteredIndexList = getRepeatingGroupFilteredIndices( - repeatingGroupIndex, formData, container.edit?.filter, ); if (filteredIndexList) { setFilteredIndexList(filteredIndexList); } - }, [repeatingGroupIndex, formData, container]); + }, [formData, container]); const onClickAdd = useCallback(() => { dispatch(FormLayoutActions.updateRepeatingGroups({ layoutElementId: id })); diff --git a/src/altinn-app-frontend/src/features/form/layout/update/updateFormLayoutSagas.ts b/src/altinn-app-frontend/src/features/form/layout/update/updateFormLayoutSagas.ts index 60aa5e76b0..2864d55a3f 100644 --- a/src/altinn-app-frontend/src/features/form/layout/update/updateFormLayoutSagas.ts +++ b/src/altinn-app-frontend/src/features/form/layout/update/updateFormLayoutSagas.ts @@ -678,7 +678,6 @@ export function* initRepeatingGroupsSaga(): SagaIterator { ) as ILayoutGroup; if (container && group.index >= 0) { const filteredIndexList = getRepeatingGroupFilteredIndices( - group.index, formDataState.formData, container.edit?.filter, ); diff --git a/src/altinn-app-frontend/src/utils/formLayout.ts b/src/altinn-app-frontend/src/utils/formLayout.ts index d208a0cdd4..b24bf98548 100644 --- a/src/altinn-app-frontend/src/utils/formLayout.ts +++ b/src/altinn-app-frontend/src/utils/formLayout.ts @@ -559,50 +559,33 @@ export function behavesLikeDataTask( } /** - * Returns a list of remaining repeating group element indices after all filters are applied. Returns undefined if no filters are present. - * @param repeatingGroupIndex IRepeatingGroup.index for the repeating group. + * (Deprecate this function) Returns the filtered indices of a repeating group. + * This is a buggy implementation, but is used for backward compatibility until a new major version is released. + * @see https://github.com/Altinn/app-frontend-react/issues/339#issuecomment-1286624933 * @param formData IFormData * @param filter IGroupEditProperties.filter or undefined. - * @returns a list of indices for repeating group elements after applying filters, or null if no filters are provided. + * @returns a list of indices for repeating group elements after applying filters, or null if no filters are provided or if no elements match. */ export function getRepeatingGroupFilteredIndices( - repeatingGroupIndex: number, formData: IFormData, filter?: IGroupFilter[], ): number[] | null { if (filter && filter.length > 0) { - let filteredIndicies = Array.from(Array(repeatingGroupIndex + 1).keys()); - - filter.forEach((rule) => { - const formDataKeys: string[] = Object.keys(formData); - - if (formDataKeys && formDataKeys.length > 0) { - const matchingSet = new Set(); - - formDataKeys - .filter((key) => { - const keyWithoutIndex = key.replaceAll(/\[\d*\]/g, ''); - return keyWithoutIndex === rule.key && formData[key] === rule.value; - }) - .forEach((key) => { - const match = key.match(/\[(\d*)\]/g); - const currentIndex = match[match.length - 1]; - const matchingIndex = parseInt( - currentIndex.substring(1, currentIndex.indexOf(']')), - 10, - ); - matchingSet.add(matchingIndex); - }); - - filteredIndicies = filteredIndicies.filter((index) => - matchingSet.has(index), - ); - return Array.from(matchingSet); - } + const rule = filter.at(-1); + const formDataKeys: string[] = Object.keys(formData).filter((key) => { + const keyWithoutIndex = key.replaceAll(/\[\d*\]/g, ''); + return keyWithoutIndex === rule.key && formData[key] === rule.value; }); - - return filteredIndicies; + if (formDataKeys && formDataKeys.length > 0) { + return formDataKeys.map((key) => { + const match = key.match(/\[(\d*)\]/g); + const currentIndex = match[match.length - 1]; + return parseInt( + currentIndex.substring(1, currentIndex.indexOf(']')), + 10, + ); + }); + } } - return null; }