Преглед изворни кода

Merge pull request #226 from smiclea/openstack-fields

Use a new component to render Openstack fields
Dorin Paslaru пре 8 година
родитељ
комит
8a03ade1ba

+ 10 - 2
src/components/atoms/TextInput/index.jsx

@@ -41,9 +41,12 @@ const Input = styled.input`
   width: ${props => getInputWidth(props)};
   height: ${StyleProps.inputSizes.regular.height}px;
   line-height: ${StyleProps.inputSizes.regular.height}px;
-  border-radius: ${StyleProps.borderRadius};
   background-color: #FFF;
   border: ${props => props.embedded ? 0 : css`1px solid ${props => borderColor(props)}`};
+  border-top-left-radius: ${props => props.embedded ? 0 : StyleProps.borderRadius};
+  border-top-right-radius: ${StyleProps.borderRadius};
+  border-bottom-left-radius: ${props => props.embedded ? 0 : StyleProps.borderRadius};
+  border-bottom-right-radius: ${StyleProps.borderRadius};
   color: ${Palette.black};
   padding: 0 ${props => props.customRequired ? '29px' : '8px'} 0 ${props => props.embedded ? 0 : '16px'};
   font-size: inherit;
@@ -98,6 +101,7 @@ type Props = {
   showClose?: boolean,
   onCloseClick?: () => void,
   embedded?: boolean,
+  requiredStyle?: any,
   'data-test-id'?: string,
 }
 const TextInput = (props: Props) => {
@@ -114,7 +118,11 @@ const TextInput = (props: Props) => {
         data-test-id="textInput-input"
         {...props}
       />
-      <Required show={required} data-test-id="textInput-required" />
+      <Required
+        show={required}
+        data-test-id="textInput-required"
+        style={props.requiredStyle}
+      />
       <Close
         data-test-id="textInput-close"
         show={showClose && value !== '' && value !== undefined}

+ 34 - 0
src/components/molecules/DropdownInput/images/arrow.js

@@ -0,0 +1,34 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+const arrow = () => `<?xml version="1.0" encoding="UTF-8"?>
+  <svg width="12px" height="7px" viewBox="0 0 12 6" version="1.1" 
+  xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+      <!-- Generator: Sketch 47 (45396) - http://www.bohemiancoding.com/sketch -->
+      <title>Chevron-Grey</title>
+      <desc>Created with Sketch.</desc>
+      <defs></defs>
+      <g id="Symbols" stroke="#616770" stroke-width="1" fill="none" fill-rule="evenodd" 
+      stroke-linecap="round" stroke-linejoin="round">
+          <g id="dropdown-arrow-image" transform="translate(-171.000000, -13.000000)">
+              <g id="Icon/Chevron/Grey" transform="translate(169.000000, 8.000000)">
+                  <polyline id="Rectangle-Copy" transform="translate(8.000000, 5.500000) 
+                  rotate(-315.000000) translate(-8.000000, -5.500000) " 
+                  points="11.8890873 1.6109127 11.8890873 9.3890873 4.1109127 9.3890873"></polyline>
+              </g>
+          </g>
+      </g>
+  </svg>`
+
+export default arrow

+ 95 - 0
src/components/molecules/DropdownInput/index.jsx

@@ -0,0 +1,95 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// @flow
+
+import React from 'react'
+import { observer } from 'mobx-react'
+import styled from 'styled-components'
+
+import DropdownLink from '../DropdownLink'
+import TextInput from '../../atoms/TextInput'
+
+import Palette from '../../styleUtils/Palette'
+import StyleProps from '../../styleUtils/StyleProps'
+
+import arrowImage from './images/arrow'
+
+const Wrapper = styled.div`
+  display: flex;
+  align-items: center;
+  border: 1px solid ${props => props.disabled ? Palette.grayscale[0] : props.highlight ? Palette.alert : Palette.grayscale[3]};
+  border-radius: ${StyleProps.borderRadius};
+  height: ${StyleProps.inputSizes.regular.height - 2}px;
+`
+const linkButtonStyle = props => {
+  return {
+    width: '60px',
+    height: '14px',
+    padding: '8px',
+    background: props.disabled ? Palette.grayscale[0] : Palette.grayscale[1],
+    borderTopLeftRadius: StyleProps.borderRadius,
+    borderBottomLeftRadius: StyleProps.borderRadius,
+    justifyContent: 'center',
+  }
+}
+type ItemType = {
+  label: string,
+  value: string,
+  [string]: any,
+}
+type Props = {
+  items: ItemType[],
+  selectedItem: string,
+  onItemChange: (item: ItemType) => void,
+  inputValue: string,
+  onInputChange: (value: string) => void,
+  placeholder?: string,
+  required?: boolean,
+  highlight?: boolean,
+  disabled?: boolean,
+}
+type State = {}
+@observer
+class DropdownInput extends React.Component<Props, State> {
+  render() {
+    return (
+      <Wrapper highlight={this.props.highlight} disabled={this.props.disabled}>
+        <DropdownLink
+          linkButtonStyle={linkButtonStyle(this.props)}
+          items={this.props.items}
+          selectedItem={this.props.selectedItem}
+          onChange={this.props.onItemChange}
+          listWidth="auto"
+          secondary
+          disabled={this.props.disabled}
+          arrowImage={arrowImage}
+        />
+        <TextInput
+          embedded
+          width="146px"
+          style={{ paddingLeft: '8px', height: '30px' }}
+          required={this.props.required}
+          value={this.props.inputValue}
+          onChange={e => { this.props.onInputChange(e.target.value) }}
+          placeholder={this.props.placeholder}
+          disabled={this.props.disabled}
+          requiredStyle={{ top: '12px' }}
+        />
+      </Wrapper>
+    )
+  }
+}
+
+export default DropdownInput

+ 71 - 0
src/components/molecules/DropdownInput/story.jsx

@@ -0,0 +1,71 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// @flow
+
+import React from 'react'
+import { storiesOf } from '@storybook/react'
+import DropdownInput from '.'
+
+const items = [{
+  label: 'Name',
+  value: 'name',
+}, {
+  label: 'ID',
+  value: 'id',
+}]
+type Props = {
+  disabled?: boolean,
+  required?: boolean,
+  highlight?: boolean,
+}
+type State = {
+  selectedItem: string,
+  inputValue: string
+}
+class Wrapper extends React.Component<Props, State> {
+  state = {
+    selectedItem: 'id',
+    inputValue: '',
+  }
+
+  render() {
+    return (
+      <div style={{ marginLeft: '128px', background: 'white', padding: '42px' }}>
+        <DropdownInput
+          items={items}
+          selectedItem={this.state.selectedItem}
+          onItemChange={item => { this.setState({ selectedItem: item.value }) }}
+          inputValue={this.state.inputValue}
+          onInputChange={inputValue => { this.setState({ inputValue }) }}
+          placeholder={this.state.selectedItem === 'id' ? 'The ID' : 'The Name'}
+          disabled={this.props.disabled}
+          highlight={this.props.highlight}
+          required={this.props.required}
+        />
+      </div>
+    )
+  }
+}
+
+storiesOf('DropdownInput', module)
+  .add('default', () => (
+    <Wrapper />
+  ))
+  .add('disabled', () => (
+    <Wrapper disabled />
+  ))
+  .add('required highlighted', () => (
+    <Wrapper required highlight />
+  ))

+ 22 - 0
src/components/molecules/DropdownLink/images/arrow.js

@@ -0,0 +1,22 @@
+// @flow
+
+const arrow = (color: string) =>
+  `<?xml version="1.0" encoding="UTF-8"?>
+  <svg width="12px" height="7px" viewBox="0 0 12 7" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
+      <title>Chevron-Grey Copy</title>
+      <desc>Created with Sketch.</desc>
+      <defs></defs>
+      <g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+          <g id="arrow" transform="translate(-788.000000, -175.000000)" stroke-width="1.5" stroke="${color}">
+              <g id="Mini-Menu" transform="translate(566.000000, 169.000000)">
+                  <g id="Icon/Chevron/Blue" transform="translate(220.000000, 1.000000)">
+                      <polyline id="Rectangle-Copy" transform="translate(8.000000, 5.500000) rotate(-315.000000) translate(-8.000000, -5.500000) " points="11.8890873 1.6109127 11.8890873 9.3890873 4.1109127 9.3890873"></polyline>
+                  </g>
+              </g>
+          </g>
+      </g>
+  </svg>`
+
+
+export default arrow

+ 0 - 16
src/components/molecules/DropdownLink/images/arrow.svg

@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="12px" height="7px" viewBox="0 0 12 7" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
-    <title>Chevron-Grey Copy</title>
-    <desc>Created with Sketch.</desc>
-    <defs></defs>
-    <g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="Elements/Scheduler" transform="translate(-788.000000, -175.000000)" stroke-width="1.5" stroke="#0044CA">
-            <g id="Mini-Menu" transform="translate(566.000000, 169.000000)">
-                <g id="Icon/Chevron/Blue" transform="translate(220.000000, 1.000000)">
-                    <polyline id="Rectangle-Copy" transform="translate(8.000000, 5.500000) rotate(-315.000000) translate(-8.000000, -5.500000) " points="11.8890873 1.6109127 11.8890873 9.3890873 4.1109127 9.3890873"></polyline>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

+ 22 - 8
src/components/molecules/DropdownLink/index.jsx

@@ -24,7 +24,7 @@ import SearchInput from '../../molecules/SearchInput'
 import Palette from '../../styleUtils/Palette'
 import StyleProps from '../../styleUtils/StyleProps'
 
-import arrowImage from './images/arrow.svg'
+import arrowImage from './images/arrow.js'
 import checkmarkImage from './images/checkmark.svg'
 
 const Wrapper = styled.div`
