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

primary form inputs with form generation for abstracted values

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

+ 40 - 19
dashboard/src/components/Selector.tsx

@@ -5,7 +5,11 @@ type PropsType = {
   activeValue: string,
   options: { value: string, label: string }[],
   setActiveValue: (x: string) => void,
-  dropdownLabel: string
+  width: string,
+  dropdownLabel?: string,
+  dropdownWidth?: string,
+  dropdownMaxHeight?: string,
+  closeOverlay?: boolean
 };
 
 type StateType = {
@@ -16,14 +20,20 @@ export default class Selector extends Component<PropsType, StateType> {
     expanded: false
   }
 
+  handleOptionClick = (option: { value: string, label: string }) => {
+    this.props.setActiveValue(option.value);
+    this.props.closeOverlay ? null : this.setState({ expanded: false });
+  }
+
   renderOptionList = () => {
-    let { options, activeValue, setActiveValue } = this.props;
+    let { options, activeValue } = this.props;
     return options.map((option: { value: string, label: string }, i: number) => {
       return (
         <Option
           key={i}
           selected={option.value === activeValue}
-          onClick={() => setActiveValue(option.value)}
+          onClick={() => this.handleOptionClick(option)}
+          lastItem={i === options.length - 1}
         >
           {option.label}
         </Option>
@@ -35,8 +45,11 @@ export default class Selector extends Component<PropsType, StateType> {
     if (this.state.expanded) {
       return (
         <div>
-          <CloseOverlay onClick={() => this.setState({ expanded: false })}/>
-          <Dropdown>
+          {this.props.closeOverlay ? <CloseOverlay onClick={() => this.setState({ expanded: false })} /> : null}
+          <Dropdown
+            dropdownWidth={this.props.dropdownWidth ? this.props.dropdownWidth : this.props.width}
+            dropdownMaxHeight={this.props.dropdownMaxHeight}
+          >
             <DropdownLabel>
               {this.props.dropdownLabel}
             </DropdownLabel>
@@ -47,6 +60,13 @@ export default class Selector extends Component<PropsType, StateType> {
     }
   }
 
+  getLabel = (value: string): any => {
+    let tgt = this.props.options.find((element: { value: string, label: string }) => element.value === value);
+    if (tgt) {
+      return tgt.label;
+    }
+  }
+
   render() {
     let { activeValue } = this.props;
     return (
@@ -54,9 +74,10 @@ export default class Selector extends Component<PropsType, StateType> {
         <MainSelector
           onClick={() => this.setState({ expanded: !this.state.expanded })}
           expanded={this.state.expanded}
+          width={this.props.width}
         >
           <TextWrap>
-            {activeValue === '' ? 'All' : activeValue}
+            {activeValue === '' ? 'All' : this.getLabel(activeValue)}
           </TextWrap>
           <i className="material-icons">arrow_drop_down</i>
         </MainSelector>
@@ -81,7 +102,7 @@ const DropdownLabel = styled.div`
 
 const Option = styled.div` 
   width: 100%;
-  border-bottom: 1px solid #ffffff10;
+  border-bottom: 1px solid ${(props: { selected: boolean, lastItem: boolean }) => props.lastItem ? '#ffffff00' : '#ffffff15'};
   height: 35px;
   font-size: 13px;
   padding-top: 9px;
@@ -92,7 +113,7 @@ const Option = styled.div`
   white-space: nowrap;
   overflow: hidden;
   text-overflow: ellipsis;
-  background: ${(props: { selected: boolean }) => props.selected ? '#ffffff11' : ''};
+  background: ${(props: { selected: boolean, lastItem: boolean }) => props.selected ? '#ffffff11' : ''};
 
   :hover {
     background: #ffffff22;
@@ -103,8 +124,8 @@ const CloseOverlay = styled.div`
   position: fixed;
   top: 0;
   left: 0;
-  width: 100vw;
-  height: 100vh;
+  width: 100%;
+  height: 100%;
   z-index: 999;
 `;
 
@@ -113,12 +134,13 @@ const Dropdown = styled.div`
   right: 0;
   top: calc(100% + 5px);
   background: #26282f;
-  width: calc(100% + 80px);
-  max-height: 300px;
-  padding-bottom: 20px;
+  width: ${(props: { dropdownWidth: string, dropdownMaxHeight: string }) => props.dropdownWidth};
+  max-height: ${(props: { dropdownWidth: string, dropdownMaxHeight: string }) => props.dropdownMaxHeight ? props.dropdownMaxHeight : '300px'};
+  padding-bottom: 10px;
   border-radius: 3px;
   z-index: 999;
   overflow-y: auto;
+  margin-bottom: 20px;
   box-shadow: 0 8px 20px 0px #00000055;
 `;
 
@@ -127,9 +149,9 @@ const StyledSelector = styled.div`
 `;
 
 const MainSelector = styled.div`
-  width: 150px;
+  width: ${(props: { expanded: boolean, width: string }) => props.width};
   height: 30px;
-  border: 1px solid #ffffff66;
+  border: 1px solid #ffffff55;
   font-size: 13px;
   padding: 5px 10px;
   padding-left: 12px;
@@ -138,14 +160,13 @@ const MainSelector = styled.div`
   align-items: center;
   justify-content: space-between;
   cursor: pointer;
-  background: ${(props: { expanded: boolean }) => props.expanded ? '#ffffff33' : '#ffffff11'};
-
+  background: ${(props: { expanded: boolean, width: string }) => props.expanded ? '#ffffff33' : '#ffffff11'};
   :hover {
-    background: ${(props: { expanded: boolean }) => props.expanded ? '#ffffff33' : '#ffffff22'};
+    background: ${(props: { expanded: boolean, width: string }) => props.expanded ? '#ffffff33' : '#ffffff22'};
   }
 
   > i {
     font-size: 20px;
-    transform: ${(props: { expanded: boolean }) => props.expanded ? 'rotate(180deg)' : ''};
+    transform: ${(props: { expanded: boolean, width: string }) => props.expanded ? 'rotate(180deg)' : ''};
   }
 `;

+ 3 - 0
dashboard/src/main/home/dashboard/NamespaceSelector.tsx

@@ -62,6 +62,9 @@ export default class NamespaceSelector extends Component<PropsType, StateType> {
           setActiveValue={(namespace) => this.props.setNamespace(namespace)}
           options={this.state.namespaceOptions}
           dropdownLabel='Namespace:'
+          width='150px'
+          dropdownWidth='230px'
+          closeOverlay={true}
         />
       </StyledNamespaceSelector>
     );

+ 10 - 5
dashboard/src/main/home/dashboard/expanded-chart/ExpandedChart.tsx

@@ -12,6 +12,7 @@ import ValuesYaml from './ValuesYaml';
 import GraphSection from './GraphSection';
 import ListSection from './ListSection';
 import LogSection from './LogSection';
+import ValuesForm from './values-form/ValuesForm';
 
 type PropsType = {
   currentChart: ChartType,
@@ -31,13 +32,13 @@ type StateType = {
 const tabOptions = [
   { label: 'Chart Overview', value: 'graph' },
   { label: 'Search Chart', value: 'list' },
-  { label: 'Values Editor', value: 'values' },
+  { label: 'Raw Values', value: 'values' },
   { label: 'Logs', value: 'logs' },
 ];
 
 const basicOptions = [
+  { label: 'Update Values', value: 'values-form' },
   { label: 'Environment', value: 'environment' },
-  { label: 'Update Values', value: 'values-abstracted' },
   { label: 'Logs', value: 'logs' },
 ];
 
@@ -45,7 +46,7 @@ const basicOptions = [
 export default class ExpandedChart extends Component<PropsType, StateType> {
   state = {
     showRevisions: false,
-    currentTab: 'environment',
+    currentTab: 'values-form',
     components: [] as ResourceType[],
     revisionPreview: null as (ChartType | null),
     devOpsMode: false
@@ -97,7 +98,7 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
 
   toggleDevOpsMode = () => {
     if (this.state.devOpsMode) {
-      this.setState({ devOpsMode: false, currentTab: 'environment' });
+      this.setState({ devOpsMode: false, currentTab: 'values-form' });
     } else {
       this.setState({ devOpsMode: true, currentTab: 'graph' });
     }
@@ -126,7 +127,6 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
     if (this.state.revisionPreview) {
       chart = this.state.revisionPreview;
     }
-    console.log(chart)
     
     if (this.state.currentTab === 'graph') {
       return (
@@ -158,6 +158,11 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
         <LogSection
         />
       );
+    } else if (this.state.currentTab === 'values-form') {
+      return (
+        <ValuesForm
+        />
+      );
     }
 
     return (

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

@@ -36,7 +36,6 @@ export default class ListSection extends Component<PropsType, StateType> {
   }
 
   renderContents = () => {
-    console.log('rerendered!')
     if (this.props.components && this.props.components.length > 0) {
       return (
         <ResourceList>

+ 3 - 3
dashboard/src/main/home/dashboard/expanded-chart/graph/GraphDisplay.tsx

@@ -516,7 +516,7 @@ export default class GraphDisplay extends Component<PropsType, StateType> {
 const Checkbox = styled.div`
   width: 16px;
   height: 16px;
-  border: 1px solid #ffffff44;
+  border: 1px solid #ffffff55;
   margin: 0px 8px 0px 3px;
   border-radius: 3px;
   background: ${(props: { checked: boolean }) => props.checked ? '#ffffff22' : ''};
@@ -543,7 +543,7 @@ const ToggleLabel = styled.div`
   border-radius: 3px;
   padding-right: 5px;
   cursor: pointer;
-  border: 1px solid #ffffff44;
+  border: 1px solid #ffffff55;
   :hover {
     background: #ffffff22;
 
@@ -572,7 +572,7 @@ const ExpandButton = styled.div`
   justify-content: center;
   align-items: center;
   border-radius: 3px;
-  border: 1px solid #ffffff44;
+  border: 1px solid #ffffff55;
 
   :hover {
     background: #ffffff44; 

+ 62 - 0
dashboard/src/main/home/dashboard/expanded-chart/values-form/CheckboxRow.tsx

@@ -0,0 +1,62 @@
+import React, { Component } from 'react';
+import styled from 'styled-components';
+
+type PropsType = {
+  label: string,
+  checked: boolean,
+  toggle: () => void
+};
+
+type StateType = {
+};
+
+export default class CheckboxRow extends Component<PropsType, StateType> {
+  render() {
+    return (
+      <StyledCheckboxRow>
+        <CheckboxWrapper onClick={this.props.toggle}>
+          <Checkbox checked={this.props.checked}>
+            <i className="material-icons">done</i>
+          </Checkbox>
+          {this.props.label}
+        </CheckboxWrapper>
+      </StyledCheckboxRow>
+    );
+  }
+}
+        
+const CheckboxWrapper = styled.div`
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+  :hover {
+    > div {
+      background: #ffffff22;
+    }
+  }
+`;
+
+const Checkbox = styled.div`
+  width: 16px;
+  height: 16px;
+  border: 1px solid #ffffff55;
+  margin: 1px 10px 0px 1px;
+  border-radius: 3px;
+  background: ${(props: { checked: boolean }) => props.checked ? '#ffffff22' : '#ffffff11'};
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  > i {
+    font-size: 12px;
+    padding-left: 0px;
+    display: ${(props: { checked: boolean }) => props.checked ? '' : 'none'};
+  }
+`;
+
+const StyledCheckboxRow = styled.div`
+  display: flex;
+  align-items: center;
+  margin-bottom: 15px;
+  margin-top: 20px;
+`;

+ 59 - 0
dashboard/src/main/home/dashboard/expanded-chart/values-form/InputRow.tsx

@@ -0,0 +1,59 @@
+import React, { Component } from 'react';
+import styled from 'styled-components';
+
+type PropsType = {
+  label: string,
+  type: string,
+  value: string | number,
+  unit?: string
+};
+
+type StateType = {
+};
+
+export default class InputRow extends Component<PropsType, StateType> {
+  render() {
+    return (
+      <StyledInputRow>
+        <Label>{this.props.label}</Label>
+        <InputWrapper>
+          <Input type={this.props.type} />
+          <Unit>{this.props.unit}</Unit>
+        </InputWrapper>
+      </StyledInputRow>
+    );
+  }
+}
+
+const Unit = styled.div`
+
+`;
+
+const InputWrapper = styled.div`
+  display: flex;
+  align-items: center;
+`;
+
+const Input = styled.input`
+  outline: none;
+  border: none;
+  font-size: 13px;
+  background: #ffffff11;
+  border: 1px solid #ffffff55;
+  border-radius: 3px;
+  width: 270px;
+  color: white;
+  padding: 5px 8px;
+  margin-right: 8px;
+  height: 30px;
+`;
+
+const Label = styled.div`
+  color: #ffffff;
+  margin-bottom: 10px;
+`;
+
+const StyledInputRow = styled.div`
+  margin-bottom: 15px;
+  margin-top: 20px;
+`;

+ 49 - 0
dashboard/src/main/home/dashboard/expanded-chart/values-form/SelectRow.tsx

@@ -0,0 +1,49 @@
+import React, { Component } from 'react';
+import styled from 'styled-components';
+
+import Selector from '../../../../../components/Selector';
+
+type PropsType = {
+  label: string,
+  value: string,
+  setActiveValue: (x: string) => void,
+  options: { value: string, label: string }[],
+  dropdownLabel?: string
+};
+
+type StateType = {
+};
+
+export default class SelectRow extends Component<PropsType, StateType> {
+  render() {
+    return (
+      <StyledSelectRow>
+        <Label>{this.props.label}</Label>
+        <SelectWrapper>
+          <Selector
+            activeValue={this.props.value}
+            setActiveValue={this.props.setActiveValue}
+            options={this.props.options}
+            dropdownLabel={this.props.dropdownLabel}
+            width='270px'
+            dropdownMaxHeight={'210px'}
+          />
+        </SelectWrapper>
+      </StyledSelectRow>
+    );
+  }
+}
+
+const SelectWrapper = styled.div`
+  display: flex;
+`;
+
+const Label = styled.div`
+  color: #ffffff;
+  margin-bottom: 10px;
+`;
+
+const StyledSelectRow = styled.div`
+  margin-bottom: 15px;
+  margin-top: 20px;
+`;

+ 168 - 0
dashboard/src/main/home/dashboard/expanded-chart/values-form/ValuesForm.tsx

@@ -0,0 +1,168 @@
+import React, { Component } from 'react';
+import styled from 'styled-components';
+
+import SaveButton from '../../../../../components/SaveButton';
+import CheckboxRow from './CheckboxRow';
+import InputRow from './InputRow';
+import SelectRow from './SelectRow';
+
+type PropsType = {
+};
+
+type StateType = any;
+
+const naiveFormArray = [
+  { type: 'heading', data: '🍦 Dessert' },
+  { type: 'helper', data: 'Select your favorite dessert' },
+  {
+    field: 'dessert', type: 'select', data: {
+      label: 'Base flavor',
+      options: [
+        { label: 'vanilla', value: 'A' },
+        { label: 'chocolate', value: 'B' },
+        { label: 'wasabi', value: 'C' }
+      ]
+    }
+  },
+  {
+    field: 'topping', type: 'select', data: {
+      label: 'Topping',
+      options: [
+        { label: 'sprinkles', value: 'A' },
+        { label: 'gummy-worms', value: 'B' },
+        { label: 'salt', value: 'C' }
+      ]
+    }
+  },
+  { type: 'heading', data: '⚡ Resources' },
+  { type: 'helper', data: 'Update computing resources and memory for certain resources.' },
+  { field: 'arguable', type: 'checkbox', data: { label: 'Use a persistent volume' } },
+  { field: 'horizon', type: 'checkbox', data: { label: 'Use a refurbished Telecaster' } },
+  { type: 'helper', data: 'Update computing resources and memory for certain resources.' },
+  { field: 'name', type: 'input', data: { type: 'string', label: 'Resource name' } },
+  { field: 'oof', type: 'checkbox', data: { label: 'Use a perspective vortex' } },
+  { field: 'memory', type: 'input', data: { type: 'number', label: 'Memory', unit: 'Mi' } },
+  { type: 'helper', data: 'Update computing resources and memory for certain resources.' },
+  {
+    field: 'ocean', type: 'select', data: {
+      label: 'Some stuff',
+      options: [
+        { label: 'volcano', value: 'A' },
+        { label: 'typhon', value: 'B' },
+        { label: 'intergalactic', value: 'C' }
+      ]
+    }
+  },
+];
+
+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) {
+        case 'checkbox':
+          formState[item.field] = false;
+          break;
+        case 'input':
+          formState[item.field] = '';
+          break;
+        case 'select':
+          formState[item.field] = item.data.options[0].value;
+        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:
+        }
+      });
+    }
+  }
+
+  render() {
+    return (
+      <Wrapper>
+        <StyledValuesForm>
+          {this.renderFormContents()}
+        </StyledValuesForm>
+        <SaveButton
+          text='Save Values'
+          onClick={() => console.log(this.state)}
+          status={null}
+        />
+      </Wrapper>
+    );
+  }
+}
+
+const Wrapper = styled.div`
+  width: 100%;
+  height: 100%;
+`;
+
+const Helper = styled.div`
+  color: #aaaabb;
+  font-size: 13px;
+  margin-bottom: 15px;
+  margin-top: 20px;
+`;
+
+const Heading = styled.div`
+  color: white;
+  font-weight: 500;
+  font-size: 16px;
+  margin-top: 30px;
+  margin-bottom: 5px;
+`;
+
+const StyledValuesForm = styled.div`
+  width: 100%;
+  height: calc(100% - 60px);
+  background: #ffffff11;
+  padding: 0px 35px 50px;
+  position: relative;
+  border-radius: 5px;
+  overflow: auto;
+`;