From ba86c10ed83cb3cd1e61eb228e5929ddf636b2e4 Mon Sep 17 00:00:00 2001 From: Ole Martin Handeland Date: Mon, 22 Sep 2025 11:35:40 +0200 Subject: [PATCH 1/6] Simplifying TaskStore by just using it as a normal context instead of using zustand. It is only really used to override things, and the pattern we used to wait until things were set in zustand clearly indicated this was the wrong approach. --- src/core/contexts/taskStoreContext.tsx | 72 ++++++---------- .../attachments/StoreAttachmentsInNode.tsx | 4 +- src/features/datamodel/DataModelsProvider.tsx | 7 +- src/features/datamodel/useBindingSchema.tsx | 6 +- src/features/form/layout/LayoutsContext.tsx | 4 +- .../form/layoutSets/useCurrentLayoutSet.ts | 4 +- src/features/instance/useProcessTaskId.ts | 4 +- .../containers/InstantiationContainer.tsx | 19 ++--- .../selection/InstanceSelection.tsx | 23 +++-- src/index.tsx | 67 +++++++-------- .../FileUploadTable/FileTableRow.tsx | 4 +- src/layout/Subform/SubformWrapper.tsx | 84 +++++-------------- .../Summary/SubformSummaryComponent2.tsx | 76 +++++++++-------- src/layout/Subform/index.tsx | 31 ++++--- .../CommonSummaryComponents/EditButton.tsx | 7 +- .../SummaryComponent2/SummaryComponent2.tsx | 13 ++- .../SummaryComponent2/TaskSummaryWrapper.tsx | 52 +++--------- src/test/renderWithProviders.tsx | 67 +++++++-------- src/utils/layout/all.test.tsx | 7 +- 19 files changed, 224 insertions(+), 327 deletions(-) diff --git a/src/core/contexts/taskStoreContext.tsx b/src/core/contexts/taskStoreContext.tsx index ceb9c6eb07..9b0c4567cd 100644 --- a/src/core/contexts/taskStoreContext.tsx +++ b/src/core/contexts/taskStoreContext.tsx @@ -1,51 +1,33 @@ -import React, { createContext, useContext, useState } from 'react'; +import React, { createContext, useContext } from 'react'; +import type { PropsWithChildren } from 'react'; -type TaskState = { - overriddenTaskId?: string; - overriddenDataModelType?: string; - overriddenDataElementId?: string; - overriddenLayoutSetId?: string; - depth?: number; -}; - -type TaskActions = { - setOverriddenLayoutSetId: (layoutSetId: string) => void; - setOverriddenDataModelType: (dataModelType: string) => void; - setOverriddenDataModelDataElementId: (dataElementId: string) => void; - setTaskId: (taskId: string) => void; - setDepth: (depth: number) => void; - clearTaskId: () => void; -}; - -const TaskContext = createContext<(TaskState & TaskActions) | null>(null); +interface TaskOverridesContext { + taskId?: string; + dataModelType?: string; + dataElementId?: string; + layoutSetId?: string; + depth: number; +} -export function TaskStoreProvider({ children }: React.PropsWithChildren) { - const [state, setState] = useState({ - overriddenTaskId: undefined, - overriddenDataModelType: undefined, - overriddenDataElementId: undefined, - overriddenLayoutSetId: undefined, - depth: 1, - }); +const Context = createContext({ depth: 1 }); - const actions: TaskActions = { - setTaskId: (overriddenTaskId: string) => setState((s) => ({ ...s, overriddenTaskId })), - setOverriddenLayoutSetId: (overriddenLayoutSetId: string) => setState((s) => ({ ...s, overriddenLayoutSetId })), - setOverriddenDataModelType: (overriddenDataModelType: string) => - setState((s) => ({ ...s, overriddenDataModelType })), - setOverriddenDataModelDataElementId: (overriddenDataElementId: string) => - setState((s) => ({ ...s, overriddenDataElementId })), - clearTaskId: () => setState((s) => ({ ...s, overriddenTaskId: '' })), - setDepth: (depth: number) => setState((s) => ({ ...s, depth })), - }; +type Props = PropsWithChildren & Omit; +export function TaskOverrides({ children, ...overrides }: Props) { + const parentContext = useContext(Context); - return {children}; + return ( + + {children} + + ); } -export const useTaskStore = (selector: (state: TaskState & TaskActions) => T) => { - const context = useContext(TaskContext); - if (!context) { - throw new Error('useTaskStore must be used within TaskStoreProvider'); - } - return selector(context); -}; +export const useTaskOverrides = () => useContext(Context); diff --git a/src/features/attachments/StoreAttachmentsInNode.tsx b/src/features/attachments/StoreAttachmentsInNode.tsx index 1857cbe9e6..43638d2e2f 100644 --- a/src/features/attachments/StoreAttachmentsInNode.tsx +++ b/src/features/attachments/StoreAttachmentsInNode.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; import deepEqual from 'fast-deep-equal'; -import { useTaskStore } from 'src/core/contexts/taskStoreContext'; +import { useTaskOverrides } from 'src/core/contexts/taskStoreContext'; import { useApplicationMetadata } from 'src/features/applicationMetadata/ApplicationMetadataProvider'; import { isAttachmentUploaded } from 'src/features/attachments/index'; import { DEFAULT_DEBOUNCE_TIMEOUT } from 'src/features/formData/types'; @@ -89,7 +89,7 @@ function useNodeAttachments(): AttachmentRecord { const { indexedId, baseId } = parent; const nodeData = useFormDataFor(baseId) as IComponentFormData>; - const overriddenTaskId = useTaskStore((state) => state.overriddenTaskId); + const overriddenTaskId = useTaskOverrides()?.taskId; const application = useApplicationMetadata(); const currentTask = useProcessQuery().data?.currentTask?.elementId; diff --git a/src/features/datamodel/DataModelsProvider.tsx b/src/features/datamodel/DataModelsProvider.tsx index 09e134e6cf..2be870c514 100644 --- a/src/features/datamodel/DataModelsProvider.tsx +++ b/src/features/datamodel/DataModelsProvider.tsx @@ -6,7 +6,7 @@ import deepEqual from 'fast-deep-equal'; import { createStore } from 'zustand'; import type { JSONSchema7 } from 'json-schema'; -import { useTaskStore } from 'src/core/contexts/taskStoreContext'; +import { useTaskOverrides } from 'src/core/contexts/taskStoreContext'; import { createZustandContext } from 'src/core/contexts/zustandContext'; import { DisplayError } from 'src/core/errorHandling/DisplayError'; import { Loader } from 'src/core/loading/Loader'; @@ -170,8 +170,9 @@ function DataModelsLoader() { const layoutSetId = useCurrentLayoutSetId(); // Subform - const overriddenDataElementId = useTaskStore((state) => state.overriddenDataElementId); - const overriddenDataType = useTaskStore((state) => state.overriddenDataModelType); + const overrides = useTaskOverrides(); + const overriddenDataElementId = overrides?.dataElementId; + const overriddenDataType = overrides?.dataModelType; // Find all data types referenced in dataModelBindings in the layout useEffect(() => { diff --git a/src/features/datamodel/useBindingSchema.tsx b/src/features/datamodel/useBindingSchema.tsx index 9ae752f133..202a80a248 100644 --- a/src/features/datamodel/useBindingSchema.tsx +++ b/src/features/datamodel/useBindingSchema.tsx @@ -2,7 +2,7 @@ import { useCallback, useMemo } from 'react'; import type { JSONSchema7 } from 'json-schema'; -import { useTaskStore } from 'src/core/contexts/taskStoreContext'; +import { useTaskOverrides } from 'src/core/contexts/taskStoreContext'; import { useApplicationMetadata } from 'src/features/applicationMetadata/ApplicationMetadataProvider'; import { getCurrentDataTypeForApplication, @@ -34,7 +34,7 @@ export function useCurrentDataModelDataElementId() { const layoutSets = useLayoutSets(); const taskId = useProcessTaskId(); - const overriddenDataElementId = useTaskStore((s) => s.overriddenDataElementId); + const overriddenDataElementId = useTaskOverrides()?.dataElementId; // Instance data elements will update often (after each save), so we have to use a selector to make // sure components don't re-render too often. @@ -130,7 +130,7 @@ export function useDataModelUrl({ dataType, dataElementId, language, prefillFrom } export function useCurrentDataModelName() { - const overriddenDataModelType = useTaskStore((state) => state.overriddenDataModelType); + const overriddenDataModelType = useTaskOverrides()?.dataModelType; const application = useApplicationMetadata(); const layoutSets = useLayoutSets(); diff --git a/src/features/form/layout/LayoutsContext.tsx b/src/features/form/layout/LayoutsContext.tsx index a590aaa5fd..0a21a2fa08 100644 --- a/src/features/form/layout/LayoutsContext.tsx +++ b/src/features/form/layout/LayoutsContext.tsx @@ -6,7 +6,7 @@ import { useAppQueries } from 'src/core/contexts/AppQueriesProvider'; import { ContextNotProvided } from 'src/core/contexts/context'; import { delayedContext } from 'src/core/contexts/delayedContext'; import { createQueryContext } from 'src/core/contexts/queryContext'; -import { useTaskStore } from 'src/core/contexts/taskStoreContext'; +import { useTaskOverrides } from 'src/core/contexts/taskStoreContext'; import { useCurrentDataModelName } from 'src/features/datamodel/useBindingSchema'; import { cleanLayout } from 'src/features/form/layout/cleanLayout'; import { makeLayoutLookups } from 'src/features/form/layout/makeLayoutLookups'; @@ -83,7 +83,7 @@ export function useLayoutSetId() { const currentProcessLayoutSetId = useCurrentLayoutSetId(); const taskId = useNavigationParam('taskId'); - const overriddenLayoutSetId = useTaskStore((state) => state.overriddenLayoutSetId); + const overriddenLayoutSetId = useTaskOverrides()?.layoutSetId; if (overriddenLayoutSetId) { return overriddenLayoutSetId; diff --git a/src/features/form/layoutSets/useCurrentLayoutSet.ts b/src/features/form/layoutSets/useCurrentLayoutSet.ts index 7da7aff85d..c7dd48f531 100644 --- a/src/features/form/layoutSets/useCurrentLayoutSet.ts +++ b/src/features/form/layoutSets/useCurrentLayoutSet.ts @@ -1,5 +1,5 @@ import { ContextNotProvided } from 'src/core/contexts/context'; -import { useTaskStore } from 'src/core/contexts/taskStoreContext'; +import { useTaskOverrides } from 'src/core/contexts/taskStoreContext'; import { useLaxApplicationMetadata } from 'src/features/applicationMetadata/ApplicationMetadataProvider'; import { getCurrentLayoutSet } from 'src/features/applicationMetadata/appMetadataUtils'; import { useLaxLayoutSets } from 'src/features/form/layoutSets/LayoutSetsProvider'; @@ -13,7 +13,7 @@ export function useCurrentLayoutSet() { const application = useLaxApplicationMetadata(); const layoutSets = useLaxLayoutSets(); const taskId = useProcessTaskId(); - const overriddenLayoutSetId = useTaskStore((state) => state.overriddenLayoutSetId); + const overriddenLayoutSetId = useTaskOverrides()?.layoutSetId; if (application === ContextNotProvided || layoutSets === ContextNotProvided) { return undefined; diff --git a/src/features/instance/useProcessTaskId.ts b/src/features/instance/useProcessTaskId.ts index 188d71d2a0..81b0ac3e71 100644 --- a/src/features/instance/useProcessTaskId.ts +++ b/src/features/instance/useProcessTaskId.ts @@ -1,9 +1,9 @@ -import { useTaskStore } from 'src/core/contexts/taskStoreContext'; +import { useTaskOverrides } from 'src/core/contexts/taskStoreContext'; import { useProcessQuery } from 'src/features/instance/useProcessQuery'; import { useNavigationParam } from 'src/hooks/navigation'; export function useProcessTaskId() { - const overriddenTaskId = useTaskStore((state) => state.overriddenTaskId); + const overriddenTaskId = useTaskOverrides()?.taskId; const processTaskId = useProcessQuery().data?.currentTask?.elementId; const urlTaskId = useNavigationParam('taskId'); return overriddenTaskId ?? processTaskId ?? urlTaskId; diff --git a/src/features/instantiate/containers/InstantiationContainer.tsx b/src/features/instantiate/containers/InstantiationContainer.tsx index 90fcb8a06b..e2e763df4b 100644 --- a/src/features/instantiate/containers/InstantiationContainer.tsx +++ b/src/features/instantiate/containers/InstantiationContainer.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { ReadyForPrint } from 'src/components/ReadyForPrint'; -import { TaskStoreProvider } from 'src/core/contexts/taskStoreContext'; import { RenderStart } from 'src/core/ui/RenderStart'; import { Footer } from 'src/features/footer/Footer'; import classes from 'src/features/instantiate/containers/InstantiationContainer.module.css'; @@ -19,15 +18,13 @@ export function InstantiationContainer({ children }: IInstantiateContainerProps) const profile = useProfile(); return ( - - -
- -
{children}
-
- -
-
-
+ +
+ +
{children}
+
+ +
+
); } diff --git a/src/features/instantiate/selection/InstanceSelection.tsx b/src/features/instantiate/selection/InstanceSelection.tsx index 572d52e90e..f6392c1677 100644 --- a/src/features/instantiate/selection/InstanceSelection.tsx +++ b/src/features/instantiate/selection/InstanceSelection.tsx @@ -10,7 +10,6 @@ import { ErrorListFromInstantiation, ErrorReport } from 'src/components/message/ import { PresentationComponent } from 'src/components/presentation/Presentation'; import { ReadyForPrint } from 'src/components/ReadyForPrint'; import { useIsProcessing } from 'src/core/contexts/processingContext'; -import { TaskStoreProvider } from 'src/core/contexts/taskStoreContext'; import { useAppName, useAppOwner } from 'src/core/texts/appTexts'; import { useApplicationMetadata } from 'src/features/applicationMetadata/ApplicationMetadataProvider'; import { @@ -44,16 +43,14 @@ function getDateDisplayString(timeStamp: string) { } export const InstanceSelectionWrapper = () => ( - - - - - - - + + + + + ); function InstanceSelection() { @@ -237,7 +234,7 @@ function InstanceSelection() { ); return ( - + <> {`${getPageTitle(appName, langAsString('instance_selection.left_of'), appOwner)}`}
@@ -289,7 +286,7 @@ function InstanceSelection() {
-
+ ); } diff --git a/src/index.tsx b/src/index.tsx index 85c330901c..b9b3930c83 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -22,7 +22,6 @@ import { ViewportWrapper } from 'src/components/ViewportWrapper'; import { KeepAliveProvider } from 'src/core/auth/KeepAliveProvider'; import { AppQueriesProvider } from 'src/core/contexts/AppQueriesProvider'; import { ProcessingProvider } from 'src/core/contexts/processingContext'; -import { TaskStoreProvider } from 'src/core/contexts/taskStoreContext'; import { DisplayErrorProvider } from 'src/core/errorHandling/DisplayErrorProvider'; import { ApplicationMetadataProvider } from 'src/features/applicationMetadata/ApplicationMetadataProvider'; import { ApplicationSettingsProvider } from 'src/features/applicationSettings/ApplicationSettingsProvider'; @@ -93,41 +92,37 @@ function Root() { return ( <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + ); } diff --git a/src/layout/FileUpload/FileUploadTable/FileTableRow.tsx b/src/layout/FileUpload/FileUploadTable/FileTableRow.tsx index b93837e4d4..49f645e3f0 100644 --- a/src/layout/FileUpload/FileUploadTable/FileTableRow.tsx +++ b/src/layout/FileUpload/FileUploadTable/FileTableRow.tsx @@ -3,7 +3,7 @@ import React from 'react'; import classNames from 'classnames'; import { AltinnLoader } from 'src/components/AltinnLoader'; -import { useTaskStore } from 'src/core/contexts/taskStoreContext'; +import { useTaskOverrides } from 'src/core/contexts/taskStoreContext'; import { isAttachmentUploaded } from 'src/features/attachments'; import { FileScanResults } from 'src/features/attachments/types'; import { Lang } from 'src/features/language/Lang'; @@ -40,7 +40,7 @@ export function FileTableRow({ const pdfModeActive = usePdfModeActive(); const readableSize = getSizeWithUnit(attachment.data.size, 2); - const overriddenTaskId = useTaskStore((state) => state.overriddenTaskId); + const overriddenTaskId = useTaskOverrides()?.taskId; const hasOverridenTaskId = !!overriddenTaskId; diff --git a/src/layout/Subform/SubformWrapper.tsx b/src/layout/Subform/SubformWrapper.tsx index b338c50298..7c5a174cf6 100644 --- a/src/layout/Subform/SubformWrapper.tsx +++ b/src/layout/Subform/SubformWrapper.tsx @@ -3,7 +3,7 @@ import type { PropsWithChildren } from 'react'; import { Form } from 'src/components/form/Form'; import { PresentationComponent } from 'src/components/presentation/Presentation'; -import { useTaskStore } from 'src/core/contexts/taskStoreContext'; +import { TaskOverrides } from 'src/core/contexts/taskStoreContext'; import { Loader } from 'src/core/loading/Loader'; import { FormProvider } from 'src/features/form/FormContext'; import { useDataTypeFromLayoutSet } from 'src/features/form/layout/LayoutsContext'; @@ -14,13 +14,11 @@ import { ProcessTaskType } from 'src/types'; import { useItemWhenType } from 'src/utils/layout/useNodeItem'; export function SubformWrapper({ baseComponentId, children }: PropsWithChildren<{ baseComponentId: string }>) { - const isDone = useDoOverride(baseComponentId); - - if (!isDone) { - return ; - } - - return {children}; + return ( + + {children} + + ); } export function SubformForm() { @@ -44,35 +42,14 @@ export const RedirectBackToMainForm = () => { return ; }; -export const useDoOverrideSummary = (dataElementId: string, layoutSet: string, dataType: string) => { - const setOverriddenLayoutSetId = useTaskStore((state) => state.setOverriddenLayoutSetId); - const setOverriddenDataModelType = useTaskStore((state) => state.setOverriddenDataModelType); - const setOverriddenDataModelDataElementId = useTaskStore((state) => state.setOverriddenDataModelDataElementId); - - const isDone = useTaskStore( - (s) => - s.overriddenDataModelType === dataType && - s.overriddenDataElementId === dataElementId && - s.overriddenLayoutSetId === layoutSet, - ); - - useEffect(() => { - setOverriddenLayoutSetId?.(layoutSet); - setOverriddenDataModelType?.(dataType); - setOverriddenDataModelDataElementId?.(dataElementId!); - }, [ - dataElementId, - dataType, - layoutSet, - setOverriddenDataModelType, - setOverriddenDataModelDataElementId, - setOverriddenLayoutSetId, - ]); - - return isDone; -}; - -export const useDoOverride = (baseComponentId: string, providedDataElementId?: string) => { +export function SubformOverrideWrapper({ + baseComponentId, + providedDataElementId, + children, +}: PropsWithChildren<{ + baseComponentId: string; + providedDataElementId?: string; +}>) { const dataElementId = useNavigationParam('dataElementId'); const actualDataElementId = providedDataElementId ? providedDataElementId : dataElementId; const { layoutSet, id } = useItemWhenType(baseComponentId, 'Subform'); @@ -82,28 +59,13 @@ export const useDoOverride = (baseComponentId: string, providedDataElementId?: s throw new Error(`Unable to find data type for subform with id ${id}`); } - const setOverriddenLayoutSetId = useTaskStore((state) => state.setOverriddenLayoutSetId); - const setOverriddenDataModelType = useTaskStore((state) => state.setOverriddenDataModelType); - const setOverriddenDataModelDataElementId = useTaskStore((state) => state.setOverriddenDataModelDataElementId); - const isDone = useTaskStore( - (s) => - s.overriddenDataModelType === dataType && - s.overriddenDataElementId === actualDataElementId && - s.overriddenLayoutSetId === layoutSet, + return ( + + {children} + ); - - useEffect(() => { - setOverriddenLayoutSetId?.(layoutSet); - setOverriddenDataModelType?.(dataType); - setOverriddenDataModelDataElementId?.(actualDataElementId!); - }, [ - actualDataElementId, - dataType, - layoutSet, - setOverriddenDataModelType, - setOverriddenDataModelDataElementId, - setOverriddenLayoutSetId, - ]); - - return isDone; -}; +} diff --git a/src/layout/Subform/Summary/SubformSummaryComponent2.tsx b/src/layout/Subform/Summary/SubformSummaryComponent2.tsx index 3e7d925096..3408e35a9d 100644 --- a/src/layout/Subform/Summary/SubformSummaryComponent2.tsx +++ b/src/layout/Subform/Summary/SubformSummaryComponent2.tsx @@ -1,16 +1,15 @@ -import React, { type PropsWithChildren } from 'react'; +import React, { Fragment, type PropsWithChildren } from 'react'; import { Heading, Paragraph } from '@digdir/designsystemet-react'; import { Flex } from 'src/app-components/Flex/Flex'; import { Label, LabelInner } from 'src/components/label/Label'; -import { TaskStoreProvider } from 'src/core/contexts/taskStoreContext'; +import { TaskOverrides } from 'src/core/contexts/taskStoreContext'; import { useApplicationMetadata } from 'src/features/applicationMetadata/ApplicationMetadataProvider'; import { FormProvider } from 'src/features/form/FormContext'; import { useDataTypeFromLayoutSet, useLayoutLookups } from 'src/features/form/layout/LayoutsContext'; import { useInstanceDataElements } from 'src/features/instance/InstanceContext'; import { Lang } from 'src/features/language/Lang'; -import { useDoOverrideSummary } from 'src/layout/Subform/SubformWrapper'; import classes from 'src/layout/Subform/Summary/SubformSummaryComponent2.module.css'; import { SubformSummaryTable } from 'src/layout/Subform/Summary/SubformSummaryTable'; import { @@ -56,7 +55,7 @@ const SummarySubformWrapperInner = ({ )} {dataElements?.map((element, idx) => ( - +
- + ))} ); @@ -88,12 +87,11 @@ const DoSummaryWrapper = ({ baseComponentId: string; }>) => { const item = useItemWhenType(baseComponentId, 'Subform'); - const isDone = useDoOverrideSummary(dataElement.id, layoutSet, dataElement.dataType); const { isSubformDataFetching, subformData, subformDataError } = useSubformFormData(dataElement.id); const subformDataSources = useExpressionDataSourcesForSubform(dataElement.dataType, subformData, entryDisplayName); - if (!isDone || isSubformDataFetching) { + if (isSubformDataFetching) { return null; } @@ -104,36 +102,42 @@ const DoSummaryWrapper = ({ return (
- - - -
- - {subformEntryName && ( - - {subformEntryName} - - )} -
+ + + + +
+ + {subformEntryName && ( + + {subformEntryName} + + )} +
+
+
- -
-
+ +
); }; diff --git a/src/layout/Subform/index.tsx b/src/layout/Subform/index.tsx index f99eafc4ee..923d1d0a24 100644 --- a/src/layout/Subform/index.tsx +++ b/src/layout/Subform/index.tsx @@ -2,7 +2,6 @@ import React, { forwardRef } from 'react'; import { Route, Routes } from 'react-router-dom'; import type { JSX, ReactNode } from 'react'; -import { TaskStoreProvider } from 'src/core/contexts/taskStoreContext'; import { type ComponentValidation } from 'src/features/validation'; import { type SummaryRendererProps } from 'src/layout/LayoutComponent'; import { SubformDef } from 'src/layout/Subform/config.def.generated'; @@ -25,22 +24,20 @@ export class Subform extends SubformDef implements ValidateComponent, SubRouting subRouting({ baseComponentId }: { baseComponentId: string }): ReactNode { return ( - - - - - - } - /> - } - /> - - + + + + + } + /> + } + /> + ); } diff --git a/src/layout/Summary2/CommonSummaryComponents/EditButton.tsx b/src/layout/Summary2/CommonSummaryComponents/EditButton.tsx index 6561eec026..a64eaccc9a 100644 --- a/src/layout/Summary2/CommonSummaryComponents/EditButton.tsx +++ b/src/layout/Summary2/CommonSummaryComponents/EditButton.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { PencilIcon } from '@navikt/aksel-icons'; import { Button } from 'src/app-components/Button/Button'; -import { useTaskStore } from 'src/core/contexts/taskStoreContext'; +import { useTaskOverrides } from 'src/core/contexts/taskStoreContext'; import { useSetReturnToView, useSetSummaryNodeOfOrigin } from 'src/features/form/layout/PageNavigationContext'; import { Lang } from 'src/features/language/Lang'; import { useLanguage } from 'src/features/language/useLanguage'; @@ -66,8 +66,9 @@ export function EditButton({ const titleTrb = textResourceBindings && 'title' in textResourceBindings ? textResourceBindings.title : undefined; const accessibleTitle = titleTrb ? langAsString(titleTrb) : ''; - const overriddenTaskId = useTaskStore((state) => state.overriddenTaskId); - const overriddenDataElementId = useTaskStore((state) => state.overriddenDataElementId); + const overrides = useTaskOverrides(); + const overriddenTaskId = overrides?.taskId; + const overriddenDataElementId = overrides?.dataElementId; const indexedId = useIndexedId(targetBaseComponentId, skipLastIdMutator); const summary2Id = useSummaryProp('id'); diff --git a/src/layout/Summary2/SummaryComponent2/SummaryComponent2.tsx b/src/layout/Summary2/SummaryComponent2/SummaryComponent2.tsx index 286e491227..bb66f7618c 100644 --- a/src/layout/Summary2/SummaryComponent2/SummaryComponent2.tsx +++ b/src/layout/Summary2/SummaryComponent2/SummaryComponent2.tsx @@ -1,6 +1,5 @@ import React from 'react'; -import { TaskStoreProvider } from 'src/core/contexts/taskStoreContext'; import { ComponentSummary } from 'src/layout/Summary2/SummaryComponent2/ComponentSummary'; import { LayoutSetSummary } from 'src/layout/Summary2/SummaryComponent2/LayoutSetSummary'; import { TaskSummaryWrapper } from 'src/layout/Summary2/SummaryComponent2/TaskSummaryWrapper'; @@ -29,13 +28,11 @@ function SummaryBody({ target }: SummaryBodyProps) { function SummaryComponent2Inner({ baseComponentId }: Pick, 'baseComponentId'>) { const target = useExternalItem(baseComponentId, 'Summary2').target; return ( - - - - - - - + + + + + ); } diff --git a/src/layout/Summary2/SummaryComponent2/TaskSummaryWrapper.tsx b/src/layout/Summary2/SummaryComponent2/TaskSummaryWrapper.tsx index d08db2552a..2ec8354c01 100644 --- a/src/layout/Summary2/SummaryComponent2/TaskSummaryWrapper.tsx +++ b/src/layout/Summary2/SummaryComponent2/TaskSummaryWrapper.tsx @@ -1,6 +1,6 @@ -import React, { useEffect } from 'react'; +import React from 'react'; -import { useTaskStore } from 'src/core/contexts/taskStoreContext'; +import { TaskOverrides } from 'src/core/contexts/taskStoreContext'; import { FormProvider } from 'src/features/form/FormContext'; import { useLayoutSets } from 'src/features/form/layoutSets/LayoutSetsProvider'; import { useNavigationParam } from 'src/hooks/navigation'; @@ -12,43 +12,17 @@ interface TaskSummaryProps { } export function TaskSummaryWrapper({ taskId, children }: React.PropsWithChildren) { - const setTaskId = useTaskStore((state) => state.setTaskId); - const setOverriddenDataModelType = useTaskStore((state) => state.setOverriddenDataModelType); - const setOverriddenDataModelDataElementId = useTaskStore((state) => state.setOverriddenDataModelDataElementId); - const setOverriddenLayoutSetId = useTaskStore((state) => state.setOverriddenLayoutSetId); - const currentTaskId = useNavigationParam('taskId'); - const overriddenTaskId = useTaskStore((state) => state.overriddenTaskId); - const notCurrentTask = overriddenTaskId && overriddenTaskId !== currentTaskId; - const layoutSets = useLayoutSets(); - - useEffect(() => { - if (taskId) { - const layoutSetForTask = layoutSets.find((set) => set.tasks?.includes(taskId)); - setTaskId && setTaskId(taskId); - if (layoutSetForTask) { - setOverriddenDataModelType && setOverriddenDataModelType(layoutSetForTask.dataType); - setOverriddenLayoutSetId && setOverriddenLayoutSetId(layoutSetForTask.id); - } - } - }, [ - layoutSets, - setOverriddenDataModelType, - setOverriddenDataModelDataElementId, - setOverriddenLayoutSetId, - setTaskId, - taskId, - ]); - - if (taskId && overriddenTaskId !== taskId) { - // Wait for the task to be set correctly - return null; - } - - if (notCurrentTask) { - return {children}; - } - - return children; + const layoutSetForTask = taskId ? layoutSets.find((set) => set.tasks?.includes(taskId)) : undefined; + + return ( + + {taskId && taskId !== currentTaskId ? {children} : children} + + ); } diff --git a/src/test/renderWithProviders.tsx b/src/test/renderWithProviders.tsx index 7e9b3b2a57..fd3db7d6ba 100644 --- a/src/test/renderWithProviders.tsx +++ b/src/test/renderWithProviders.tsx @@ -20,7 +20,6 @@ import { getPartyMock } from 'src/__mocks__/getPartyMock'; import { paymentResponsePayload } from 'src/__mocks__/getPaymentPayloadMock'; import { getTextResourcesMock } from 'src/__mocks__/getTextResourcesMock'; import { AppQueriesProvider } from 'src/core/contexts/AppQueriesProvider'; -import { TaskStoreProvider } from 'src/core/contexts/taskStoreContext'; import { RenderStart } from 'src/core/ui/RenderStart'; import { ApplicationMetadataProvider } from 'src/features/applicationMetadata/ApplicationMetadataProvider'; import { ApplicationSettingsProvider } from 'src/features/applicationSettings/ApplicationSettingsProvider'; @@ -330,34 +329,32 @@ function DefaultProviders({ children, queries, queryClient, Router = DefaultRout queryClient={queryClient} > - - - - - - - - - - - - - - - {children} - - - - - - - - - - - - - + + + + + + + + + + + + + + {children} + + + + + + + + + + + + ); @@ -383,13 +380,11 @@ function MinimalProviders({ children, queries, queryClient, Router = DefaultRout {...queries} queryClient={queryClient} > - - - - {children} - - - + + + {children} + + ); } diff --git a/src/utils/layout/all.test.tsx b/src/utils/layout/all.test.tsx index 8c7bbf47c0..70d7c9698d 100644 --- a/src/utils/layout/all.test.tsx +++ b/src/utils/layout/all.test.tsx @@ -9,7 +9,6 @@ import type { JSONSchema7 } from 'json-schema'; import { ignoredConsoleMessages } from 'test/e2e/support/fail-on-console-log'; -import { TaskStoreProvider } from 'src/core/contexts/taskStoreContext'; import { quirks } from 'src/features/form/layout/quirks'; import { GenericComponent } from 'src/layout/GenericComponent'; import { SubformWrapper } from 'src/layout/Subform/SubformWrapper'; @@ -80,11 +79,7 @@ function RenderAllComponents() { * Makes sure we go one level deeper into the subform context when testing subforms */ function SubformTestWrapper({ baseId, children }: PropsWithChildren<{ baseId: string }>) { - return ( - - {children} - - ); + return {children}; } const windowLoggers = ['logError', 'logErrorOnce', 'logWarn', 'logWarnOnce', 'logInfo', 'logInfoOnce']; From 01c51d3a6d55c540e8feba94c004384002c6963c Mon Sep 17 00:00:00 2001 From: Ole Martin Handeland Date: Mon, 22 Sep 2025 16:03:55 +0200 Subject: [PATCH 2/6] Renaming file, renaming dataElementId -> dataModelElementId --- src/core/contexts/{taskStoreContext.tsx => TaskOverrides.tsx} | 4 ++-- src/features/attachments/StoreAttachmentsInNode.tsx | 2 +- src/features/datamodel/DataModelsProvider.tsx | 4 ++-- src/features/datamodel/useBindingSchema.tsx | 4 ++-- src/features/form/layout/LayoutsContext.tsx | 2 +- src/features/form/layoutSets/useCurrentLayoutSet.ts | 2 +- src/features/instance/useProcessTaskId.ts | 2 +- src/layout/FileUpload/FileUploadTable/FileTableRow.tsx | 2 +- src/layout/Subform/SubformWrapper.tsx | 4 ++-- src/layout/Subform/Summary/SubformSummaryComponent2.tsx | 4 ++-- src/layout/Summary2/CommonSummaryComponents/EditButton.tsx | 4 ++-- src/layout/Summary2/SummaryComponent2/TaskSummaryWrapper.tsx | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) rename src/core/contexts/{taskStoreContext.tsx => TaskOverrides.tsx} (87%) diff --git a/src/core/contexts/taskStoreContext.tsx b/src/core/contexts/TaskOverrides.tsx similarity index 87% rename from src/core/contexts/taskStoreContext.tsx rename to src/core/contexts/TaskOverrides.tsx index 9b0c4567cd..fbfe63799a 100644 --- a/src/core/contexts/taskStoreContext.tsx +++ b/src/core/contexts/TaskOverrides.tsx @@ -4,7 +4,7 @@ import type { PropsWithChildren } from 'react'; interface TaskOverridesContext { taskId?: string; dataModelType?: string; - dataElementId?: string; + dataModelElementId?: string; layoutSetId?: string; depth: number; } @@ -20,7 +20,7 @@ export function TaskOverrides({ children, ...overrides }: Props) { value={{ taskId: overrides.taskId ?? parentContext.taskId, dataModelType: overrides.dataModelType ?? parentContext.dataModelType, - dataElementId: overrides.dataElementId ?? parentContext.dataElementId, + dataModelElementId: overrides.dataModelElementId ?? parentContext.dataModelElementId, layoutSetId: overrides.layoutSetId ?? parentContext.layoutSetId, depth: parentContext.depth + 1, }} diff --git a/src/features/attachments/StoreAttachmentsInNode.tsx b/src/features/attachments/StoreAttachmentsInNode.tsx index 43638d2e2f..2562a17021 100644 --- a/src/features/attachments/StoreAttachmentsInNode.tsx +++ b/src/features/attachments/StoreAttachmentsInNode.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; import deepEqual from 'fast-deep-equal'; -import { useTaskOverrides } from 'src/core/contexts/taskStoreContext'; +import { useTaskOverrides } from 'src/core/contexts/TaskOverrides'; import { useApplicationMetadata } from 'src/features/applicationMetadata/ApplicationMetadataProvider'; import { isAttachmentUploaded } from 'src/features/attachments/index'; import { DEFAULT_DEBOUNCE_TIMEOUT } from 'src/features/formData/types'; diff --git a/src/features/datamodel/DataModelsProvider.tsx b/src/features/datamodel/DataModelsProvider.tsx index 2be870c514..01aab4d887 100644 --- a/src/features/datamodel/DataModelsProvider.tsx +++ b/src/features/datamodel/DataModelsProvider.tsx @@ -6,7 +6,7 @@ import deepEqual from 'fast-deep-equal'; import { createStore } from 'zustand'; import type { JSONSchema7 } from 'json-schema'; -import { useTaskOverrides } from 'src/core/contexts/taskStoreContext'; +import { useTaskOverrides } from 'src/core/contexts/TaskOverrides'; import { createZustandContext } from 'src/core/contexts/zustandContext'; import { DisplayError } from 'src/core/errorHandling/DisplayError'; import { Loader } from 'src/core/loading/Loader'; @@ -171,7 +171,7 @@ function DataModelsLoader() { // Subform const overrides = useTaskOverrides(); - const overriddenDataElementId = overrides?.dataElementId; + const overriddenDataElementId = overrides?.dataModelElementId; const overriddenDataType = overrides?.dataModelType; // Find all data types referenced in dataModelBindings in the layout diff --git a/src/features/datamodel/useBindingSchema.tsx b/src/features/datamodel/useBindingSchema.tsx index 202a80a248..0fb64249e2 100644 --- a/src/features/datamodel/useBindingSchema.tsx +++ b/src/features/datamodel/useBindingSchema.tsx @@ -2,7 +2,7 @@ import { useCallback, useMemo } from 'react'; import type { JSONSchema7 } from 'json-schema'; -import { useTaskOverrides } from 'src/core/contexts/taskStoreContext'; +import { useTaskOverrides } from 'src/core/contexts/TaskOverrides'; import { useApplicationMetadata } from 'src/features/applicationMetadata/ApplicationMetadataProvider'; import { getCurrentDataTypeForApplication, @@ -34,7 +34,7 @@ export function useCurrentDataModelDataElementId() { const layoutSets = useLayoutSets(); const taskId = useProcessTaskId(); - const overriddenDataElementId = useTaskOverrides()?.dataElementId; + const overriddenDataElementId = useTaskOverrides()?.dataModelElementId; // Instance data elements will update often (after each save), so we have to use a selector to make // sure components don't re-render too often. diff --git a/src/features/form/layout/LayoutsContext.tsx b/src/features/form/layout/LayoutsContext.tsx index 0a21a2fa08..ef5f74f8aa 100644 --- a/src/features/form/layout/LayoutsContext.tsx +++ b/src/features/form/layout/LayoutsContext.tsx @@ -6,7 +6,7 @@ import { useAppQueries } from 'src/core/contexts/AppQueriesProvider'; import { ContextNotProvided } from 'src/core/contexts/context'; import { delayedContext } from 'src/core/contexts/delayedContext'; import { createQueryContext } from 'src/core/contexts/queryContext'; -import { useTaskOverrides } from 'src/core/contexts/taskStoreContext'; +import { useTaskOverrides } from 'src/core/contexts/TaskOverrides'; import { useCurrentDataModelName } from 'src/features/datamodel/useBindingSchema'; import { cleanLayout } from 'src/features/form/layout/cleanLayout'; import { makeLayoutLookups } from 'src/features/form/layout/makeLayoutLookups'; diff --git a/src/features/form/layoutSets/useCurrentLayoutSet.ts b/src/features/form/layoutSets/useCurrentLayoutSet.ts index c7dd48f531..509d29717d 100644 --- a/src/features/form/layoutSets/useCurrentLayoutSet.ts +++ b/src/features/form/layoutSets/useCurrentLayoutSet.ts @@ -1,5 +1,5 @@ import { ContextNotProvided } from 'src/core/contexts/context'; -import { useTaskOverrides } from 'src/core/contexts/taskStoreContext'; +import { useTaskOverrides } from 'src/core/contexts/TaskOverrides'; import { useLaxApplicationMetadata } from 'src/features/applicationMetadata/ApplicationMetadataProvider'; import { getCurrentLayoutSet } from 'src/features/applicationMetadata/appMetadataUtils'; import { useLaxLayoutSets } from 'src/features/form/layoutSets/LayoutSetsProvider'; diff --git a/src/features/instance/useProcessTaskId.ts b/src/features/instance/useProcessTaskId.ts index 81b0ac3e71..7da47a19c3 100644 --- a/src/features/instance/useProcessTaskId.ts +++ b/src/features/instance/useProcessTaskId.ts @@ -1,4 +1,4 @@ -import { useTaskOverrides } from 'src/core/contexts/taskStoreContext'; +import { useTaskOverrides } from 'src/core/contexts/TaskOverrides'; import { useProcessQuery } from 'src/features/instance/useProcessQuery'; import { useNavigationParam } from 'src/hooks/navigation'; diff --git a/src/layout/FileUpload/FileUploadTable/FileTableRow.tsx b/src/layout/FileUpload/FileUploadTable/FileTableRow.tsx index 49f645e3f0..f9728c86f1 100644 --- a/src/layout/FileUpload/FileUploadTable/FileTableRow.tsx +++ b/src/layout/FileUpload/FileUploadTable/FileTableRow.tsx @@ -3,7 +3,7 @@ import React from 'react'; import classNames from 'classnames'; import { AltinnLoader } from 'src/components/AltinnLoader'; -import { useTaskOverrides } from 'src/core/contexts/taskStoreContext'; +import { useTaskOverrides } from 'src/core/contexts/TaskOverrides'; import { isAttachmentUploaded } from 'src/features/attachments'; import { FileScanResults } from 'src/features/attachments/types'; import { Lang } from 'src/features/language/Lang'; diff --git a/src/layout/Subform/SubformWrapper.tsx b/src/layout/Subform/SubformWrapper.tsx index 7c5a174cf6..d52b185ede 100644 --- a/src/layout/Subform/SubformWrapper.tsx +++ b/src/layout/Subform/SubformWrapper.tsx @@ -3,7 +3,7 @@ import type { PropsWithChildren } from 'react'; import { Form } from 'src/components/form/Form'; import { PresentationComponent } from 'src/components/presentation/Presentation'; -import { TaskOverrides } from 'src/core/contexts/taskStoreContext'; +import { TaskOverrides } from 'src/core/contexts/TaskOverrides'; import { Loader } from 'src/core/loading/Loader'; import { FormProvider } from 'src/features/form/FormContext'; import { useDataTypeFromLayoutSet } from 'src/features/form/layout/LayoutsContext'; @@ -62,7 +62,7 @@ export function SubformOverrideWrapper({ return ( {children} diff --git a/src/layout/Subform/Summary/SubformSummaryComponent2.tsx b/src/layout/Subform/Summary/SubformSummaryComponent2.tsx index 3408e35a9d..bd275934fc 100644 --- a/src/layout/Subform/Summary/SubformSummaryComponent2.tsx +++ b/src/layout/Subform/Summary/SubformSummaryComponent2.tsx @@ -4,7 +4,7 @@ import { Heading, Paragraph } from '@digdir/designsystemet-react'; import { Flex } from 'src/app-components/Flex/Flex'; import { Label, LabelInner } from 'src/components/label/Label'; -import { TaskOverrides } from 'src/core/contexts/taskStoreContext'; +import { TaskOverrides } from 'src/core/contexts/TaskOverrides'; import { useApplicationMetadata } from 'src/features/applicationMetadata/ApplicationMetadataProvider'; import { FormProvider } from 'src/features/form/FormContext'; import { useDataTypeFromLayoutSet, useLayoutLookups } from 'src/features/form/layout/LayoutsContext'; @@ -103,7 +103,7 @@ const DoSummaryWrapper = ({ return (
diff --git a/src/layout/Summary2/CommonSummaryComponents/EditButton.tsx b/src/layout/Summary2/CommonSummaryComponents/EditButton.tsx index a64eaccc9a..7dc6297f50 100644 --- a/src/layout/Summary2/CommonSummaryComponents/EditButton.tsx +++ b/src/layout/Summary2/CommonSummaryComponents/EditButton.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { PencilIcon } from '@navikt/aksel-icons'; import { Button } from 'src/app-components/Button/Button'; -import { useTaskOverrides } from 'src/core/contexts/taskStoreContext'; +import { useTaskOverrides } from 'src/core/contexts/TaskOverrides'; import { useSetReturnToView, useSetSummaryNodeOfOrigin } from 'src/features/form/layout/PageNavigationContext'; import { Lang } from 'src/features/language/Lang'; import { useLanguage } from 'src/features/language/useLanguage'; @@ -68,7 +68,7 @@ export function EditButton({ const overrides = useTaskOverrides(); const overriddenTaskId = overrides?.taskId; - const overriddenDataElementId = overrides?.dataElementId; + const overriddenDataElementId = overrides?.dataModelElementId; const indexedId = useIndexedId(targetBaseComponentId, skipLastIdMutator); const summary2Id = useSummaryProp('id'); diff --git a/src/layout/Summary2/SummaryComponent2/TaskSummaryWrapper.tsx b/src/layout/Summary2/SummaryComponent2/TaskSummaryWrapper.tsx index 2ec8354c01..1f2b244350 100644 --- a/src/layout/Summary2/SummaryComponent2/TaskSummaryWrapper.tsx +++ b/src/layout/Summary2/SummaryComponent2/TaskSummaryWrapper.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { TaskOverrides } from 'src/core/contexts/taskStoreContext'; +import { TaskOverrides } from 'src/core/contexts/TaskOverrides'; import { FormProvider } from 'src/features/form/FormContext'; import { useLayoutSets } from 'src/features/form/layoutSets/LayoutSetsProvider'; import { useNavigationParam } from 'src/hooks/navigation'; From 2c053423b0088530cc1f5852c7b51651ed2baafd Mon Sep 17 00:00:00 2001 From: Ole Martin Handeland Date: Mon, 22 Sep 2025 16:05:34 +0200 Subject: [PATCH 3/6] Depth is never read(!) --- src/core/contexts/TaskOverrides.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/contexts/TaskOverrides.tsx b/src/core/contexts/TaskOverrides.tsx index fbfe63799a..396e794ca2 100644 --- a/src/core/contexts/TaskOverrides.tsx +++ b/src/core/contexts/TaskOverrides.tsx @@ -6,10 +6,9 @@ interface TaskOverridesContext { dataModelType?: string; dataModelElementId?: string; layoutSetId?: string; - depth: number; } -const Context = createContext({ depth: 1 }); +const Context = createContext({}); type Props = PropsWithChildren & Omit; export function TaskOverrides({ children, ...overrides }: Props) { @@ -22,7 +21,6 @@ export function TaskOverrides({ children, ...overrides }: Props) { dataModelType: overrides.dataModelType ?? parentContext.dataModelType, dataModelElementId: overrides.dataModelElementId ?? parentContext.dataModelElementId, layoutSetId: overrides.layoutSetId ?? parentContext.layoutSetId, - depth: parentContext.depth + 1, }} > {children} From 2bd667861f29a8fd73e43e128d26db42e1133337 Mon Sep 17 00:00:00 2001 From: Ole Martin Handeland Date: Mon, 22 Sep 2025 17:11:22 +0200 Subject: [PATCH 4/6] Suggestions from coderabbit in review --- src/core/contexts/TaskOverrides.tsx | 3 ++- src/features/attachments/StoreAttachmentsInNode.tsx | 3 +-- src/layout/FileUpload/FileUploadTable/FileTableRow.tsx | 6 ++---- src/layout/Summary2/CommonSummaryComponents/EditButton.tsx | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/core/contexts/TaskOverrides.tsx b/src/core/contexts/TaskOverrides.tsx index 396e794ca2..8af2ed08f4 100644 --- a/src/core/contexts/TaskOverrides.tsx +++ b/src/core/contexts/TaskOverrides.tsx @@ -9,8 +9,9 @@ interface TaskOverridesContext { } const Context = createContext({}); +Context.displayName = 'TaskOverridesContext'; -type Props = PropsWithChildren & Omit; +type Props = PropsWithChildren & TaskOverridesContext; export function TaskOverrides({ children, ...overrides }: Props) { const parentContext = useContext(Context); diff --git a/src/features/attachments/StoreAttachmentsInNode.tsx b/src/features/attachments/StoreAttachmentsInNode.tsx index 2562a17021..85abbb4c60 100644 --- a/src/features/attachments/StoreAttachmentsInNode.tsx +++ b/src/features/attachments/StoreAttachmentsInNode.tsx @@ -96,8 +96,7 @@ function useNodeAttachments(): AttachmentRecord { const data = useInstanceDataElements(baseId); const mappedAttachments = useMemoDeepEqual(() => { - const taskId = overriddenTaskId ? overriddenTaskId : currentTask; - + const taskId = overriddenTaskId ?? currentTask; return mapAttachments(indexedId, baseId, data, application, taskId, nodeData); }, [indexedId, baseId, data, application, currentTask, nodeData, overriddenTaskId]); diff --git a/src/layout/FileUpload/FileUploadTable/FileTableRow.tsx b/src/layout/FileUpload/FileUploadTable/FileTableRow.tsx index f9728c86f1..5abf1109d8 100644 --- a/src/layout/FileUpload/FileUploadTable/FileTableRow.tsx +++ b/src/layout/FileUpload/FileUploadTable/FileTableRow.tsx @@ -40,9 +40,7 @@ export function FileTableRow({ const pdfModeActive = usePdfModeActive(); const readableSize = getSizeWithUnit(attachment.data.size, 2); - const overriddenTaskId = useTaskOverrides()?.taskId; - - const hasOverridenTaskId = !!overriddenTaskId; + const hasOverriddenTaskId = Boolean(useTaskOverrides()?.taskId); const uniqueId = isAttachmentUploaded(attachment) ? attachment.data.id : attachment.data.temporaryId; @@ -78,7 +76,7 @@ export function FileTableRow({ className={rowStyle} id={`altinn-file-list-row-${uniqueId}`} tabIndex={0} - style={hasOverridenTaskId ? { padding: '8px 0' } : {}} + style={hasOverriddenTaskId ? { padding: '8px 0' } : {}} > 0)) { + if (pdfModeActive || overriddenTaskId?.length) { return null; } From ad6918e7d3a7baddb8674e932e8c25003905939e Mon Sep 17 00:00:00 2001 From: Ole Martin Handeland Date: Mon, 22 Sep 2025 17:13:33 +0200 Subject: [PATCH 5/6] I found another issue. The useLayoutSetId() hook was overly complicated, and did much of the same stuff that useCurrentLayoutSetId() already did. The main difference was the taskId source, where useLayoutSetId() preferred the one from the URL, so I renamed it to reflect that. --- src/features/form/layout/LayoutsContext.tsx | 30 ++----------------- .../form/layoutSets/useCurrentLayoutSet.ts | 20 ++++++++++--- .../layoutSettings/LayoutSettingsContext.tsx | 4 +-- src/queries/formPrefetcher.ts | 5 ++-- 4 files changed, 23 insertions(+), 36 deletions(-) diff --git a/src/features/form/layout/LayoutsContext.tsx b/src/features/form/layout/LayoutsContext.tsx index ef5f74f8aa..5af44595dd 100644 --- a/src/features/form/layout/LayoutsContext.tsx +++ b/src/features/form/layout/LayoutsContext.tsx @@ -6,16 +6,14 @@ import { useAppQueries } from 'src/core/contexts/AppQueriesProvider'; import { ContextNotProvided } from 'src/core/contexts/context'; import { delayedContext } from 'src/core/contexts/delayedContext'; import { createQueryContext } from 'src/core/contexts/queryContext'; -import { useTaskOverrides } from 'src/core/contexts/TaskOverrides'; import { useCurrentDataModelName } from 'src/features/datamodel/useBindingSchema'; import { cleanLayout } from 'src/features/form/layout/cleanLayout'; import { makeLayoutLookups } from 'src/features/form/layout/makeLayoutLookups'; import { applyLayoutQuirks } from 'src/features/form/layout/quirks'; import { useLayoutSets } from 'src/features/form/layoutSets/LayoutSetsProvider'; -import { useCurrentLayoutSetId } from 'src/features/form/layoutSets/useCurrentLayoutSet'; +import { useLayoutSetIdFromUrl } from 'src/features/form/layoutSets/useCurrentLayoutSet'; import { useInstanceDataQuery } from 'src/features/instance/InstanceContext'; import { useProcessQuery } from 'src/features/instance/useProcessQuery'; -import { useNavigationParam } from 'src/hooks/navigation'; import { makeLikertChildId } from 'src/layout/Likert/Generator/makeLikertChildId'; import type { QueryDefinition } from 'src/core/queries/usePrefetchQuery'; import type { CompExternal, ILayoutCollection, ILayouts } from 'src/layout/layout'; @@ -45,7 +43,7 @@ export function useLayoutQueryDef( function useLayoutQuery() { const { data: process } = useProcessQuery(); - const currentLayoutSetId = useLayoutSetId(); + const currentLayoutSetId = useLayoutSetIdFromUrl(); const defaultDataModel = useCurrentDataModelName() ?? 'unknown'; const hasInstance = !!useInstanceDataQuery().data; @@ -78,30 +76,6 @@ const { Provider, useCtx, useLaxCtx } = delayedContext(() => }), ); -export function useLayoutSetId() { - const layoutSets = useLayoutSets(); - const currentProcessLayoutSetId = useCurrentLayoutSetId(); - const taskId = useNavigationParam('taskId'); - - const overriddenLayoutSetId = useTaskOverrides()?.layoutSetId; - - if (overriddenLayoutSetId) { - return overriddenLayoutSetId; - } - - const layoutSetId = - taskId != null - ? layoutSets.find((set) => { - if (set.tasks?.length) { - return set.tasks.includes(taskId); - } - return false; - })?.id - : undefined; - - return layoutSetId ?? currentProcessLayoutSetId; -} - export function useDataTypeFromLayoutSet(layoutSetName: string | undefined) { const layoutSets = useLayoutSets(); return layoutSets.find((set) => set.id === layoutSetName)?.dataType; diff --git a/src/features/form/layoutSets/useCurrentLayoutSet.ts b/src/features/form/layoutSets/useCurrentLayoutSet.ts index 509d29717d..7b26a21302 100644 --- a/src/features/form/layoutSets/useCurrentLayoutSet.ts +++ b/src/features/form/layoutSets/useCurrentLayoutSet.ts @@ -4,15 +4,27 @@ import { useLaxApplicationMetadata } from 'src/features/applicationMetadata/Appl import { getCurrentLayoutSet } from 'src/features/applicationMetadata/appMetadataUtils'; import { useLaxLayoutSets } from 'src/features/form/layoutSets/LayoutSetsProvider'; import { useProcessTaskId } from 'src/features/instance/useProcessTaskId'; +import { useNavigationParam } from 'src/hooks/navigation'; -export function useCurrentLayoutSetId() { - return useCurrentLayoutSet()?.id; +/** + * This is a variant that prefers the taskId from the URL. The alternative useCurrentLayoutSetId() and + * useCurrentLayoutSet() will prefer the taskId from the current process state (i.e., where the process is right now, + * not necessarily what the user is looking at right now). + */ +export function useLayoutSetIdFromUrl() { + const taskId = useNavigationParam('taskId'); + return useCurrentLayoutSetId(taskId); } -export function useCurrentLayoutSet() { +export function useCurrentLayoutSetId(taskId?: string) { + return useCurrentLayoutSet(taskId)?.id; +} + +export function useCurrentLayoutSet(_taskId?: string) { const application = useLaxApplicationMetadata(); const layoutSets = useLaxLayoutSets(); - const taskId = useProcessTaskId(); + const processTaskId = useProcessTaskId(); + const taskId = _taskId ?? processTaskId; const overriddenLayoutSetId = useTaskOverrides()?.layoutSetId; if (application === ContextNotProvided || layoutSets === ContextNotProvided) { diff --git a/src/features/form/layoutSettings/LayoutSettingsContext.tsx b/src/features/form/layoutSettings/LayoutSettingsContext.tsx index 01ea3ebc18..3241600abe 100644 --- a/src/features/form/layoutSettings/LayoutSettingsContext.tsx +++ b/src/features/form/layoutSettings/LayoutSettingsContext.tsx @@ -7,8 +7,8 @@ import { useAppQueries } from 'src/core/contexts/AppQueriesProvider'; import { ContextNotProvided } from 'src/core/contexts/context'; import { delayedContext } from 'src/core/contexts/delayedContext'; import { createQueryContext } from 'src/core/contexts/queryContext'; -import { useLayoutSetId } from 'src/features/form/layout/LayoutsContext'; import { useLaxGlobalUISettings } from 'src/features/form/layoutSets/LayoutSetsProvider'; +import { useLayoutSetIdFromUrl } from 'src/features/form/layoutSets/useCurrentLayoutSet'; import { useShallowMemo } from 'src/hooks/useShallowMemo'; import type { QueryDefinition } from 'src/core/queries/usePrefetchQuery'; import type { GlobalPageSettings, ILayoutSettings, NavigationPageGroup } from 'src/layout/common.generated'; @@ -23,7 +23,7 @@ export function useLayoutSettingsQueryDef(layoutSetId?: string): QueryDefinition } function useLayoutSettingsQuery() { - const layoutSetId = useLayoutSetId(); + const layoutSetId = useLayoutSetIdFromUrl(); const query = useQuery(useLayoutSettingsQueryDef(layoutSetId)); useEffect(() => { diff --git a/src/queries/formPrefetcher.ts b/src/queries/formPrefetcher.ts index 49dd67f498..c46dc1055c 100644 --- a/src/queries/formPrefetcher.ts +++ b/src/queries/formPrefetcher.ts @@ -1,7 +1,8 @@ import { usePrefetchQuery } from 'src/core/queries/usePrefetchQuery'; import { useCurrentDataModelDataElementId, useCurrentDataModelName } from 'src/features/datamodel/useBindingSchema'; import { useDynamicsQueryDef } from 'src/features/form/dynamics/DynamicsContext'; -import { useLayoutQueryDef, useLayoutSetId } from 'src/features/form/layout/LayoutsContext'; +import { useLayoutQueryDef } from 'src/features/form/layout/LayoutsContext'; +import { useLayoutSetIdFromUrl } from 'src/features/form/layoutSets/useCurrentLayoutSet'; import { useLayoutSettingsQueryDef } from 'src/features/form/layoutSettings/LayoutSettingsContext'; import { useRulesQueryDef } from 'src/features/form/rules/RulesContext'; import { useLaxInstanceId } from 'src/features/instance/InstanceContext'; @@ -15,7 +16,7 @@ import { useIsPdf } from 'src/hooks/useIsPdf'; * Prefetches requests happening in the FormProvider */ export function FormPrefetcher() { - const layoutSetId = useLayoutSetId(); + const layoutSetId = useLayoutSetIdFromUrl(); const isPDF = useIsPdf(); const dataTypeId = useCurrentDataModelName() ?? 'unknown'; const instanceId = useLaxInstanceId(); From 98d2171fc23a1dc4cea438f13f0a8e361343649a Mon Sep 17 00:00:00 2001 From: Ole Martin Handeland Date: Mon, 22 Sep 2025 17:16:23 +0200 Subject: [PATCH 6/6] Fixing import --- .../devtools/components/LayoutInspector/LayoutInspector.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/features/devtools/components/LayoutInspector/LayoutInspector.tsx b/src/features/devtools/components/LayoutInspector/LayoutInspector.tsx index f2334d1910..a44c6ec894 100644 --- a/src/features/devtools/components/LayoutInspector/LayoutInspector.tsx +++ b/src/features/devtools/components/LayoutInspector/LayoutInspector.tsx @@ -10,7 +10,8 @@ import { LayoutInspectorItem } from 'src/features/devtools/components/LayoutInsp import { SplitView } from 'src/features/devtools/components/SplitView/SplitView'; import { useDevToolsStore } from 'src/features/devtools/data/DevToolsStore'; import { useLayoutValidationForPage } from 'src/features/devtools/layoutValidation/useLayoutValidation'; -import { useLayouts, useLayoutSetId } from 'src/features/form/layout/LayoutsContext'; +import { useLayouts } from 'src/features/form/layout/LayoutsContext'; +import { useLayoutSetIdFromUrl } from 'src/features/form/layoutSets/useCurrentLayoutSet'; import { useCurrentView } from 'src/hooks/useNavigatePage'; import { parseAndCleanText } from 'src/language/sharedLanguage'; import type { LayoutContextValue } from 'src/features/form/layout/LayoutsContext'; @@ -20,7 +21,7 @@ export const LayoutInspector = () => { const setSelectedComponent = useDevToolsStore((state) => state.actions.layoutInspectorSet); const currentView = useCurrentView(); const layouts = useLayouts(); - const currentLayoutSetId = useLayoutSetId(); + const currentLayoutSetId = useLayoutSetIdFromUrl(); const [componentProperties, setComponentProperties] = useState(null); const [propertiesHaveChanged, setPropertiesHaveChanged] = useState(false); const [error, setError] = useState(false);