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
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion packages/garden/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@equinor/workspace-garden",
"version": "6.0.0",
"version": "6.0.1",
"type": "module",
"sideEffects": false,
"license": "MIT",
Expand Down
20 changes: 12 additions & 8 deletions packages/garden/src/lib/components/Garden.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -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}
Expand All @@ -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}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
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,
Expand All @@ -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;
Expand All @@ -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>
Expand All @@ -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;
Expand Down
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;
Expand All @@ -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,
Expand Down Expand Up @@ -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}
/>
</>
) : (
Expand Down
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';
Expand All @@ -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> = {
Expand All @@ -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;
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ export const SubGroupItem = ({
itemIndex,
parentRef,
}: SubGroupItemProps) => {
const { isLoading, error, data, refetch } = query;

const {
selectionService: { selection },
} = useGarden();
Expand All @@ -47,6 +45,8 @@ export const SubGroupItem = ({
getDisplayName,
} = useGardenConfig();

const { isLoading, error, data, refetch } = query;

if (isLoading) {
/** Skeleton loading state */
return (
Expand Down
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 = {
Expand All @@ -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>
);
};

Expand Down
Loading
Loading