From e086e440182928cbb128e7b18738d5ab71151ffa Mon Sep 17 00:00:00 2001
From: Gustav Eikaas <guei@equinor.com>
Date: Mon, 20 Nov 2023 09:15:55 +0100
Subject: [PATCH 1/2] fix: garden group crashing

---
 packages/garden/src/lib/components/Garden.tsx | 20 +++---
 .../GardenItemContainer.tsx                   |  4 +-
 .../GroupingSelector/GroupingSelector.tsx     | 72 +++++++++----------
 .../components/ViewSettings/ViewSettings.tsx  |  7 --
 .../VirtualContainer/VirtualContainer.tsx     | 36 ++--------
 .../defaultComponents/item/SubGroupItem.tsx   |  4 +-
 .../garden/src/lib/context/gardenContext.tsx  | 31 +++++++-
 .../src/lib/hooks/useExpandSubgroups.ts       | 10 ++-
 packages/workspace-fusion/package.json        |  2 +-
 9 files changed, 93 insertions(+), 93 deletions(-)

diff --git a/packages/garden/src/lib/components/Garden.tsx b/packages/garden/src/lib/components/Garden.tsx
index 2df389ad9..3c78b8eb0 100644
--- a/packages/garden/src/lib/components/Garden.tsx
+++ b/packages/garden/src/lib/components/Garden.tsx
@@ -30,11 +30,15 @@ export type GardenMetaRequest = {
   dateVariant?: string | null;
 };
 
