Skip to content

Commit 6d2bd6e

Browse files
fix: garden group crashing (#524)
* fix: garden group crashing * chore: release
1 parent a505f11 commit 6d2bd6e

File tree

9 files changed

+93
-93
lines changed

9 files changed

+93
-93
lines changed

packages/garden/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@equinor/workspace-garden",
3-
"version": "6.0.0",
3+
"version": "6.0.1",
44
"type": "module",
55
"sideEffects": false,
66
"license": "MIT",

packages/garden/src/lib/components/Garden.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,15 @@ export type GardenMetaRequest = {
3030
dateVariant?: string | null;
3131
};
3232

33-
export type GardenDataSource<TContext> = {
34-
getGardenMeta: (request: GardenMetaRequest, context: TContext, signal?: AbortSignal) => Promise<GardenMeta>;
35-
getBlockAsync: (args: GetBlockRequestArgs, context: TContext, signal?: AbortSignal) => Promise<GardenGroup<any>[]>;
36-
getHeader: (args: GetHeaderBlockRequestArgs, context: TContext, signal?: AbortSignal) => Promise<GardenHeaderGroup[]>;
37-
getSubgroupItems: (args: GetSubgroupItemsArgs, context: TContext, signal?: AbortSignal) => Promise<any[]>;
33+
export type GardenDataSource<TContext = undefined> = {
34+
getGardenMeta: (request: GardenMetaRequest, context?: TContext, signal?: AbortSignal) => Promise<GardenMeta>;
35+
getBlockAsync: (args: GetBlockRequestArgs, context?: TContext, signal?: AbortSignal) => Promise<GardenGroup<any>[]>;
36+
getHeader: (
37+
args: GetHeaderBlockRequestArgs,
38+
context?: TContext,
39+
signal?: AbortSignal
40+
) => Promise<GardenHeaderGroup[]>;
41+
getSubgroupItems: (args: GetSubgroupItemsArgs, context?: TContext, signal?: AbortSignal) => Promise<any[]>;
3842
};
3943

4044
interface GardenProps<TData extends Record<PropertyKey, unknown>, TContext = undefined> {
@@ -96,11 +100,13 @@ export function Garden<TData extends Record<PropertyKey, unknown>, TContext = un
96100

97101
return (
98102
<QueryClientProvider client={client.current}>
99-
<ErrorBoundary FallbackComponent={GardenError}>
103+
<ErrorBoundary FallbackComponent={() => <GardenError />}>
100104
<Suspense fallback={<SplashScreen />}>
101105
<GardenContextProvider
102106
getIdentifier={getIdentifier}
103107
timeInterval={timeInterval}
108+
context={context}
109+
dataSource={dataSource}
104110
dateVariant={dateVariant}
105111
initialGrouping={groupingKeys}
106112
selected={selected}
@@ -121,11 +127,9 @@ export function Garden<TData extends Record<PropertyKey, unknown>, TContext = un
121127
// Hides ViewSettings sidebar when sidesheet is open
122128
selected ? null : (
123129
<ViewSettings
124-
dataSource={dataSource}
125130
dateVariant={dateVariant}
126131
groupingKeys={groupingKeys}
127132
timeInterval={timeInterval}
128-
context={context}
129133
onChangeDateVariant={onChangeDateVariant}
130134
onChangeTimeInterval={onChangetimeInterval}
131135
setGroupingKeys={setGroupingKeys}

packages/garden/src/lib/components/GardenItemContainer/GardenItemContainer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const GardenItemContainer = <TData extends Record<PropertyKey, unknown>,
4747
props: PackageContainerProps<TData, TContext>
4848
): JSX.Element => {
4949
const {
50-
selectionService: { selection, selectNode },
50+
selectionService: { selection },
5151
} = useGarden();
5252

5353
const {
@@ -193,7 +193,7 @@ export const GardenItemContainer = <TData extends Record<PropertyKey, unknown>,
193193

194194
const flatIndex = calculatedIndex;
195195

196-
if (flatIndex.isSubgroupItem) {
196+
if (flatIndex.isSubgroupItem && groupingKeys.length > 1) {
197197
const query = queries[calculateActualIndex(expandedIndexes, flatIndex.parent.index).actualIndex];
198198
return (
199199
<SubGroupItem

packages/garden/src/lib/components/GroupingSelector/GroupingSelector.tsx

Lines changed: 32 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { Autocomplete, Divider, EdsProvider, Label, Radio } from '@equinor/eds-core-react';
2-
import { useQuery } from '@tanstack/react-query';
3-
import { Fragment, useRef } from 'react';
1+
import { Autocomplete, CircularProgress, Divider, EdsProvider, Label, Radio } from '@equinor/eds-core-react';
2+
import { Fragment, startTransition, useRef } from 'react';
43
import { GroupingOption } from '../../types';
5-
import { GardenDataSource } from '../Garden';
64
import {
75
RadioButtonWrapper,
86
RadioCategoryWrapper,
@@ -12,10 +10,9 @@ import {
1210
StyledGroupHeader,
1311
StyledSubGroupHeader,
1412
} from './groupingSelector.styles';
13+
import { useGarden } from '../../hooks/useGarden';
1514

1615
type GroupingSelectorProps<TContext> = {
17-
dataSource: GardenDataSource<TContext>;
18-
context: TContext;
1916
setGroupingKeys: (keys: string[]) => void;
2017
groupingKeys: string[];
2118
timeInterval: string | null;
@@ -25,63 +22,58 @@ type GroupingSelectorProps<TContext> = {
2522
};
2623

2724
export function GroupingSelector<TContext>({
28-
dataSource,
29-
context,
3025
timeInterval,
3126
dateVariant,
3227
setGroupingKeys,
3328
onChangeTimeInterval,
3429
onChangeDateVarient,
3530
groupingKeys,
3631
}: GroupingSelectorProps<TContext>): JSX.Element | null {
37-
const { data } = useQuery(['garden', ...groupingKeys, timeInterval, dateVariant, context], {
38-
refetchOnWindowFocus: false,
39-
suspense: true,
40-
useErrorBoundary: true,
41-
keepPreviousData: false,
42-
cacheTime: Infinity,
43-
staleTime: Infinity,
44-
queryFn: ({ signal }) =>
45-
dataSource.getGardenMeta({ groupingKeys, timeInterval, dateVariant }, context, signal ?? new AbortSignal()),
46-
});
47-
4832
const selectorRef = useRef(null);
4933

50-
const setGardenKey = (key: string) => {
51-
const foundGroupingOption = data?.allGroupingOptions.find((option) => option.groupingKey === key);
52-
if (!foundGroupingOption) {
53-
throw new Error('Invalid grouping option');
54-
}
55-
56-
if (!foundGroupingOption?.timeInterval?.includes(timeInterval ?? '')) {
57-
onChangeTimeInterval(foundGroupingOption.timeInterval?.at(0) ?? null);
58-
}
59-
60-
if (!foundGroupingOption?.dateVariant?.includes(dateVariant ?? '')) {
61-
onChangeDateVarient(foundGroupingOption.dateVariant?.at(0) ?? null);
62-
}
63-
64-
setGroupingKeys([key]);
65-
};
34+
const { gardenMetaQuery } = useGarden();
6635

6736
const handleExistingSelectionChange = (newValue: string | null | undefined) => {
6837
const gardenKey = groupingKeys.at(0);
6938
if (!gardenKey) {
7039
throw new Error('');
7140
}
7241
const newKeys = newValue == null ? [gardenKey] : [gardenKey, newValue];
73-
setGroupingKeys(newKeys);
42+
startTransition(() => {
43+
setGroupingKeys(newKeys);
44+
});
7445
};
7546

7647
const handleGardenKeyChange = (newValue: string | null | undefined) => {
7748
const keyFromLabel = newValue;
7849
keyFromLabel && setGardenKey(keyFromLabel);
7950
};
8051

81-
if (!data) {
52+
if (gardenMetaQuery.isLoading) {
53+
return <CircularProgress size={48} />;
54+
}
55+
56+
if (!gardenMetaQuery.data) {
8257
throw new Error('An error occurred while fetching grouping selections');
8358
}
8459

60+
const setGardenKey = (key: string) => {
61+
const foundGroupingOption = gardenMetaQuery.data.allGroupingOptions.find((option) => option.groupingKey === key);
62+
if (!foundGroupingOption) {
63+
throw new Error('Invalid grouping option');
64+
}
65+
66+
if (!foundGroupingOption?.timeInterval?.includes(timeInterval ?? '')) {
67+
onChangeTimeInterval(foundGroupingOption.timeInterval?.at(0) ?? null);
68+
}
69+
70+
if (!foundGroupingOption?.dateVariant?.includes(dateVariant ?? '')) {
71+
onChangeDateVarient(foundGroupingOption.dateVariant?.at(0) ?? null);
72+
}
73+
74+
setGroupingKeys([key]);
75+
};
76+
8577
return (
8678
<EdsProvider density="compact">
8779
<SelectorBody>
@@ -91,22 +83,22 @@ export function GroupingSelector<TContext>({
9183
<Autocomplete
9284
ref={selectorRef}
9385
key={groupingKeys[0]}
94-
options={data.allGroupingOptions.map((option: GroupingOption) => option.groupingKey)}
86+
options={gardenMetaQuery.data.allGroupingOptions.map((option: GroupingOption) => option.groupingKey)}
9587
label={'Group by'}
9688
hideClearButton
9789
multiple={false}
9890
selectedOptions={[groupingKeys[0]]}
9991
onOptionsChange={(changes) => handleGardenKeyChange(changes.selectedItems[0])}
10092
/>
10193
<Autocomplete
102-
options={data.validGroupingOptions}
94+
options={gardenMetaQuery.data.validGroupingOptions}
10395
label={'Then Group by'}
10496
selectedOptions={[groupingKeys.at(1)]}
10597
onOptionsChange={(changes) => handleExistingSelectionChange(changes.selectedItems[0])}
10698
/>
10799
</StyledAutoCompleteWrapper>
108100

109-
{data.allGroupingOptions.map((groupingOption) => {
101+
{gardenMetaQuery.data.allGroupingOptions.map((groupingOption) => {
110102
// Check if dateVariant or timeInterval is defined
111103
const hasDateVariant = !!groupingOption.dateVariant;
112104
const hasTimeInterval = !!groupingOption.timeInterval;

packages/garden/src/lib/components/ViewSettings/ViewSettings.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import { Button, Icon } from '@equinor/eds-core-react';
22
import { expand, collapse } from '@equinor/eds-icons';
33
import { useState } from 'react';
4-
import { GardenDataSource } from '../Garden';
54
import { GroupingSelector } from '../GroupingSelector/GroupingSelector';
65
import { StyledViewSettings } from './viewSettings.styles';
76

87
const LOCAL_STORAGE_KEY = 'WorkspaceSidebarToggleState';
98

109
interface ViewSettingsProps<TData extends Record<PropertyKey, unknown>, TContext = undefined> {
11-
dataSource: GardenDataSource<TContext>;
12-
context?: TContext;
1310
groupingKeys: string[];
1411
timeInterval: string | null;
1512
dateVariant: string | null;
@@ -21,8 +18,6 @@ interface ViewSettingsProps<TData extends Record<PropertyKey, unknown>, TContext
2118
Icon.add({ expand, collapse });
2219

2320
export function ViewSettings<TData extends Record<PropertyKey, unknown>, TContext = undefined>({
24-
dataSource,
25-
context,
2621
groupingKeys,
2722
timeInterval,
2823
dateVariant,
@@ -57,8 +52,6 @@ export function ViewSettings<TData extends Record<PropertyKey, unknown>, TContex
5752
onChangeTimeInterval={onChangeTimeInterval}
5853
dateVariant={dateVariant}
5954
onChangeDateVarient={onChangeDateVariant}
60-
context={context as any}
61-
dataSource={dataSource}
6255
/>
6356
</>
6457
) : (

packages/garden/src/lib/components/VirtualContainer/VirtualContainer.tsx

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { useQuery } from '@tanstack/react-query';
21
import { useItemWidths } from '../../hooks';
32
import { useGarden } from '../../hooks/useGarden';
43
import { useGardenConfig } from '../../hooks/useGardenConfig';
@@ -9,6 +8,7 @@ import { StyledVirtualContainer } from './virtualContainer.styles';
98
import { info_circle } from '@equinor/eds-icons';
109
import { Icon } from '@equinor/eds-core-react';
1110
import styled from 'styled-components';
11+
import { SplashScreen } from '../splashScreen/SplashScreen';
1212
Icon.add({ info_circle });
1313

1414
type VirtualContainerProps<TContext = undefined> = {
@@ -21,33 +21,17 @@ export const VirtualContainer = <TContext,>({
2121
context,
2222
}: VirtualContainerProps<TContext>): JSX.Element | null => {
2323
const { onClickItem } = useGardenConfig();
24-
const {
25-
groupingService: { groupingKeys, timeInterval, dateVariant },
26-
} = useGarden();
24+
const { gardenMetaQuery } = useGarden();
2725

28-
const { data, isFetching } = useQuery(['garden', ...groupingKeys, timeInterval, dateVariant, context], {
29-
refetchOnWindowFocus: false,
30-
suspense: true,
31-
useErrorBoundary: true,
32-
keepPreviousData: false,
33-
cacheTime: Infinity,
34-
staleTime: Infinity,
35-
queryFn: ({ signal }) =>
36-
dataSource.getGardenMeta(
37-
{
38-
timeInterval,
39-
dateVariant,
40-
groupingKeys,
41-
},
42-
context,
43-
signal ?? new AbortSignal()
44-
),
45-
});
26+
if (gardenMetaQuery.isLoading) {
27+
return <SplashScreen />;
28+
}
4629

47-
if (!data) {
30+
if (!gardenMetaQuery.data) {
4831
// Will never happen when suspense is true
4932
throw new Error();
5033
}
34+
const { data } = gardenMetaQuery;
5135

5236
const amountOfColumns = data.columnCount;
5337
const columnWidth = data.columnWidth || 300;
@@ -73,14 +57,8 @@ export const VirtualContainer = <TContext,>({
7357
return null;
7458
}
7559

76-
//TODO: temp fix, should show skeletons
77-
if (isFetching) {
78-
return null;
79-
}
80-
8160
return (
8261
<>
83-
{/* <ReactQueryDevtools /> */}
8462
<StyledVirtualContainer id={'garden_root'}>
8563
<ExpandProvider initialWidths={widths} defaultColumnWidth={columnWidth}>
8664
<VirtualGarden

packages/garden/src/lib/components/defaultComponents/item/SubGroupItem.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ export const SubGroupItem = ({
3636
itemIndex,
3737
parentRef,
3838
}: SubGroupItemProps) => {
39-
const { isLoading, error, data, refetch } = query;
40-
4139
const {
4240
selectionService: { selection },
4341
} = useGarden();
@@ -47,6 +45,8 @@ export const SubGroupItem = ({
4745
getDisplayName,
4846
} = useGardenConfig();
4947

48+
const { isLoading, error, data, refetch } = query;
49+
5050
if (isLoading) {
5151
/** Skeleton loading state */
5252
return (

packages/garden/src/lib/context/gardenContext.tsx

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { PropsWithChildren, createContext, useCallback, useEffect, useState } from 'react';
2-
import { GetIdentifier } from '../types';
2+
import { GardenMeta, GetIdentifier } from '../types';
3+
import { UseQueryResult, useQuery } from '@tanstack/react-query';
4+
import { GardenDataSource } from '../components';
35

46
type GardenState = {
57
selectionService: SelectionService;
68
groupingService: GroupingService;
9+
gardenMetaQuery: UseQueryResult<GardenMeta, unknown>;
710
};
811

912
type GroupingService = {
@@ -20,20 +23,42 @@ type SelectionService = {
2023

2124
export const GardenContext = createContext<GardenState | null>(null);
2225

23-
export const GardenContextProvider = <T,>(
26+
export const GardenContextProvider = <T, TContext = undefined>(
2427
props: PropsWithChildren<{
2528
getIdentifier: GetIdentifier<T>;
2629
selected: string | null;
2730
initialGrouping: string[];
2831
timeInterval: string | null;
2932
dateVariant: string | null;
33+
dataSource: GardenDataSource<TContext>;
34+
context: TContext | undefined;
3035
}>
3136
) => {
37+
const gardenMetaQuery = useQuery(
38+
['garden', ...props.initialGrouping, props.timeInterval, props.dateVariant, props.context],
39+
{
40+
refetchOnWindowFocus: false,
41+
suspense: true,
42+
useErrorBoundary: true,
43+
keepPreviousData: false,
44+
cacheTime: Infinity,
45+
staleTime: Infinity,
46+
queryFn: ({ signal }) =>
47+
props.dataSource.getGardenMeta(
48+
{ groupingKeys: props.initialGrouping, timeInterval: props.timeInterval, dateVariant: props.dateVariant },
49+
props.context,
50+
signal ?? new AbortSignal()
51+
),
52+
}
53+
);
54+
3255
const selectionService = useSelectionService(props.getIdentifier, props.selected);
3356
const groupingService = useGroupingService(props.initialGrouping, props.timeInterval, props.dateVariant);
3457

3558
return (
36-
<GardenContext.Provider value={{ groupingService, selectionService }}>{props.children}</GardenContext.Provider>
59+
<GardenContext.Provider value={{ groupingService, selectionService, gardenMetaQuery }}>
60+
{props.children}
61+
</GardenContext.Provider>
3762
);
3863
};
3964

0 commit comments

Comments
 (0)