Skip to content

Commit

Permalink
Fixup based on feedback after testing
Browse files Browse the repository at this point in the history
- Added ApiStatesWrapper for list of react queries
- Handle selected vectors when deleting/removing ensembles
- Handle selected vectors when selecting new or non-cached ensemble
- Fix bug in use effect in settings, was `return` instead of `continue` in for-loop, thus not triggering update of view.
  • Loading branch information
jorgenherje committed Sep 6, 2023
1 parent ca03b4a commit f5a3db2
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 30 deletions.
41 changes: 41 additions & 0 deletions frontend/src/lib/components/ApiStatesWrapper/apiStatesWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from "react";

import { resolveClassNames } from "@lib/utils/resolveClassNames";
import { QueryObserverResult } from "@tanstack/react-query";

export type ApiStatesWrapperProps = {
apiResults: QueryObserverResult[];
loadingComponent: React.ReactNode;
errorComponent: React.ReactNode;
className?: string;
style?: React.CSSProperties;
children: React.ReactNode;
};

export const ApiStatesWrapper: React.FC<ApiStatesWrapperProps> = (props: ApiStatesWrapperProps) => {
return (
<div
className={resolveClassNames(
"relative rounded",
{ "outline outline-blue-100 outline-offset-2": props.apiResults.some((elm) => elm.isLoading) },
{ "outline outline-red-100 outline-offset-2": props.apiResults.some((elm) => elm.isError) },
props.className ?? ""
)}
style={props.style}
>
{props.apiResults.some((elm) => elm.isLoading) && (
<div className="absolute left-0 right-0 w-full h-full bg-white bg-opacity-80 flex items-center justify-center z-10">
{props.loadingComponent}
</div>
)}
{props.apiResults.some((elm) => elm.isError) && (
<div className="absolute left-0 right-0 w-full h-full bg-white bg-opacity-80 flex items-center justify-center z-10">
{props.errorComponent}
</div>
)}
{props.children}
</div>
);
};