-export type GardenDataSource<TContext> = {
-  getGardenMeta: (request: GardenMetaRequest, context: TContext, signal?: AbortSignal) => Promise<GardenMeta>;
-  getBlockAsync: (args: GetBlockRequestArgs, context: TContext, signal?: AbortSignal) => Promise<GardenGroup<any>[]>;
-  getHeader: (args: GetHeaderBlockRequestArgs, context: TContext, signal?: AbortSignal) => Promise<GardenHeaderGroup[]>;
-  getSubgroupItems: (args: GetSubgroupItemsArgs, context: TContext, signal?: AbortSignal) => Promise<any[]>;
+export type GardenDataSource<TContext = undefined> = {
+  getGardenMeta: (request: GardenMetaRequest, context?: TContext, signal?: AbortSignal) => Promise<GardenMeta>;
+  getBlockAsync: (args: GetBlockRequestArgs, context?: TContext, signal?: AbortSignal) => Promise<GardenGroup<any>[]>;
+  getHeader: (
+    args: GetHeaderBlockRequestArgs,
+    context?: TContext,
+    signal?: AbortSignal
+  ) => Promise<GardenHeaderGroup[]>;
+  getSubgroupItems: (args: GetSubgroupItemsArgs, context?: TContext, signal?: AbortSignal) => Promise<any[]>;
 };
 
 interface GardenProps<TData extends Record<PropertyKey, unknown>, TContext = undefined> {
@@ -96,11 +100,13 @@ export function Garden<TData extends Record<PropertyKey, unknown>, TContext = un
 
   return (
     <QueryClientProvider client={client.current}>
-      <ErrorBoundary FallbackComponent={GardenError}>
+      <ErrorBoundary FallbackComponent={() => <GardenError />}>
         <Suspense fallback={<SplashScreen />}>
           <GardenContextProvider
             getIdentifier={getIdentifier}
             timeInterval={timeInterval}
+            context={context}
+            dataSource={dataSource}
             dateVariant={dateVariant}
             initialGrouping={groupingKeys}
             selected={selected}
@@ -121,11 +127,9 @@ export function Garden<TData extends Record<PropertyKey, unknown>, TContext = un
               // Hides ViewSettings sidebar when sidesheet is open
               selected ? null : (
                 <ViewSettings
-                  dataSource={dataSource}
                   dateVariant={dateVariant}
                   groupingKeys={groupingKeys}
                   timeInterval={timeInterval}
-                  context={context}
                   onChangeDateVariant={onChangeDateVariant}
                   onChangeTimeInterval={onChangetimeInterval}
                   setGroupingKeys={setGroupingKeys}
diff --git a/packages/garden/src/lib/components/GardenItemContainer/GardenItemContainer.tsx b/packages/garden/src/lib/components/GardenItemContainer/GardenItemContainer.tsx
index 13f246ead..ef2da6925 100644
--- a/packages/garden/src/lib/components/GardenItemContainer/GardenItemContainer.tsx
+++ b/packages/garden/src/lib/components/GardenItemContainer/GardenItemContainer.tsx
@@ -47,7 +47,7 @@ export const GardenItemContainer = <TData extends Record<PropertyKey, unknown>,
   props: PackageContainerProps<TData, TContext>
 ): JSX.Element => {
   const {
-    selectionService: { selection, selectNode },
+    selectionService: { selection },
   } = useGarden();
 
   const {
@@ -193,7 +193,7 @@ export const GardenItemContainer = <TData extends Record<PropertyKey, unknown>,
 
         const flatIndex = calculatedIndex;
 
-        if (flatIndex.isSubgroupItem) {
+        if (flatIndex.isSubgroupItem && groupingKeys.length > 1) {
           const query = queries[calculateActualIndex(expandedIndexes, flatIndex.parent.index).actualIndex];
           return (
             <SubGroupItem
diff --git a/packages/garden/src/lib/components/GroupingSelector/GroupingSelector.tsx b/packages/garden/src/lib/components/GroupingSelector/GroupingSelector.tsx
index 5c4d322b5..b9c78a6e9 100644
--- a/packages/garden/src/lib/components/GroupingSelector/GroupingSelector.tsx
+++ b/packages/garden/src/lib/components/GroupingSelector/GroupingSelector.tsx
@@ -1,8 +1,6 @@
-import { Autocomplete, Divider, EdsProvider, Label, Radio } from '@equinor/eds-core-react';
-import { useQuery } from '@tanstack/react-query';
-import { Fragment, useRef } from 'react';
+import { Autocomplete, CircularProgress, Divider, EdsProvider, Label, Radio } from '@equinor/eds-core-react';
+import { Fragment, startTransition, useRef } from 'react';
 import { GroupingOption } from '../../types';
-import { GardenDataSource } from '../Garden';
 import {
   RadioButtonWrapper,
   RadioCategoryWrapper,
@@ -12,10 +10,9 @@ import {
   StyledGroupHeader,
   StyledSubGroupHeader,
 } from './groupingSelector.styles';
+import { useGarden } from '../../hooks/useGarden';
 
 type GroupingSelectorProps<TContext> = {
-  dataSource: GardenDataSource<TContext>;
-  context: TContext;
   setGroupingKeys: (keys: string[]) => void;
   groupingKeys: string[];
   timeInterval: string | null;
@@ -25,8 +22,6 @@ type GroupingSelectorProps<TContext> = {
 };
 
 export function GroupingSelector<TContext>({
-  dataSource,
-  context,
   timeInterval,
   dateVariant,
   setGroupingKeys,
@@ -34,35 +29,9 @@ export function GroupingSelector<TContext>({
   onChangeDateVarient,
   groupingKeys,
 }: GroupingSelectorProps<TContext>): JSX.Element | null {
-  const { data } = useQuery(['garden', ...groupingKeys, timeInterval, dateVariant, context], {
-    refetchOnWindowFocus: false,
-    suspense: true,
-    useErrorBoundary: true,
-    keepPreviousData: false,
-    cacheTime: Infinity,
-    staleTime: Infinity,
-    queryFn: ({ signal }) =>
-      dataSource.getGardenMeta({ groupingKeys, timeInterval, dateVariant }, context, signal ?? new AbortSignal()),
-  });
-
   const selectorRef = useRef(null);
 
-  const setGardenKey = (key: string) => {
-    const foundGroupingOption = data?.allGroupingOptions.find((option) => option.groupingKey === key);
-    if (!foundGroupingOption) {
-      throw new Error('Invalid grouping option');
-    }
-
-    if (!foundGroupingOption?.timeInterval?.includes(timeInterval ?? '')) {
-      onChangeTimeInterval(foundGroupingOption.timeInterval?.at(0) ?? null);
-    }
-
-    if (!foundGroupingOption?.dateVariant?.includes(dateVariant ?? '')) {
-      onChangeDateVarient(foundGroupingOption.dateVariant?.at(0) ?? null);
-    }
-
-    setGroupingKeys([key]);
-  };
+  const { gardenMetaQuery } = useGarden();
 
   const handleExistingSelectionChange = (newValue: string | null | undefined) => {
     const gardenKey = groupingKeys.at(0);
@@ -70,7 +39,9 @@ export function GroupingSelector<TContext>({
       throw new Error('');
     }
     const newKeys = newValue == null ? [gardenKey] : [gardenKey, newValue];
-    setGroupingKeys(newKeys);
+    startTransition(() => {
+      setGroupingKeys(newKeys);
+    });
   };
 
   const handleGardenKeyChange = (newValue: string | null | undefined) => {
@@ -78,10 +49,31 @@ export function GroupingSelector<TContext>({
     keyFromLabel && setGardenKey(keyFromLabel);
   };
 
-  if (!data) {
+  if (gardenMetaQuery.isLoading) {
+    return <CircularProgress size={48} />;
+  }
+
+  if (!gardenMetaQuery.data) {
     throw new Error('An error occurred while fetching grouping selections');
   }
 
+  const setGardenKey = (key: string) => {
+    const foundGroupingOption = gardenMetaQuery.data.allGroupingOptions.find((option) => option.groupingKey === key);
+    if (!foundGroupingOption) {
+      throw new Error('Invalid grouping option');
+    }
+
+    if (!foundGroupingOption?.timeInterval?.includes(timeInterval ?? '')) {
+      onChangeTimeInterval(foundGroupingOption.timeInterval?.at(0) ?? null);
+    }
+
+    if (!foundGroupingOption?.dateVariant?.includes(dateVariant ?? '')) {
+      onChangeDateVarient(foundGroupingOption.dateVariant?.at(0) ?? null);
+    }
+
+    setGroupingKeys([key]);
+  };
+
   return (
     <EdsProvider density="compact">
       <SelectorBody>
@@ -91,7 +83,7 @@ export function GroupingSelector<TContext>({
           <Autocomplete
             ref={selectorRef}
             key={groupingKeys[0]}
-            options={data.allGroupingOptions.map((option: GroupingOption) => option.groupingKey)}
+            options={gardenMetaQuery.data.allGroupingOptions.map((option: GroupingOption) => option.groupingKey)}
             label={'Group by'}
             hideClearButton
             multiple={false}
@@ -99,14 +91,14 @@ export function GroupingSelector<TContext>({
             onOptionsChange={(changes) => handleGardenKeyChange(changes.selectedItems[0])}
           />
           <Autocomplete
-            options={data.validGroupingOptions}
+            options={gardenMetaQuery.data.validGroupingOptions}
             label={'Then Group by'}
             selectedOptions={[groupingKeys.at(1)]}
             onOptionsChange={(changes) => handleExistingSelectionChange(changes.selectedItems[0])}
           />
         </StyledAutoCompleteWrapper>
 
-        {data.allGroupingOptions.map((groupingOption) => {
+        {gardenMetaQuery.data.allGroupingOptions.map((groupingOption) => {
           // Check if dateVariant or timeInterval is defined
           const hasDateVariant = !!groupingOption.dateVariant;
           const hasTimeInterval = !!groupingOption.timeInterval;
diff --git a/packages/garden/src/lib/components/ViewSettings/ViewSettings.tsx b/packages/garden/src/lib/components/ViewSettings/ViewSettings.tsx
index a5c70d618..9d8f5eb46 100644
--- a/packages/garden/src/lib/components/ViewSettings/ViewSettings.tsx
+++ b/packages/garden/src/lib/components/ViewSettings/ViewSettings.tsx
@@ -1,15 +1,12 @@
 import { Button, Icon } from '@equinor/eds-core-react';
 import { expand, collapse } from '@equinor/eds-icons';
 import { useState } from 'react';
-import { GardenDataSource } from '../Garden';
 import { GroupingSelector } from '../GroupingSelector/GroupingSelector';
 import { StyledViewSettings } from './viewSettings.styles';
 
 const LOCAL_STORAGE_KEY = 'WorkspaceSidebarToggleState';
 
 interface ViewSettingsProps<TData extends Record<PropertyKey, unknown>, TContext = undefined> {
-  dataSource: GardenDataSource<TContext>;
-  context?: TContext;
   groupingKeys: string[];
   timeInterval: string | null;
   dateVariant: string | null;
@@ -21,8 +18,6 @@ interface ViewSettingsProps<TData extends Record<PropertyKey, unknown>, TContext
 Icon.add({ expand, collapse });
 
 export function ViewSettings<TData extends Record<PropertyKey, unknown>, TContext = undefined>({
-  dataSource,
-  context,
   groupingKeys,
   timeInterval,
   dateVariant,
@@ -57,8 +52,6 @@ export function ViewSettings<TData extends Record<PropertyKey, unknown>, TContex
             onChangeTimeInterval={onChangeTimeInterval}
             dateVariant={dateVariant}
             onChangeDateVarient={onChangeDateVariant}
-            context={context as any}
-            dataSource={dataSource}
           />
         </>
       ) : (
diff --git a/packages/garden/src/lib/components/VirtualContainer/VirtualContainer.tsx b/packages/garden/src/lib/components/VirtualContainer/VirtualContainer.tsx
index a3f97a51e..2df885df2 100644
--- a/packages/garden/src/lib/components/VirtualContainer/VirtualContainer.tsx
+++ b/packages/garden/src/lib/components/VirtualContainer/VirtualContainer.tsx
@@ -1,4 +1,3 @@
-import { useQuery } from '@tanstack/react-query';
 import { useItemWidths } from '../../hooks';
 import { useGarden } from '../../hooks/useGarden';
 import { useGardenConfig } from '../../hooks/useGardenConfig';
@@ -9,6 +8,7 @@ import { StyledVirtualContainer } from './virtualContainer.styles';
 import { info_circle } from '@equinor/eds-icons';
 import { Icon } from '@equinor/eds-core-react';
 import styled from 'styled-components';
+import { SplashScreen } from '../splashScreen/SplashScreen';
 Icon.add({ info_circle });
 
 type VirtualContainerProps<TContext = undefined> = {
@@ -21,33 +21,17 @@ export const VirtualContainer = <TContext,>({
   context,
 }: VirtualContainerProps<TContext>): JSX.Element | null => {
   const { onClickItem } = useGardenConfig();
-  const {
-    groupingService: { groupingKeys, timeInterval, dateVariant },
-  } = useGarden();
+  const { gardenMetaQuery } = useGarden();
 
-  const { data, isFetching } = useQuery(['garden', ...groupingKeys, timeInterval, dateVariant, context], {
-    refetchOnWindowFocus: false,
-    suspense: true,
-    useErrorBoundary: true,
-    keepPreviousData: false,
-    cacheTime: Infinity,
-    staleTime: Infinity,
-    queryFn: ({ signal }) =>
-      dataSource.getGardenMeta(
-        {
-          timeInterval,
-          dateVariant,
-          groupingKeys,
-        },
-        context,
-        signal ?? new AbortSignal()
-      ),
-  });
+  if (gardenMetaQuery.isLoading) {
+    return <SplashScreen />;
+  }
 
-  if (!data) {
+  if (!gardenMetaQuery.data) {
     // Will never happen when suspense is true
     throw new Error();
   }
+  const { data } = gardenMetaQuery;
 
   const amountOfColumns = data.columnCount;
   const columnWidth = data.columnWidth || 300;
@@ -73,14 +57,8 @@ export const VirtualContainer = <TContext,>({
     return null;
   }
 
-  //TODO: temp fix, should show skeletons
-  if (isFetching) {
-    return null;
-  }
-
   return (
     <>
-      {/* <ReactQueryDevtools /> */}
       <StyledVirtualContainer id={'garden_root'}>
         <ExpandProvider initialWidths={widths} defaultColumnWidth={columnWidth}>
           <VirtualGarden
diff --git a/packages/garden/src/lib/components/defaultComponents/item/SubGroupItem.tsx b/packages/garden/src/lib/components/defaultComponents/item/SubGroupItem.tsx
index 1bdae054e..75dbceb1b 100644
--- a/packages/garden/src/lib/components/defaultComponents/item/SubGroupItem.tsx
+++ b/packages/garden/src/lib/components/defaultComponents/item/SubGroupItem.tsx
@@ -36,8 +36,6 @@ export const SubGroupItem = ({
   itemIndex,
   parentRef,
 }: SubGroupItemProps) => {
-  const { isLoading, error, data, refetch } = query;
-
   const {
     selectionService: { selection },
   } = useGarden();
@@ -47,6 +45,8 @@ export const SubGroupItem = ({
     getDisplayName,
   } = useGardenConfig();
 
+  const { isLoading, error, data, refetch } = query;
+
   if (isLoading) {
     /** Skeleton loading state */
     return (
diff --git a/packages/garden/src/lib/context/gardenContext.tsx b/packages/garden/src/lib/context/gardenContext.tsx
index 3441ccf62..d8eca1ff7 100644
--- a/packages/garden/src/lib/context/gardenContext.tsx
+++ b/packages/garden/src/lib/context/gardenContext.tsx
@@ -1,9 +1,12 @@
 import { PropsWithChildren, createContext, useCallback, useEffect, useState } from 'react';
-import { GetIdentifier } from '../types';
+import { GardenMeta, GetIdentifier } from '../types';
+import { UseQueryResult, useQuery } from '@tanstack/react-query';
+import { GardenDataSource } from '../components';
 
 type GardenState = {
   selectionService: SelectionService;
   groupingService: GroupingService;
+  gardenMetaQuery: UseQueryResult<GardenMeta, unknown>;
 };
 
 type GroupingService = {
@@ -20,20 +23,42 @@ type SelectionService = {
 
 export const GardenContext = createContext<GardenState | null>(null);
 
-export const GardenContextProvider = <T,>(
+export const GardenContextProvider = <T, TContext = undefined>(
   props: PropsWithChildren<{
     getIdentifier: GetIdentifier<T>;
     selected: string | null;
     initialGrouping: string[];
     timeInterval: string | null;
     dateVariant: string | null;
+    dataSource: GardenDataSource<TContext>;
+    context: TContext | undefined;
   }>
 ) => {
+  const gardenMetaQuery = useQuery(
+    ['garden', ...props.initialGrouping, props.timeInterval, props.dateVariant, props.context],
+    {
+      refetchOnWindowFocus: false,
+      suspense: true,
+      useErrorBoundary: true,
+      keepPreviousData: false,
+      cacheTime: Infinity,
+      staleTime: Infinity,
+      queryFn: ({ signal }) =>
+        props.dataSource.getGardenMeta(
+          { groupingKeys: props.initialGrouping, timeInterval: props.timeInterval, dateVariant: props.dateVariant },
+          props.context,
+          signal ?? new AbortSignal()
+        ),
+    }
+  );
+
   const selectionService = useSelectionService(props.getIdentifier, props.selected);
   const groupingService = useGroupingService(props.initialGrouping, props.timeInterval, props.dateVariant);
 
   return (
-    <GardenContext.Provider value={{ groupingService, selectionService }}>{props.children}</GardenContext.Provider>
+    <GardenContext.Provider value={{ groupingService, selectionService, gardenMetaQuery }}>
+      {props.children}
+    </GardenContext.Provider>
   );
 };
 
diff --git a/packages/garden/src/lib/hooks/useExpandSubgroups.ts b/packages/garden/src/lib/hooks/useExpandSubgroups.ts
index d93afb1b2..1120af2f6 100644
--- a/packages/garden/src/lib/hooks/useExpandSubgroups.ts
+++ b/packages/garden/src/lib/hooks/useExpandSubgroups.ts
@@ -1,8 +1,16 @@
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
 import { ExpandedWithRange, Expanded } from '../components/VirtualGarden';
+import { useGarden } from './useGarden';
 
 export const useExpandedSubGroups = (columnCount: number) => {
+  const {
+    groupingService: { groupingKeys, dateVariant, timeInterval },
+  } = useGarden();
+
   const [expanded, setExpanded] = useState<ExpandedWithRange[][]>(new Array(columnCount).fill(0).map(() => []));
+  useEffect(() => {
+    setExpanded(new Array(columnCount).fill(0).map(() => []));
+  }, [groupingKeys.toString(), dateVariant, timeInterval, columnCount]);
 
   const expandColumn = (columnIndex: number, item: Expanded) => {
     const expandedIndexes = expanded[columnIndex];
diff --git a/packages/workspace-fusion/package.json b/packages/workspace-fusion/package.json
index b383745e8..ab743bce9 100644
--- a/packages/workspace-fusion/package.json
+++ b/packages/workspace-fusion/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@equinor/workspace-fusion",
-  "version": "6.0.1",
+  "version": "6.0.2",
   "type": "module",
   "sideEffects": false,
   "license": "MIT",

From eca642d2205546c369e5b672831f9bf572fba3ef Mon Sep 17 00:00:00 2001
From: Gustav Eikaas <guei@equinor.com>
Date: Mon, 20 Nov 2023 09:18:38 +0100
Subject: [PATCH 2/2] chore: release

---
 packages/garden/package.json           | 2 +-
 packages/workspace-fusion/package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/garden/package.json b/packages/garden/package.json
index 8a2e54b83..c1ea514e0 100644
--- a/packages/garden/package.json
+++ b/packages/garden/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@equinor/workspace-garden",
-  "version": "6.0.0",
+  "version": "6.0.1",
   "type": "module",
   "sideEffects": false,
   "license": "MIT",
diff --git a/packages/workspace-fusion/package.json b/packages/workspace-fusion/package.json
index ab743bce9..b383745e8 100644
--- a/packages/workspace-fusion/package.json
+++ b/packages/workspace-fusion/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@equinor/workspace-fusion",
-  "version": "6.0.2",
+  "version": "6.0.1",
   "type": "module",
   "sideEffects": false,
   "license": "MIT",