Просмотр исходного кода

overhauled tab region frontend

jusrhee 5 лет назад
Родитель
Сommit
cb48cdd384

+ 9 - 25
dashboard/src/components/TabRegion.tsx

@@ -6,27 +6,21 @@ import Loading from './Loading';
 
 type PropsType = {
   options: { label: string, value: string }[],
-  tabContents: any,
+  currentTab: string,
+  setCurrentTab: (x: string) => void,
   defaultTab?: string,
   addendum?: any,
-  checkTabExists?: boolean, // Handles the currently selected tab disappearing
   color?: string | null,
 };
 
 type StateType = {
-  currentTab: string
 };
 
 // Manages a tab selector and renders the associated view
-// TODO: consider rearchitecturing to support standard re-render
 export default class TabRegion extends Component<PropsType, StateType> {
-  state = {
-    currentTab: this.props.defaultTab
-  }
-
   setDefaultTab = () => {
     if (!this.props.defaultTab && this.props.options[0]) {
-      this.setState({ currentTab: this.props.options[0].value });
+      this.props.setCurrentTab(this.props.options[0].value);
     }
   }
 
@@ -35,24 +29,14 @@ export default class TabRegion extends Component<PropsType, StateType> {
   }
 
   componentDidUpdate(prevProps: PropsType) {
-    let { options, checkTabExists } = this.props;
-    if (prevProps.options !== options && !this.state.currentTab) {
-      this.setDefaultTab();
-    } else if (prevProps.checkTabExists !== checkTabExists
-      && !options.some((e: any) => e.value === this.state.currentTab)) {
+    let { options } = this.props;
+    if (prevProps.options !== options) {
       this.setDefaultTab();
     }
   }
 
-  renderTabContents = () => {
-    let found = this.props.tabContents.find((el: any) => el.value === this.state.currentTab);
-    if (found) {
-      return found.component;
-    }
-  }
-
   renderContents = () => {
-    if (!this.state.currentTab) {
+    if (!this.props.currentTab) {
       return (
         <Loading />
       );
@@ -63,13 +47,13 @@ export default class TabRegion extends Component<PropsType, StateType> {
         <TabSelector
           options={this.props.options}
           color={this.props.color}
-          currentTab={this.state.currentTab}
-          setCurrentTab={(x: string) => this.setState({ currentTab: x })}
+          currentTab={this.props.currentTab}
+          setCurrentTab={(x: string) => this.props.setCurrentTab(x)}
           addendum={this.props.addendum}
         />
         <Gap />
         <TabContents>
-          {this.renderTabContents()}
+          {this.props.children}
         </TabContents>
       </Div>
     );

+ 119 - 113
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx

@@ -16,6 +16,7 @@ import ListSection from './ListSection';
 import StatusSection from './status/StatusSection';
 import ValuesForm from '../../../../components/values-form/ValuesForm';
 import SettingsSection from './SettingsSection';
+import { NavLink } from 'react-router-dom';
 
 type PropsType = {
   currentChart: ChartType,
@@ -31,15 +32,12 @@ type StateType = {
   podSelectors: string[]
   revisionPreview: ChartType | null,
   devOpsMode: boolean,
-  tabOptions: ChoiceType[],
+  tabOptions: any[],
   tabContents: any,
-  checkTabExists: boolean,
+  currentTab: string | null,
   saveValuesStatus: string | null,
 };
 
-// Tabs not display when previewing an old revision
-const excludedTabs = ['status', 'settings', 'deploy'];
-
 /*
   TODO: consolidate revisionPreview and currentChart (currentChart can just be the initial state)
   In general, tab management for ExpandedChart should be refactored. Cases to handle:
@@ -55,9 +53,9 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
     podSelectors: [] as string[],
     revisionPreview: null as (ChartType | null),
     devOpsMode: localStorage.getItem('devOpsMode') === 'true',
-    tabOptions: [] as ChoiceType[],
+    tabOptions: [] as any[],
     tabContents: [] as any,
-    checkTabExists: false,
+    currentTab: null as string | null,
     saveValuesStatus: null as (string | null),
   }
 
@@ -77,21 +75,27 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
       if (err) {
         console.log(err)
       } else {
-        this.setState({ components: res.data.Objects, podSelectors: res.data.PodSelectors }, this.refreshTabs);
+        this.setState({ components: res.data.Objects, podSelectors: res.data.PodSelectors });
       }
     });
   }
 
+  componentDidMount() {
+    this.updateTabs();
+    this.updateResources();
+  }
+
+  componentDidUpdate(prevProps: PropsType) {
+    if (this.props.currentChart !== prevProps.currentChart) {
+      this.updateResources();
+    }
+  }
+
   getFormData = (): any => {
     let { files } = this.props.currentChart.chart;
     for (const file of files) { 
       if (file.name === 'form.yaml') {
         let formData = yaml.load(Base64.decode(file.data));
-        /*
-        if (this.props.currentChart.config) {
-          console.log(formData)
-        }
-        */
         return formData;
       }
     };
@@ -124,117 +128,126 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
       }
     });
   }
