Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: garden group crashing #524

Merged
merged 2 commits into from
Nov 20, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
fix: garden group crashing
Gustav-Eikaas committed Nov 20, 2023
commit e086e440182928cbb128e7b18738d5ab71151ffa
20 changes: 12 additions & 8 deletions packages/garden/src/lib/components/Garden.tsx
Original file line number Diff line number Diff line change
@@ -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}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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,63 +22,58 @@ type GroupingSelectorProps<TContext> = {
};

export function GroupingSelector<TContext>({
dataSource,
context,
timeInterval,
dateVariant,
setGroupingKeys,
onChangeTimeInterval,
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);
if (!gardenKey) {
throw new Error('');
}
const newKeys = newValue == null ? [gardenKey] : [gardenKey, newValue];
setGroupingKeys(newKeys);
startTransition(() => {
setGroupingKeys(newKeys);
});
};

const handleGardenKeyChange = (newValue: string | null | undefined) => {
const keyFromLabel = newValue;
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,22 +83,22 @@ 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}
selectedOptions={[groupingKeys[0]]}
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;
Original file line number Diff line number Diff line change
@@ -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}
/>
</>
) : (
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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 (
31 changes: 28 additions & 3 deletions packages/garden/src/lib/context/gardenContext.tsx
Original file line number Diff line number Diff line change
@@ -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>
);
};

10 changes: 9 additions & 1 deletion packages/garden/src/lib/hooks/useExpandSubgroups.ts
Original file line number Diff line number Diff line change
@@ -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];
2 changes: 1 addition & 1 deletion packages/workspace-fusion/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@equinor/workspace-fusion",
"version": "6.0.1",
"version": "6.0.2",
"type": "module",
"sideEffects": false,
"license": "MIT",