ApiStatesWrapper.displayName = "ApiStatesWrapper";
1 change: 1 addition & 0 deletions frontend/src/lib/components/ApiStatesWrapper/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ApiStatesWrapper } from "./apiStatesWrapper";
69 changes: 41 additions & 28 deletions frontend/src/modules/SimulationTimeSeriesMatrix/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import { EnsembleSet } from "@framework/EnsembleSet";
import { ModuleFCProps } from "@framework/Module";
import { useEnsembleSet } from "@framework/WorkbenchSession";
import { MultiEnsembleSelect } from "@framework/components/MultiEnsembleSelect";
import { fixupEnsembleIdents } from "@framework/utils/ensembleUiHelpers";
import { ApiStatesWrapper } from "@lib/components/ApiStatesWrapper";
import { Checkbox } from "@lib/components/Checkbox";
import { CircularProgress } from "@lib/components/CircularProgress";
import { CollapsibleGroup } from "@lib/components/CollapsibleGroup";
import { Dropdown } from "@lib/components/Dropdown";
import { Label } from "@lib/components/Label";
import { RadioGroup } from "@lib/components/RadioGroup";
import { SmartNodeSelectorSelection } from "@lib/components/SmartNodeSelector";
import { SmartNodeSelectorSelection, TreeDataNode } from "@lib/components/SmartNodeSelector";
import { VectorSelector } from "@lib/components/VectorSelector";
import { resolveClassNames } from "@lib/utils/resolveClassNames";
import { createVectorSelectorDataFromVectors } from "@lib/utils/vectorSelectorUtils";
Expand Down Expand Up @@ -46,6 +49,7 @@ export function settings({ moduleContext, workbenchSession }: ModuleFCProps<Stat
const [previousEnsembleSet, setPreviousEnsembleSet] = React.useState<EnsembleSet>(ensembleSet);
const [selectedEnsembleIdents, setSelectedEnsembleIdents] = React.useState<EnsembleIdent[]>([]);
const [selectedVectorNames, setSelectedVectorNames] = React.useState<string[]>([]);
const [vectorSelectorData, setVectorSelectorData] = React.useState<TreeDataNode[]>([]);

const [prevVisualizationMode, setPrevVisualizationMode] = React.useState<VisualizationMode>(visualizationMode);
if (prevVisualizationMode !== visualizationMode) {
Expand All @@ -56,27 +60,24 @@ export function settings({ moduleContext, workbenchSession }: ModuleFCProps<Stat
const ensembleVectorListsHelper = new EnsembleVectorListsHelper(selectedEnsembleIdents, vectorListQueries);
const vectorsUnion: VectorDescription_api[] = ensembleVectorListsHelper.vectorsUnion();

const vectorSelectorData = createVectorSelectorDataFromVectors(vectorsUnion.map((vector) => vector.name));
const selectedVectorNamesHasHistorical = ensembleVectorListsHelper.hasAnyHistoricalVector(selectedVectorNames);
const currentVectorSelectorData = createVectorSelectorDataFromVectors(vectorsUnion.map((vector) => vector.name));

const newSelectedVectorNames = [];
for (const vector of selectedVectorNames) {
if (
vectorsUnion.some((item) => {
return item.name === vector;
})
) {
newSelectedVectorNames.push(vector);
}
}
if (!isEqual(selectedVectorNames, newSelectedVectorNames)) {
setSelectedVectorNames(newSelectedVectorNames);
// Only update if all vector lists are retrieved before updating vectorSelectorData has changed
const hasVectorListQueriesErrorOrLoading = vectorListQueries.some((query) => query.isLoading || query.isError);
if (!hasVectorListQueriesErrorOrLoading && !isEqual(currentVectorSelectorData, vectorSelectorData)) {
setVectorSelectorData(currentVectorSelectorData);
}

const selectedVectorNamesHasHistorical = ensembleVectorListsHelper.hasAnyHistoricalVector(newSelectedVectorNames);

if (!isEqual(ensembleSet, previousEnsembleSet)) {
// TODO:
// Handle change of ensembleSet-> validity of ensemble selection and vector selection
const newSelectedEnsembleIdents = selectedEnsembleIdents.filter(
(ensemble) => ensembleSet.findEnsemble(ensemble) !== null
);
const validatedEnsembleIdents = fixupEnsembleIdents(newSelectedEnsembleIdents, ensembleSet) ?? [];
if (!isEqual(selectedEnsembleIdents, validatedEnsembleIdents)) {
setSelectedEnsembleIdents(validatedEnsembleIdents);
}

setPreviousEnsembleSet(ensembleSet);
}

Expand All @@ -86,7 +87,7 @@ export function settings({ moduleContext, workbenchSession }: ModuleFCProps<Stat
for (const ensemble of selectedEnsembleIdents) {
for (const vector of selectedVectorNames) {
if (!ensembleVectorListsHelper.isVectorInEnsemble(ensemble, vector)) {
return;
continue;
}

newVectorSpecifications.push({
Expand Down Expand Up @@ -221,15 +222,27 @@ export function settings({ moduleContext, workbenchSession }: ModuleFCProps<Stat
disabled={true}
onChange={handleShowObservations}
/>
<VectorSelector
data={vectorSelectorData}
selectedTags={selectedVectorNames}
placeholder="Add new vector..."
maxNumSelectedNodes={50}
numSecondsUntilSuggestionsAreShown={0.5}
lineBreakAfterTag={true}
onChange={handleVectorSelectChange}
/>
<div
className={resolveClassNames({
"pointer-events-none opacity-80": vectorListQueries.some((query) => query.isLoading),
})}
>
<ApiStatesWrapper
apiResults={vectorListQueries}
loadingComponent={<CircularProgress />}
errorComponent={"Could not load the vectors for selected ensembles"}
>
<VectorSelector
data={vectorSelectorData}
selectedTags={selectedVectorNames}
placeholder="Add new vector..."
maxNumSelectedNodes={50}
numSecondsUntilSuggestionsAreShown={0.5}
lineBreakAfterTag={true}
onChange={handleVectorSelectChange}
/>
</ApiStatesWrapper>
</div>
</CollapsibleGroup>
<CollapsibleGroup expanded={true} title="Visualization">
<RadioGroup
Expand Down
9 changes: 7 additions & 2 deletions frontend/src/modules/SimulationTimeSeriesMatrix/view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,18 +141,23 @@ export const view = ({ moduleContext, workbenchSettings }: ModuleFCProps<State>)
return;
}

const plotData = subplotBuilder.createPlotData();
// TODO: Keep uirevision?
return (
<div className="w-full h-full" ref={wrapperDivRef}>
<Plot
data={subplotBuilder.createPlotData()}
key={plotData.length} // Note: To trigger re-render and remove legends when plotData is empty
data={plotData}
layout={subplotBuilder.createPlotLayout()}
config={{ scrollZoom: true }}
onHover={handleHover}
onUnhover={handleUnHover}
/>
{isDevMode() && (
<div className="absolute top-10 left-5 italic text-pink-400">(rc={renderCount.current})</div>
<>
<div className="absolute top-10 left-5 italic text-pink-400">(rc={renderCount.current})</div>
<div className="absolute top-10 left-20 italic text-pink-400"> Traces: {plotData.length}</div>
</>
)}
</div>
);
Expand Down

0 comments on commit f5a3db2

Please sign in to comment.