-
-  refreshTabs = () => {
-    let formData = this.getFormData();
-    let tabOptions = [] as ChoiceType[];
-    let tabContents = [] as any;
-
-    // Generate form tabs if form.yaml exists
-    if (formData && formData.tabs) {
-      formData.tabs.map((tab: any, i: number) => {
-        tabOptions.push({ value: '@' + tab.name, label: tab.label });
-        tabContents.push({
-          value: '@' + tab.name,
-          component: (
-            <ValuesFormWrapper>
-              <ValuesForm 
-                sections={tab.sections} 
-                onSubmit={this.upgradeValues}
-                saveValuesStatus={this.state.saveValuesStatus}
-                config={this.props.currentChart.config}
-              />
-            </ValuesFormWrapper>
-          ),
-        });
-      });
-    }
-
-    // Append universal tabs
-    tabOptions.push(
-      { label: 'Status', value: 'status' },
-      //{ label: 'Deploy', value: 'deploy' },
-      { label: 'Chart Overview', value: 'graph' },
-      { label: 'Settings', value: 'settings' },
-    );
-
-    if (this.state.devOpsMode) {
-      tabOptions.push(
-        { label: 'Manifests', value: 'list' },
-        { label: 'Raw Values', value: 'values' }
-      );
-    }
-
+  
+  renderTabContents = () => {
+    let { 
+      currentTab, 
+      podSelectors, 
+      components, 
+      showRevisions,
+      saveValuesStatus,
+      tabOptions,
+      revisionPreview,
+    } = this.state;
     let { currentChart, refreshChart, setSidebar, setCurrentView } = this.props;
-    let chart = this.state.revisionPreview || currentChart;
-    tabContents.push(
-      {
-        value: 'status', component: (
-          <StatusSection currentChart={chart} selectors={this.state.podSelectors} />
-        ),
-      },
-      {
-        value: 'deploy', component: (
+    let chart = revisionPreview || currentChart;
+
+    switch (currentTab) {
+      case 'status': 
+        return (
+          <StatusSection currentChart={chart} selectors={podSelectors} />
+        );
+      case 'deploy': 
+        return (
           <Unimplemented>Coming soon.</Unimplemented> 
-        ),
-      },
-      {
-        value: 'settings', component: (
+        );
+      case 'settings': 
+        return (
           <SettingsSection
             currentChart={chart}
             refreshChart={refreshChart}
             setCurrentView={setCurrentView}
           /> 
-        ),
-      },
-      {
-        value: 'graph', component: (
+        );
+      case 'graph': 
+        return (
           <GraphSection
-            components={this.state.components}
+            components={components}
             currentChart={chart}
             setSidebar={setSidebar}
 
             // Handle resize YAML wrapper
-            showRevisions={this.state.showRevisions}
+            showRevisions={showRevisions}
           />
-        ),
-      },
-      {
-        value: 'list', component: (
+        );
+      case 'list': 
+        return (
           <ListSection
             currentChart={chart}
-            components={this.state.components}
+            components={components}
 
             // Handle resize YAML wrapper
-            showRevisions={this.state.showRevisions}
+            showRevisions={showRevisions}
           />
-        ),
-      },
-      {
-        value: 'values', component: (
+        );
+      case 'values': 
+        return (
           <ValuesYaml
             currentChart={chart}
             refreshChart={refreshChart}
           />
-        ),
-      },
-    );
-    this.setState({ tabOptions, tabContents });
+        );
+      default:
+        if (currentTab && currentTab.includes('@')) {
+          return tabOptions.map((tab: any, i: number) => {
+
+            // If tab is current, render
+            if (tab.value === currentTab) {
+              
+              return (
+                <ValuesFormWrapper>
+                  <ValuesForm 
+                    sections={tab.sections} 
+                    onSubmit={this.upgradeValues}
+                    saveValuesStatus={saveValuesStatus}
+                    config={currentChart.config}
+                  />
+                </ValuesFormWrapper>
+              );
+            }
+          });
+        }
+    }
   }
 
-  componentDidMount() {
-    this.updateResources();
-  }
+  updateTabs = () => {
+    let formData = this.getFormData();
+    let tabOptions = [] as any[];
 
-  componentDidUpdate(prevProps: PropsType) {
-    if (this.props.currentChart !== prevProps.currentChart) {
-      this.updateResources();
+    // Generate form tabs if form.yaml exists
+    if (formData && formData.tabs) {
+      formData.tabs.map((tab: any, i: number) => {
+        tabOptions.push({ value: '@' + tab.name, label: tab.label, sections: tab.sections });
+      });
     }
+
+    // Append universal tabs
+    tabOptions.push(
+      { label: 'Status', value: 'status' },
+      //{ label: 'Deploy', value: 'deploy' },
+      { label: 'Chart Overview', value: 'graph' },
+      { label: 'Settings', value: 'settings' },
+    );
+
+    if (this.state.devOpsMode) {
+      tabOptions.push(
+        { label: 'Manifests', value: 'list' },
+        { label: 'Raw Values', value: 'values' }
+      );
+    }
+
+    // Filter tabs if previewing an old revision
+    if (this.state.revisionPreview) {
+      let liveTabs = ['status', 'settings', 'deploy'];
+      tabOptions = tabOptions.filter((tab: any) => !liveTabs.includes(tab.value));
+    }
+    
+    this.setState({ tabOptions });
   }
 
   setRevisionPreview = (oldChart: ChartType) => {
     let { currentCluster, currentProject } = this.context;
-    this.setState({ revisionPreview: oldChart, checkTabExists: true });
+    this.setState({ revisionPreview: oldChart }, () => this.updateTabs());
 
     if (oldChart) {
       api.getChartComponents('<token>', {
@@ -249,11 +262,10 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
         if (err) {
           console.log(err)
         } else {
-          this.setState({ components: res.data.Objects, podSelectors: res.data.PodSelectors }, this.refreshTabs);
+          this.setState({ components: res.data.Objects, podSelectors: res.data.PodSelectors });
         }
       });
     } else {
-      this.setState({ checkTabExists: false });
       this.updateResources();
     }
   }
@@ -261,19 +273,13 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
   // TODO: consolidate with pop + push in refreshTabs
   toggleDevOpsMode = () => {
     if (this.state.devOpsMode) {
-      let { tabOptions } = this.state;
-      tabOptions.pop();
-      tabOptions.pop();
-      this.setState({ devOpsMode: false, checkTabExists: true, tabOptions }, () => {
-        localStorage.setItem('devOpsMode', 'false')
+      this.setState({ devOpsMode: false }, () => {
+        this.updateTabs();
+        localStorage.setItem('devOpsMode', 'false');
       });
     } else {
-      let { tabOptions } = this.state;
-      tabOptions.push(
-        { label: 'Manifests', value: 'list' },
-        { label: 'Raw Values', value: 'values' }
-      );
-      this.setState({ devOpsMode: true, tabOptions, checkTabExists: false }, () => {
+      this.setState({ devOpsMode: true }, () => {
+        this.updateTabs();
         localStorage.setItem('devOpsMode', 'true');
       });
     }
@@ -339,18 +345,18 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
           </HeaderWrapper>
 
           <TabRegion
-            options={this.state.tabOptions.filter((opt: any) => {
-              return !this.state.revisionPreview || !excludedTabs.includes(opt.value);
-            })}
-            tabContents={this.state.tabContents}
-            checkTabExists={this.state.checkTabExists}
+            currentTab={this.state.currentTab}
+            setCurrentTab={(x: string) => this.setState({ currentTab: x })}
+            options={this.state.tabOptions}
             color={this.state.revisionPreview ? '#f5cb42' : null}
             addendum={
               <TabButton onClick={this.toggleDevOpsMode} devOpsMode={this.state.devOpsMode}>
                 <i className="material-icons">offline_bolt</i> DevOps Mode
               </TabButton>
             }
-          />
+          >
+            {this.renderTabContents()}
+          </TabRegion>
         </StyledExpandedChart>
       </div>
     );

+ 1 - 0
dashboard/src/main/home/cluster-dashboard/expanded-chart/ListSection.tsx

@@ -45,6 +45,7 @@ export default class ListSection extends Component<PropsType, StateType> {
       let rawYaml = yaml.dump(resource.RawYAML);
       return (
         <ResourceTab
+          key={i}
           handleClick={() => this.setState({ yaml: rawYaml })}
           selected={this.state.yaml === rawYaml}
           kind={resource.Kind}

+ 22 - 22
dashboard/src/main/home/templates/expanded-template/LaunchTemplate.tsx

@@ -23,7 +23,7 @@ type StateType = {
   selectedImageUrl: string | null,
   selectedTag: string | null,
   tabOptions: ChoiceType[],
-  tabContents: any
+  currentTab: string | null,
 };
 
 export default class LaunchTemplate extends Component<PropsType, StateType> {
@@ -34,7 +34,7 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
     selectedImageUrl: '' as string | null,
     selectedTag: '' as string | null,
     tabOptions: [] as ChoiceType[],
-    tabContents: [] as any,
+    currentTab: null as string | null,
   };
 
   onSubmit = (formValues: any) => {
@@ -56,14 +56,12 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
     });
   }
 
-  refreshTabs = () => {
-    // Generate settings tabs from the provided form
-    let tabOptions = [] as ChoiceType[];
-    let tabContents = [] as any;
-    this.props.currentTemplate.form.tabs.map((tab: any, i: number) => {
-      tabOptions.push({ value: tab.name, label: tab.label });
-      tabContents.push({
-        value: tab.name, component: (
+  renderTabContents = () => {
+    return this.props.currentTemplate.form.tabs.map((tab: any, i: number) => {
+
+      // If tab is current, render
+      if (tab.name === this.state.currentTab) {
+        return (
           <ValuesFormWrapper>
             <ValuesForm 
               sections={tab.sections} 
@@ -71,14 +69,19 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
               disabled={!this.state.selectedImageUrl || this.state.selectedImageUrl === ''}
             />
           </ValuesFormWrapper>
-        ),
-      });
+        );
+      }
     });
-    this.setState({ tabOptions, tabContents });
   }
 
   componentDidMount() {
-    this.refreshTabs();
+
+    // Retrieve tab options
+    let tabOptions = [] as ChoiceType[];
+    this.props.currentTemplate.form.tabs.map((tab: any, i: number) => {
+      tabOptions.push({ value: tab.name, label: tab.label });
+    });
+    this.setState({ tabOptions });
 
     // TODO: query with selected filter once implemented
     let { currentProject } = this.context;
@@ -94,12 +97,6 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
     });
   }
 
-  componentDidUpdate(prevProps: PropsType, prevState: StateType) {
-    if (this.state.selectedImageUrl != prevState.selectedImageUrl) {
-      this.refreshTabs();
-    }
-  }
-
   renderIcon = (icon: string) => {
     if (icon) {
       return <Icon src={icon} />
@@ -159,8 +156,11 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
         <Subtitle>Configure additional settings for this template (optional).</Subtitle>
         <TabRegion
           options={this.state.tabOptions}
-          tabContents={this.state.tabContents}
-        />
+          currentTab={this.state.currentTab}
+          setCurrentTab={(x: string) => this.setState({ currentTab: x })}
+        >
+          {this.renderTabContents()}
+        </TabRegion>
       </StyledLaunchTemplate>
     );
   }