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

Merge branch 'master' of https://github.com/porter-dev/porter

sunguroku 5 лет назад
Родитель
Сommit
24e97d1f77

+ 0 - 1
dashboard/src/components/repo-selector/ContentsList.tsx

@@ -62,7 +62,6 @@ export default class ContentsList extends Component<PropsType, StateType> {
 
   componentDidUpdate(prevProps: PropsType) {
     if (this.props.subdirectory !== prevProps.subdirectory) {
-      console.log('New subdirectory:', this.props.subdirectory);
       this.updateContents();  
     }
   }

+ 12 - 4
dashboard/src/components/values-form/InputRow.tsx

@@ -1,10 +1,11 @@
-import React, { Component } from 'react';
+import React, { ChangeEvent, Component } from 'react';
 import styled from 'styled-components';
 
 type PropsType = {
   label: string,
   type: string,
   value: string | number,
+  setValue: (x: string) => void,
   unit?: string
 };
 
@@ -13,12 +14,19 @@ type StateType = {
 
 export default class InputRow extends Component<PropsType, StateType> {
   render() {
+    let { label, value, type, unit } = this.props;
     return (
       <StyledInputRow>
-        <Label>{this.props.label}</Label>
+        <Label>{label}</Label>
         <InputWrapper>
-          <Input type={this.props.type} />
-          <Unit>{this.props.unit}</Unit>
+          <Input
+            type={type}
+            value={value}
+            onChange={(e: ChangeEvent<HTMLInputElement>) =>
+              this.props.setValue(e.target.value)
+            }
+          />
+          <Unit>{unit}</Unit>
         </InputWrapper>
       </StyledInputRow>
     );

+ 96 - 66
dashboard/src/components/values-form/ValuesForm.tsx

@@ -1,94 +1,123 @@
 import React, { Component } from 'react';
 import styled from 'styled-components';
 
+import { FormYAML, Section, FormElement } from '../../shared/types';
+
 import SaveButton from '../SaveButton';
 import CheckboxRow from './CheckboxRow';
 import InputRow from './InputRow';
 import SelectRow from './SelectRow';
 
 type PropsType = {
+  formData?: FormYAML
 };
 
 type StateType = any;
 
-const naiveFormArray = [
-  { type: 'heading', data: '⚡ Wordpress Settings' },
-  { type: 'helper', data: 'Enable persistent volume for WordPress' },
-  { field: 'pv-enabled', type: 'checkbox', data: { label: 'Persistent volume enabled' } },
-  { field: 'name', type: 'input', data: { type: 'number', label: 'WordPress volume size', unit: 'Gi' } },
-  {
-    field: 'ocean', type: 'select', data: {
-      label: 'Default StorageClass for WordPress',
-      options: [
-        { label: 'Standard', value: 'A' },
-        { label: 'Custom Storage Class', value: 'B' },
-      ]
-    }
-  },
-];
-
 export default class ValuesForm extends Component<PropsType, StateType> {
 
   // Initialize corresponding state fields for form blocks
   componentDidMount() {
     let formState: any = {};
-    naiveFormArray.forEach((item: any, i: number) => {
-      switch (item.type) {
+    this.props.formData.Sections.forEach((section: Section, i: number) => {
+      section.Contents.forEach((item: FormElement, i: number) => {
+
+        // If no name is assigned use values.yaml variable as identifier
+        let key = item.Name || item.Variable;
+        
+        let def = item.Settings.Default;
+        switch (item.Type) {
+          case 'checkbox':
+            formState[key] = def ? def : false;
+            break;
+          case 'string-input':
+            formState[key] = def ? def : '';
+            break;
+          case 'number-input':
+            formState[key] = def ? def : '';
+            break;
+          case 'select':
+            formState[key] = def ? def : item.Settings.Options[0].Value;
+          default:
+        }
+      });
+    });
+    this.setState(formState);
+  }
+
+  renderSection = (section: Section) => {
+    return section.Contents.map((item: FormElement, i: number) => {
+
+      // If no name is assigned use values.yaml variable as identifier
+      let key = item.Name || item.Variable;
+      switch (item.Type) {
+        case 'heading':
+          return <Heading key={i}>{item.Label}</Heading>
+        case 'subtitle':
+          return <Helper key={i}>{item.Label}</Helper>
         case 'checkbox':
-          formState[item.field] = false;
-          break;
-        case 'input':
-          formState[item.field] = '';
-          break;
+          return (
+            <CheckboxRow
+              key={i}
+              checked={this.state[key]}
+              toggle={() => this.setState({ [key]: !this.state[key] })}
+              label={item.Label}
+            />
+          );
+        case 'string-input':
+          return (
+            <InputRow
+              key={i}
+              type={'text'}
+              value={this.state[key]}
+              setValue={(x: string) => this.setState({ [key]: x })}
+              label={item.Label}
+              unit={item.Settings ? item.Settings.Unit : null}
+            />
+          );
+        case 'number-input':
+          return (
+            <InputRow
+              key={i}
+              type={'number'}
+              value={this.state[key]}
+              setValue={(x: string) => this.setState({ [key]: parseInt(x) })}
+              label={item.Label}
+              unit={item.Settings ? item.Settings.Unit : null}
+            />
+          );
         case 'select':
-          formState[item.field] = item.data.options[0].value;
+          return (
+            <SelectRow
+              key={i}
+              value={this.state[key]}
+              setActiveValue={(val) => this.setState({ [key]: val })}
+              options={item.Settings.Options}
+              dropdownLabel=''
+              label={item.Label}
+            />
+          );
         default:
       }
     });
-
-    this.setState(formState);
   }
 
   renderFormContents = () => {
     if (this.state) {
-      return naiveFormArray.map((item: any, i: number) => {
-        switch (item.type) {
-          case 'heading':
-            return <Heading key={i}>{item.data}</Heading>
-          case 'helper':
-            return <Helper key={i}>{item.data}</Helper>
-          case 'checkbox':
-            return (
-              <CheckboxRow
-                key={i}
-                checked={this.state[item.field]}
-                toggle={() => this.setState({ [item.field]: !this.state[item.field] })}
-                label={item.data.label}
-              />
-            );
-          case 'input':
-            return (
-              <InputRow
-                key={i}
-                type={item.data.type}
-                value={this.state[item.field]}
-                label={item.data.label}
-                unit={item.data.unit}
-              />
-            );
-          case 'select':
-            return (
-              <SelectRow
-                key={i}
-                value={this.state[item.field]}
-                setActiveValue={(val) => this.setState({ [item.field]: val })}
-                options={item.data.options}
-                dropdownLabel=''
-                label={item.data.label}
-              />
-            );
-          default:
+      return this.props.formData.Sections.map((section: Section, i: number) => {
+
+        // Hide collapsible section if deciding field is false
+        if (section.ShowIf) {
+          if (!this.state[section.ShowIf]) {
+            return null;
+          }
         }
+
+        return (
+          <div key={i}>
+            {this.renderSection(section)}
+          </div>
+        );
       });
     }
   }
@@ -111,7 +140,7 @@ export default class ValuesForm extends Component<PropsType, StateType> {
 }
 
 const DarkMatter = styled.div`
-  margin-top: -5px;
+  margin-top: 0px;
 `;
 
 const Wrapper = styled.div`
@@ -121,6 +150,7 @@ const Wrapper = styled.div`
 
 const Helper = styled.div`
   color: #aaaabb;
+  line-height: 1.6em;
   font-size: 13px;
   margin-bottom: 15px;
   margin-top: 20px;
@@ -130,7 +160,7 @@ const Heading = styled.div`
   color: white;
   font-weight: 500;
   font-size: 16px;
-  margin-top: 35px;
+  margin-top: 30px;
   margin-bottom: 5px;
 `;
 
@@ -139,7 +169,7 @@ const StyledValuesForm = styled.div`
   height: 100%;
   background: #ffffff11;
   color: #ffffff;
-  padding: 0px 35px;
+  padding: 0px 35px 30px;
   position: relative;
   border-radius: 5px;
   font-size: 13px;

+ 101 - 2
dashboard/src/main/home/dashboard/expanded-chart/ExpandedChart.tsx

@@ -47,6 +47,98 @@ const basicOptions = [
   { label: 'Settings', value: 'settings' },
 ];
 
+// FormYAML represents a chart's values.yaml form abstraction
+export interface FormYAML {
+	Name?: string,  
+	Icon?: string,   
+	Description?: string,   
+	Tags?: string[],
+  Sections?: Section[]
+}
+
+export interface Section {
+  Name?: string,
+  ShowIf?: string,
+  Contents: FormElement[]
+}
+
+// FormElement represents a form element
+export interface FormElement {
+  Type: string,
+  Label: string,
+  Name?: string,
+  Variable?: string,
+  Settings?: {
+    Default?: number | string | boolean,
+    Options?: any[],
+    Unit?: string
+  }
+}
+
+const dummyForm = {
+  Sections: [
+    {
+      Name: 'main',
+      Contents: [
+        {
+          Type: 'heading',
+          Label: '⚡ Electric feel settings',
+          Settings: {}
+        },
+        {
+          Type: 'subtitle',
+          Label: 'Shock me like an electric eel',
+          Settings: {}
+        },
+        {
+          Type: 'number-input',
+          Name: 'voltage',
+          Variable: 'volts',
+          Label: 'Voltage',
+          Settings: {
+            Default: 200,
+            Unit: 'Volts'
+          }
+        },
+        {
+          Type: 'number-input',
+          Name: 'batteries',
+          Variable: 'batteries',
+          Label: 'Batteries',
+          Settings: {
+            Default: 4,
+            Unit: 'AA'
+          }
+        },
+        {
+          Type: 'checkbox',
+          Name: 'trivia-checkbox',
+          Label: 'Show a fun fact?',
+          Settings: {
+            Default: true
+          }
+        },
+      ]
+    },
+    {
+      Name: 'trivia',
+      ShowIf: 'trivia-checkbox',
+      Contents: [
+        {
+          Type: 'heading',
+          Label: '🌊 Ocean fact No. 11232',
+          Settings: {}
+        },
+        {
+          Type: 'subtitle',
+          Label: 'Electric eels can reach huge proportions, exceeding 8 feet in length and 44 pounds in weight.',
+          Settings: {}
+        }
+      ]
+    }
+  ]
+}
+
 // TODO: consolidate revisionPreview and currentChart (currentChart can just be the initial state)
 export default class ExpandedChart extends Component<PropsType, StateType> {
   state = {
@@ -189,8 +281,9 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
       );
     } else if (this.state.currentTab === 'values-form') {
       return (
-        <ValuesForm
-        />
+        <ValuesFormWrapper>
+          <ValuesForm formData={dummyForm} />
+        </ValuesFormWrapper>
       );
     } else if (this.state.currentTab === 'settings') {
       return (
@@ -276,6 +369,12 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
 
 ExpandedChart.contextType = Context;
 
+const ValuesFormWrapper = styled.div`
+  width: 100%;
+  height: calc(100% - 60px);
+  margin-bottom: 60px;
+`;
+
 const Unimplemented = styled.div`
   width: 100%;
   height: 100%;

+ 36 - 2
dashboard/src/main/home/modals/LaunchTemplateModal.tsx

@@ -85,13 +85,19 @@ export default class LaunchTemplateModal extends Component<PropsType, StateType>
       );
     }
 
-    console.log(this.context.currentModalData.template.Form)
+    let subdir = this.state.subdirectory === '' ? '' : '/' + this.state.subdirectory;
     return (
       <Div>
         <Subtitle>Optionally edit default settings for this template</Subtitle>
         <ValuesFormWrapper>
-          <ValuesForm />
+          <ValuesForm
+            formData={this.context.currentModalData.template.Form}
+          />
         </ValuesFormWrapper>
+        <RepoButton onClick={() => this.setState({ currentView: 'repo' })}>
+          <i className="material-icons">keyboard_backspace</i>
+          {this.state.selectedRepo.FullName + subdir}
+        </RepoButton>
       </Div>
     );
   }
@@ -139,6 +145,34 @@ export default class LaunchTemplateModal extends Component<PropsType, StateType>
 
 LaunchTemplateModal.contextType = Context;
 
+const RepoButton = styled.div`
+  height: 40px;
+  font-size: 13px;
+  padding: 6px 20px 7px 13px;
+  border-radius: 5px;
+  background: #ffffff11;
+  color: #ffffff;
+  border: 1px solid #ffffff55;
+  cursor: pointer;
+  user-select: none;
+  display: flex;
+  align-items: center;
+  position: absolute;
+  bottom: 25px;
+  left: 30px;
+  :hover {
+    background: #ffffff22;
+  }
+
+  > i {
+    font-size: 16px;
+    margin-right: 10px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+`;
+
 const Div = styled.div`
   width: calc(100% + 64px);
   margin-left: -32px;

+ 17 - 12
dashboard/src/shared/types.tsx

@@ -74,24 +74,29 @@ export interface PorterChart {
 
 // FormYAML represents a chart's values.yaml form abstraction
 export interface FormYAML {
-	Name: string,  
-	Icon: string,   
-	Description: string,   
-	Tags: string[],
-  Sections: {
-    Name: string,
-    Contents: FormElement[]
-  }[]
+	Name?: string,  
+	Icon?: string,   
+	Description?: string,   
+	Tags?: string[],
+  Sections?: Section[]
+}
+
+export interface Section {
+  Name?: string,
+  ShowIf?: string,
+  Contents: FormElement[]
 }
 
 // FormElement represents a form element
 export interface FormElement {
   Type: string,
   Label: string,
-  Name: string,
-  Variable: string,
-  Settings: {
-    Default: number
+  Name?: string,
+  Variable?: string,
+  Settings?: {
+    Default?: number | string | boolean,
+    Options?: any[],
+    Unit?: string
   }
 }
 

+ 1 - 0
server/api/template_handler.go

@@ -53,6 +53,7 @@ type FormYAML struct {
 	Tags        []string `yaml:"tags"`
 	Sections    []struct {
 		Name     string `yaml:"name"`
+		ShowIf   string `yaml:"show_if"`
 		Contents []struct {
 			Type     string `yaml:"type"`
 			Label    string `yaml:"label"`