Skip to content

Commit

Permalink
feat: Split GroupTree into GroupTreePlot-component and settings (#1794)
Browse files Browse the repository at this point in the history
Split previous `/package/group-tree` into a view-component,
`/package/group-tree-plot`, which is independent of `redux` and `mui`.
The old `GroupTree`-component placed in `python/src/components`, for
usage in Dash, now has a lot of the code from the old
`/package/group-tree`

Thereby the new `group-tree-plot` can be used directly in Webviz
NextGen.

Removed package: `package/group-tree`
New package: `package/group-tree-plot`

-----
Closes: equinor/webviz#461
  • Loading branch information
jorgenherje authored Dec 12, 2023
1 parent 513005b commit ef07441
Show file tree
Hide file tree
Showing 61 changed files with 958 additions and 3,463 deletions.
39 changes: 16 additions & 23 deletions python/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion python/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"@mui/material": "^5.11",
"@reduxjs/toolkit": "^1.7.2",
"@webviz/core-components": "^0.6.2",
"@webviz/group-tree": "file:../typescript/packages/group-tree",
"@webviz/group-tree-plot": "file:../typescript/packages/group-tree-plot",
"@webviz/subsurface-viewer": "file:../typescript/packages/subsurface-viewer",
"@webviz/well-completions-plot": "file:../typescript/packages/well-completions-plot",
"@webviz/well-log-viewer": "file:../typescript/packages/well-log-viewer",
Expand Down
34 changes: 20 additions & 14 deletions python/src/components/GroupTree/GroupTree.jsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import React from "react";
import PropTypes from "prop-types";

import {
DatedTreePropTypes,
EdgeMetadataPropTypes,
NodeMetadataPropTypes,
} from "@webviz/group-tree-plot";

const GroupTreeComponent = React.lazy(() =>
import(/* webpackChunkName: "webviz-group-tree" */ "@webviz/group-tree")
import(
/* webpackChunkName: "webviz-group-tree" */ "./components/GroupTreeComponent"
)
);

const GroupTree = (props) => {
const {
edge_options: edgeOptions,
node_options: nodeOptions,
...rest
} = props;
export const GroupTree = (props) => {
const { edge_metadata_list, node_metadata_list, ...rest } = props;
return (
<React.Suspense fallback={<div>Loading...</div>}>
<GroupTreeComponent
edgeOptions={edgeOptions}
nodeOptions={nodeOptions}
edgeMetadataList={edge_metadata_list}
nodeMetadataList={node_metadata_list}
{...rest}
/>
</React.Suspense>
Expand All @@ -29,15 +33,17 @@ GroupTree.propTypes = {
* components in an app.
*/
id: PropTypes.string.isRequired,

/**
* Array of JSON objects describing group tree data.
*/
data: PropTypes.arrayOf(PropTypes.object),
data: PropTypes.arrayOf(DatedTreePropTypes),

edge_options: PropTypes.arrayOf(PropTypes.object),
node_options: PropTypes.arrayOf(PropTypes.object),
/**
* Arrays of metadata. Used in drop down selectors and tree visualization.
*/
edge_metadata_list: EdgeMetadataPropTypes,
node_metadata_list: NodeMetadataPropTypes,
};

GroupTree.displayName = "GroupTree";

export default GroupTree;
69 changes: 69 additions & 0 deletions python/src/components/GroupTree/components/DataLoader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/* eslint-disable react-hooks/exhaustive-deps */ // remove when ready to fix these.

import type { PropsWithChildren, ReactNode } from "react";
import React, { useMemo } from "react";
import { Provider as ReduxProvider } from "react-redux";
import { createReduxStore } from "../redux/store";
import type { UISettings } from "../redux/types";
import type {
DatedTree,
EdgeMetadata,
NodeMetadata,
} from "@webviz/group-tree-plot";

export type DateTreesIndices = {
treeIndex: number;
dateIndex: number;
};

interface DataProviderProps {
id: string;
data: DatedTree[];
edgeMetadataList: EdgeMetadata[];
nodeMetadataList: NodeMetadata[];
initialIndices: DateTreesIndices;
children: ReactNode;
}

export const DataContext = React.createContext<DatedTree[]>([]);

const DataProvider: React.FC<DataProviderProps> = (
props: PropsWithChildren<DataProviderProps>
) => {
const preloadedState = useMemo(() => {
// Use "initialIndices" from previous data if it refers to a valid date otherwise use first date.
const treeIdx = props.initialIndices.treeIndex;
const dateIdx = props.initialIndices.dateIndex;
const hasValidIndices =
props.data.length > treeIdx &&
props.data[treeIdx].dates.length > dateIdx;
const initialDateTime = hasValidIndices
? props.data[treeIdx].dates[dateIdx]
: props.data[0].dates[0];

const initialFlowRate = props.edgeMetadataList[0]?.key ?? "";
const initialNodeInfo = props.nodeMetadataList[0]?.key ?? "";

return {
id: props.id,
ui: {
currentDateTime: initialDateTime,
currentFlowRate: initialFlowRate,
currentNodeInfo: initialNodeInfo,
} as UISettings,
};
}, [props.id, props.data]); // Shallow compare does not detect updated data? Will useMemo actually help?

const store = useMemo(
() => createReduxStore(preloadedState),
[preloadedState]
);

return (
<DataContext.Provider value={props.data}>
<ReduxProvider store={store}>{props.children}</ReduxProvider>
</DataContext.Provider>
);
};

export default DataProvider;
72 changes: 72 additions & 0 deletions python/src/components/GroupTree/components/GroupTreeComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React, { useCallback, useState } from "react";

import DataProvider, { DateTreesIndices } from "./DataLoader";
import GroupTreeViewer from "./GroupTreeViewer";
import { DatedTree, EdgeMetadata, NodeMetadata } from "@webviz/group-tree-plot";

//TODO schema check
export interface GroupTreeProps {
/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: string;
/**
* Array of JSON objects describing group tree data.
*/
data: DatedTree[];

/**
* Arrays of metadata. Used in drop down selectors and tree visualization.
*/
edgeMetadataList: EdgeMetadata[];
nodeMetadataList: NodeMetadata[];
}

const GroupTreeComponent: React.FC<GroupTreeProps> = React.memo(
(props: GroupTreeProps) => {
const [indices, setIndices] = useState<DateTreesIndices>({
treeIndex: 0,
dateIndex: 0,
});

const currentDateTimeChangedCallBack = useCallback(
(currentDateTime: string) => {
const newTreeIndex = props.data.findIndex((e) => {
return e.dates.includes(currentDateTime);
});
const newDateIndex =
props.data[newTreeIndex].dates.indexOf(currentDateTime);

setIndices({
treeIndex: newTreeIndex,
dateIndex: newDateIndex,
});
},
[props.data]
);

return (
<DataProvider
id={props.id}
data={props.data}
edgeMetadataList={props.edgeMetadataList}
nodeMetadataList={props.nodeMetadataList}
initialIndices={indices}
>
<GroupTreeViewer
id={props.id}
edgeMetadataList={props.edgeMetadataList}
nodeMetadataList={props.nodeMetadataList}
currentDateTimeChangedCallBack={
currentDateTimeChangedCallBack
}
/>
</DataProvider>
);
}
);

GroupTreeComponent.displayName = "GroupTreeComponent";
export default GroupTreeComponent;
80 changes: 80 additions & 0 deletions python/src/components/GroupTree/components/GroupTreeViewer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/* eslint-disable react-hooks/exhaustive-deps */ // remove when ready to fix these.

import { styled } from "@mui/material/styles";
import React, { useContext, useEffect } from "react";
import { useSelector } from "react-redux";
import type { GroupTreeState } from "../redux/store";
import { DataContext } from "./DataLoader";
import SettingsBar from "./Settings/SettingsBar";

import {
GroupTreePlot,
EdgeMetadata,
NodeMetadata,
} from "@webviz/group-tree-plot";

const PREFIX = "GroupTreeViewer";

const classes = {
root: `${PREFIX}-root`,
};

const Root = styled("div")(() => ({
[`&.${classes.root}`]: {
position: "relative",
display: "flex",
flex: 1,
flexDirection: "column",
height: "90%",
},
}));

interface GroupTreeViewerProps {
id: string;
edgeMetadataList: EdgeMetadata[];
nodeMetadataList: NodeMetadata[];
currentDateTimeChangedCallBack: (currentDateTime: string) => void;
}

const GroupTreeViewer: React.FC<GroupTreeViewerProps> = (
props: GroupTreeViewerProps
) => {
const data = useContext(DataContext);

const currentDateTime = useSelector(
(state: GroupTreeState) => state.ui.currentDateTime
);
const currentFlowRateKey = useSelector(
(state: GroupTreeState) => state.ui.currentFlowRate
);
const currentNodeKey = useSelector(
(state: GroupTreeState) => state.ui.currentNodeInfo
);

useEffect(() => {
if (typeof props.currentDateTimeChangedCallBack !== "undefined") {
props.currentDateTimeChangedCallBack(currentDateTime);
}
}, [currentDateTime]);

return (
<Root className={classes.root}>
<SettingsBar
edgeMetadataList={props.edgeMetadataList}
nodeMetadataList={props.nodeMetadataList}
/>
<GroupTreePlot
id={props.id}
datedTrees={data}
edgeMetadataList={props.edgeMetadataList}
nodeMetadataList={props.nodeMetadataList}
selectedEdgeKey={currentFlowRateKey}
selectedNodeKey={currentNodeKey}
selectedDateTime={currentDateTime}
/>
</Root>
);
};

GroupTreeViewer.displayName = "GroupTreeViewer";
export default GroupTreeViewer;
Loading

0 comments on commit ef07441

Please sign in to comment.