21642: Change conditionaltabs to render tabs as hidden to avoid re-renders
authorStephen Smith <stephen@curii.com>
Mon, 29 Apr 2024 13:35:02 +0000 (09:35 -0400)
committerStephen Smith <stephen@curii.com>
Mon, 29 Apr 2024 13:35:02 +0000 (09:35 -0400)
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen@curii.com>

services/workbench2/src/components/conditional-tabs/conditional-tabs.tsx
services/workbench2/src/views/process-panel/process-io-card.tsx

index ff8d517c4e45ef1e50190e666d05a7eadd49e519..499e84df8a335b1b4c419aacd924ec30d425dbad 100644 (file)
@@ -2,14 +2,19 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import React, { ReactNode, useEffect, useState } from "react";
+import React, { CSSProperties, ReactElement, useEffect, useState } from "react";
 import { Tabs, Tab } from "@material-ui/core";
 import { TabsProps } from "@material-ui/core/Tabs";
 
+interface ComponentWithHidden {
+    styles: CSSProperties;
+    hidden: boolean;
+};
+
 export type TabData = {
     show: boolean;
     label: string;
-    content: ReactNode;
+    content: ReactElement<ComponentWithHidden>;
 };
 
 type ConditionalTabsProps = {
@@ -19,7 +24,6 @@ type ConditionalTabsProps = {
 export const ConditionalTabs = (props: Omit<TabsProps, 'value' | 'onChange'> & ConditionalTabsProps) => {
     const [tabState, setTabState] = useState(0);
     const visibleTabs = props.tabs.filter(tab => tab.show);
-    const activeTab = visibleTabs[tabState];
     const visibleTabNames = visibleTabs.map(tab => tab.label).join();
 
     const handleTabChange = (event: React.MouseEvent<HTMLElement>, value: number) => {
@@ -39,6 +43,9 @@ export const ConditionalTabs = (props: Omit<TabsProps, 'value' | 'onChange'> & C
             onChange={handleTabChange} >
             {visibleTabs.map(tab => <Tab key={tab.label} label={tab.label} />)}
         </Tabs>
-        {activeTab && activeTab.content}
+
+        {visibleTabs.map((tab, i) => (
+            React.cloneElement(tab.content, {hidden: i !== tabState})
+        ))}
     </>;
 };
index 76d4c52d04a8d33ea239c395590966d0f1d484c2..6d60b8cf2219455dc362a91356e271c90b3cc743 100644 (file)
@@ -485,12 +485,13 @@ export type ProcessIOParameter = {
 interface ProcessIOPreviewDataProps {
     data: ProcessIOParameter[];
     valueLabel: string;
+    hidden?: boolean;
 }
 
 type ProcessIOPreviewProps = ProcessIOPreviewDataProps & WithStyles<CssRules>;
 
 const ProcessIOPreview = memo(
-    withStyles(styles)(({ data, valueLabel, classes }: ProcessIOPreviewProps) => {
+    withStyles(styles)(({ data, valueLabel, hidden, classes }: ProcessIOPreviewProps) => {
         const showLabel = data.some((param: ProcessIOParameter) => param.label);
 
         const hasMoreValues = (index: number) => (
@@ -546,7 +547,7 @@ const ProcessIOPreview = memo(
             </TableRow>;
         };
 
-        return <div className={classes.tableWrapper}>
+        return <div className={classes.tableWrapper} hidden={hidden}>
             <Table
                 className={classes.paramTableRoot}
                 aria-label="Process IO Preview"
@@ -590,10 +591,11 @@ const ProcessValuePreview = withStyles(styles)(({ value, classes }: ProcessValue
 
 interface ProcessIORawDataProps {
     data: ProcessIOParameter[];
+    hidden?: boolean;
 }
 
-const ProcessIORaw = withStyles(styles)(({ data, classes }: ProcessIORawDataProps & WithStyles<CssRules>) => (
-    <div className={classes.jsonWrapper}>
+const ProcessIORaw = withStyles(styles)(({ data, hidden, classes }: ProcessIORawDataProps & WithStyles<CssRules>) => (
+    <div className={classes.jsonWrapper} hidden={hidden}>
         <Paper elevation={0} style={{minWidth: "100%", height: "100%"}}>
             <DefaultVirtualCodeSnippet
                 lines={JSON.stringify(data, null, 2).split('\n')}
@@ -605,6 +607,7 @@ const ProcessIORaw = withStyles(styles)(({ data, classes }: ProcessIORawDataProp
 
 interface ProcessInputMountsDataProps {
     mounts: InputCollectionMount[];
+    hidden?: boolean;
 }
 
 type ProcessInputMountsProps = ProcessInputMountsDataProps & WithStyles<CssRules>;
@@ -612,10 +615,11 @@ type ProcessInputMountsProps = ProcessInputMountsDataProps & WithStyles<CssRules
 const ProcessInputMounts = withStyles(styles)(
     connect((state: RootState) => ({
         auth: state.auth,
-    }))(({ mounts, classes, auth }: ProcessInputMountsProps & { auth: AuthState }) => (
+    }))(({ mounts, hidden, classes, auth }: ProcessInputMountsProps & { auth: AuthState }) => (
         <Table
             className={classes.mountsTableRoot}
             aria-label="Process Input Mounts"
+            hidden={hidden}
         >
             <TableHead>
                 <TableRow>
@@ -652,10 +656,10 @@ const mapNavigateToProps = (dispatch: Dispatch): ProcessOutputCollectionActionPr
     navigateTo: uuid => dispatch<any>(navigateTo(uuid)),
 });
 
-type ProcessOutputCollectionProps = {outputUuid: string | undefined} & ProcessOutputCollectionActionProps &  WithStyles<CssRules>;
+type ProcessOutputCollectionProps = {outputUuid: string | undefined, hidden?: boolean} & ProcessOutputCollectionActionProps &  WithStyles<CssRules>;
 
-const ProcessOutputCollection = withStyles(styles)(connect(null, mapNavigateToProps)(({ outputUuid, navigateTo, classes }: ProcessOutputCollectionProps) => (
-    <div className={classes.tableWrapper}>
+const ProcessOutputCollection = withStyles(styles)(connect(null, mapNavigateToProps)(({ outputUuid, hidden, navigateTo, classes }: ProcessOutputCollectionProps) => (
+    <div className={classes.tableWrapper} hidden={hidden}>
         <>
             {outputUuid && (
                 <Typography className={classes.collectionLink}>