@@ -39,8 +39,8 @@ const LinkButton = styled.div`
 `
 const List = styled.div`
   position: absolute;
-  z-index: 20;
-  padding: 8px;
+  z-index: 1001;
+  padding: 8px 16px 8px 8px;
   background: ${Palette.grayscale[1]};
   border-radius: 4px;
   border: 1px solid ${Palette.grayscale[0]};
@@ -89,7 +89,7 @@ const Checkmark = styled.div`
   margin-right: 8px;
 `
 const Label = styled.div`
-  color: ${Palette.primary};
+  color: ${props => props.secondary ? Palette.grayscale[4] : Palette.primary};
   white-space: nowrap;
   overflow: hidden;
   text-overflow: ellipsis;
@@ -97,9 +97,8 @@ const Label = styled.div`
 const Arrow = styled.div`
   width: 16px;
   height: 16px;
-  background: url('${arrowImage}') center no-repeat;
   margin-left: 4px;
-  margin-top: -1px;
+  margin-top: -3px;
 `
 const EmptySearch = styled.div`
   margin-top: 8px;
@@ -122,7 +121,10 @@ type Props = {
   listWidth?: string,
   searchable?: boolean,
   disabled?: boolean,
+  secondary?: boolean,
   'data-test-id'?: string,
+  linkButtonStyle?: any,
+  arrowImage?: (color: string) => string,
 }
 type State = {
   showDropdownList: boolean,
@@ -340,6 +342,8 @@ class DropdownLink extends React.Component<Props, State> {
       return this.props.selectItemLabel
     }
 
+    let arrowImageFunc = this.props.arrowImage || arrowImage
+
     return (
       <Wrapper
         className={this.props.className}
@@ -350,9 +354,19 @@ class DropdownLink extends React.Component<Props, State> {
         <LinkButton
           onClick={() => this.handleButtonClick()}
           disabled={this.props.disabled}
+          style={this.props.linkButtonStyle}
         >
-          <Label innerRef={label => { this.labelRef = label }} data-test-id="dropdownLink-label">{renderLabel()}</Label>
-          <Arrow innerRef={arrow => { this.arrowRef = arrow }} />
+          <Label
+            secondary={this.props.secondary}
+            innerRef={label => { this.labelRef = label }}
+            data-test-id="dropdownLink-label"
+          >{renderLabel()}</Label>
+          <Arrow
+            innerRef={arrow => { this.arrowRef = arrow }}
+            dangerouslySetInnerHTML={{
+              __html: arrowImageFunc(this.props.secondary ? Palette.grayscale[3] : Palette.primary),
+            }}
+          />
         </LinkButton>
         {this.renderList()}
       </Wrapper>

+ 34 - 0
src/components/molecules/EndpointField/index.jsx

@@ -23,6 +23,8 @@ import TextInput from '../../atoms/TextInput'
 import RadioInput from '../../atoms/RadioInput'
 import InfoIcon from '../../atoms/InfoIcon'
 import Dropdown from '../../molecules/Dropdown'
+import DropdownInput from '../../molecules/DropdownInput'
+import type { Field as FieldType } from '../../../types/Field'
 
 import LabelDictionary from '../../../utils/LabelDictionary'
 import StyleProps from '../../styleUtils/StyleProps'
@@ -45,6 +47,8 @@ type Props = {
   type: string,
   value: any,
   onChange?: (value: any) => void,
+  getFieldValue?: (fieldName: string) => string,
+  onFieldChange?: (fieldName: string, fieldValue: string) => void,
   className?: string,
   minimum: number,
   maximum: number,
@@ -54,6 +58,7 @@ type Props = {
   highlight: boolean,
   disabled: boolean,
   enum?: string[],
+  items?: FieldType[],
 }
 @observer
 class Field extends React.Component<Props> {
@@ -143,8 +148,37 @@ class Field extends React.Component<Props> {
     )
   }
 
+  renderDropdownInput() {
+    if (!this.props.items) {
+      return null
+    }
+
+    let items = this.props.items.map(field => {
+      return {
+        value: field.name,
+        label: field.label || LabelDictionary.get(field.name),
+      }
+    })
+
+    return (
+      <DropdownInput
+        items={items}
+        selectedItem={this.props.value}
+        onItemChange={item => { if (this.props.onChange) this.props.onChange(item.value) }}
+        inputValue={this.props.getFieldValue ? this.props.getFieldValue(this.props.value) : ''}
+        onInputChange={value => { if (this.props.onFieldChange) this.props.onFieldChange(this.props.value, value) }}
+        placeholder={LabelDictionary.get(this.props.value)}
+        required={this.props.required}
+        highlight={this.props.highlight}
+        disabled={this.props.disabled}
+      />
+    )
+  }
+
   renderInput() {
     switch (this.props.type) {
+      case 'input-choice':
+        return this.renderDropdownInput()
       case 'boolean':
         return this.renderSwitch()
       case 'string':

+ 1 - 1
src/components/organisms/Endpoint/index.jsx

@@ -383,7 +383,7 @@ class Endpoint extends React.Component<Props, State> {
           cancelButtonText: this.props.cancelButtonText,
           getFieldValue: field => this.getFieldValue(field),
           highlightRequired: () => this.highlightRequired(),
-          handleFieldChange: (field, value) => { this.handleFieldsChange([{ field, value }]) },
+          handleFieldChange: (field, value) => { if (field) this.handleFieldsChange([{ field, value }]) },
           handleFieldsChange: fields => { this.handleFieldsChange(fields) },
           handleValidateClick: () => { this.handleValidateClick() },
           handleCancelClick: () => { this.handleCancelClick() },

+ 26 - 23
src/plugins/endpoint/openstack/ContentPlugin.jsx

@@ -21,7 +21,7 @@ import ToggleButtonBar from '../../../components/atoms/ToggleButtonBar'
 import type { Field } from '../../../types/Field'
 import { Wrapper, Fields, FieldStyled, Row } from '../default/ContentPlugin'
 
-const ToggleButtonBarStyled = styled(ToggleButtonBar)`
+const ToggleButtonBarStyled = styled(ToggleButtonBar) `
   margin-top: 16px;
 `
 
@@ -30,7 +30,7 @@ type Props = {
   validation: { valid: boolean, validation: { message: string } },
   invalidFields: string[],
   getFieldValue: (field: ?Field) => any,
-  handleFieldChange: (field: Field, value: any) => void,
+  handleFieldChange: (field: ?Field, value: any) => void,
   disabled: boolean,
   cancelButtonText: string,
   validating: boolean,
@@ -63,23 +63,29 @@ class ContentPlugin extends React.Component<Props, State> {
     this.props.onRef(undefined)
   }
 
+  getApiVersion(): number {
+    return this.props.getFieldValue(this.props.connectionInfoSchema.find(n => n.name === 'identity_api_version'))
+  }
+
   handleAdvancedOptionsToggle(useAdvancedOptions: boolean) {
     this.setState({ useAdvancedOptions })
   }
 
   findInvalidFields = () => {
-    const apiVersion = this.props.getFieldValue(this.props.connectionInfoSchema.find(n => n.name === 'identity_api_version'))
+    let inputChoices = ['user_domain', 'project_domain']
+
     const invalidFields = this.props.connectionInfoSchema.filter(field => {
-      let required
-      if (typeof field.required === 'function') {
-        required = field.required(apiVersion)
-      } else {
-        required = field.required
-      }
-      if (required) {
+      if (field.required) {
         let value = this.props.getFieldValue(field)
         return !value
       }
+      let inputChoice = inputChoices.find(c => c === field.name)
+      if (inputChoice && this.getApiVersion() > 2) {
+        let selectionValue = this.props.getFieldValue(this.props.connectionInfoSchema.find(f => f.name === inputChoice))
+        let itemValue = this.props.getFieldValue(this.props.connectionInfoSchema.find(f => f.name === selectionValue))
+        return !itemValue
+      }
+
       return false
     }).map(f => f.name)
 
@@ -87,19 +93,16 @@ class ContentPlugin extends React.Component<Props, State> {
   }
 
   filterSimpleAdvanced(): Field[] {
-    const apiVersion = this.props.getFieldValue(this.props.connectionInfoSchema.find(n => n.name === 'identity_api_version'))
-    const extraAdvancedFields = ['description', 'glance_api_version', 'identity_api_version']
-    return this.props.connectionInfoSchema.filter(field => {
+    let extraAdvancedFields = ['description', 'glance_api_version', 'identity_api_version']
+    if (this.getApiVersion() > 2) {
+      extraAdvancedFields = extraAdvancedFields.concat(['user_domain', 'project_domain'])
+    }
+    let ignoreFields = ['user_domain_id', 'project_domain_id', 'user_domain_name', 'project_domain_name']
+    return this.props.connectionInfoSchema.filter(f => !ignoreFields.find(i => i === f.name)).filter(field => {
       if (this.state.useAdvancedOptions) {
         return true
       }
-      let required
-      if (typeof field.required === 'function') {
-        required = field.required(apiVersion)
-      } else {
-        required = field.required
-      }
-      return required || extraAdvancedFields.find(fieldName => field.name === fieldName)
+      return field.required || extraAdvancedFields.find(fieldName => field.name === fieldName)
     })
   }
 
@@ -116,21 +119,21 @@ class ContentPlugin extends React.Component<Props, State> {
   renderFields() {
     const rows = []
     let lastField
-    let apiVersion = this.props.getFieldValue(this.props.connectionInfoSchema.find(n => n.name === 'identity_api_version'))
-
     let fields = this.filterSimpleAdvanced()
 
     fields.forEach((field, i) => {
       const currentField = (
         <FieldStyled
           {...field}
-          required={typeof field.required === 'function' ? field.required(apiVersion) : field.required}
+          required={field.required || (this.getApiVersion() > 2 ? field.name === 'user_domain' || field.name === 'project_domain' : false)}
           large
           disabled={this.props.disabled}
           password={field.name === 'password'}
           highlight={this.props.invalidFields.findIndex(fn => fn === field.name) > -1}
           value={this.props.getFieldValue(field)}
           onChange={value => { this.props.handleFieldChange(field, value) }}
+          getFieldValue={fieldName => this.props.getFieldValue(this.props.connectionInfoSchema.find(n => n.name === fieldName))}
+          onFieldChange={(fieldName, fieldValue) => { this.props.handleFieldChange(this.props.connectionInfoSchema.find(n => n.name === fieldName), fieldValue) }}
         />
       )
       if (i % 2 !== 0) {

+ 21 - 9
src/plugins/endpoint/openstack/SchemaPlugin.js

@@ -29,8 +29,8 @@ const customSort = (fields: Field[]) => {
     project_name: 6,
     glance_api_version: 7,
     identity_api_version: 8,
-    project_domain_name: 9,
-    user_domain_name: 10,
+    project_domain: 9,
+    user_domain: 10,
   }
   fields.sort((a, b) => {
     if (sortPriority[a.name] && sortPriority[b.name]) {
@@ -55,20 +55,32 @@ export default class ConnectionSchemaParser {
     if (identityField && !identityField.default) {
       identityField.default = identityField.minimum
     }
-    customSort(fields)
 
-    let projectDomainField = fields.find(f => f.name === 'project_domain_name')
-    let userDomainField = fields.find(f => f.name === 'user_domain_name')
-    let requiredFunc = (apiVersion: number) => apiVersion > 2
-    if (projectDomainField && userDomainField) {
-      projectDomainField.required = requiredFunc
-      userDomainField.required = requiredFunc
+    let createInputChoice = (name: string, field1Name: string, field2Name: string) => {
+      let field1 = fields.find(f => f.name === field1Name)
+      let field2 = fields.find(f => f.name === field2Name)
+      if (field1 && field2) {
+        field1.label = 'Name'
+        field2.label = 'ID'
+        let field: Field = {
+          name,
+          type: 'input-choice',
+          items: [field1, field2],
+          default: field1Name,
+        }
+        fields.push(field)
+      }
     }
+    createInputChoice('project_domain', 'project_domain_name', 'project_domain_id')
+    createInputChoice('user_domain', 'user_domain_name', 'user_domain_id')
 
+    customSort(fields)
     return fields
   }
 
   static parseFieldsToPayload(data: { [string]: mixed }, schema: Schema) {
+    delete data.project_domain
+    delete data.user_domain
     let payload = DefaultConnectionSchemaParser.parseFieldsToPayload(data, schema)
     return payload
   }

+ 1 - 1
src/sources/EndpointSource.js

@@ -61,7 +61,7 @@ class EdnpointSource {
   }
   static delete(endpoint: Endpoint): Promise<string> {
     return new Promise((resolve, reject) => {
-      let projectId :any = cookie.get('projectId')
+      let projectId: any = cookie.get('projectId')
 
       Api.send({
         url: `${servicesUrl.coriolis}/${projectId}/endpoints/${endpoint.id}`,

+ 2 - 1
src/types/Field.js

@@ -18,8 +18,8 @@ export type Field = {
   name: string,
   type?: string,
   value?: any,
+  label?: string,
   enum?: string[],
-  required?: boolean | (value: any) => boolean,
   default?: any,
   items?: Field[],
   fields?: Field[],
@@ -27,4 +27,5 @@ export type Field = {
   maximum?: number,
   parent?: string,
   properties?: Field[],
+  required?: boolean,
 }

+ 2 - 0
src/utils/LabelDictionary.js

@@ -99,6 +99,8 @@ class LabelDictionary {
     use_replica: 'Use replica',
     windows_migr_image: { label: 'Windows Migration Image', description: 'The Windows Migration Image information found on the Azure page' },
     linux_migr_image: { label: 'Linux Migration Image', description: 'The Linux Migration Image information found on the Azure page' },
+    user_domain_id: 'User Domain ID',
+    project_domain_id: 'Project Domain ID',
   }
 
   static get(fieldName: ?string): string {