index.jsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /*
  2. Copyright (C) 2017 Cloudbase Solutions SRL
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. // @flow
  15. import React from 'react'
  16. import styled from 'styled-components'
  17. import { observer } from 'mobx-react'
  18. import Switch from '../../atoms/Switch'
  19. import TextInput from '../../atoms/TextInput'
  20. import Dropdown from '../../molecules/Dropdown'
  21. import InfoIcon from '../../atoms/InfoIcon'
  22. import PropertiesTable from '../../molecules/PropertiesTable'
  23. import StyleProps from '../../styleUtils/StyleProps'
  24. import LabelDictionary from '../../../utils/LabelDictionary'
  25. import type { Field } from '../../../types/Field'
  26. import asteriskImage from './images/asterisk.svg'
  27. const getDirection = props => {
  28. if (props.type === 'strict-boolean' || props.type === 'boolean') {
  29. return 'row'
  30. }
  31. return 'column'
  32. }
  33. const Wrapper = styled.div`
  34. display: flex;
  35. flex-direction: ${props => getDirection(props)};
  36. ${props => getDirection(props) === 'row' ? '' : 'justify-content: center;'}
  37. `
  38. const Label = styled.div`
  39. font-weight: ${StyleProps.fontWeights.medium};
  40. ${props => getDirection(props) === 'column' ? 'margin-bottom: 8px;' : ''}
  41. `
  42. const LabelText = styled.span`
  43. margin-right: 24px;
  44. `
  45. const Asterisk = styled.div`
  46. ${StyleProps.exactSize('16px')}
  47. display: inline-block;
  48. background: url('${asteriskImage}') center no-repeat;
  49. margin-bottom: -3px;
  50. margin-left: ${props => props.marginLeft || '0px'};
  51. `
  52. type Props = {
  53. type: 'replica' | 'migration',
  54. name: string,
  55. value: any,
  56. onChange: (value: any) => void,
  57. valueCallback: (prop: Field, value: any) => void,
  58. className?: string,
  59. properties: Field[],
  60. enum: string[],
  61. required: boolean,
  62. width?: number,
  63. skipNullValue?: boolean,
  64. }
  65. @observer
  66. class WizardOptionsField extends React.Component<Props> {
  67. renderSwitch(propss: { triState: boolean }) {
  68. return (
  69. <Switch
  70. width="112px"
  71. justifyContent="flex-end"
  72. triState={propss.triState}
  73. checked={this.props.value}
  74. onChange={checked => { this.props.onChange(checked) }}
  75. style={{ marginTop: '-8px' }}
  76. leftLabel
  77. data-test-id="wOptionsField-switch"
  78. />
  79. )
  80. }
  81. renderTextInput() {
  82. return (
  83. <TextInput
  84. width={`${StyleProps.inputSizes.wizard.width}px`}
  85. value={this.props.value}
  86. onChange={e => { this.props.onChange(e.target.value) }}
  87. placeholder={LabelDictionary.get(this.props.name)}
  88. data-test-id="wOptionsField-textInput"
  89. />
  90. )
  91. }
  92. renderObjectTable() {
  93. if (!this.props.properties || !this.props.properties.length) {
  94. return null
  95. }
  96. return (
  97. <PropertiesTable
  98. properties={this.props.properties}
  99. valueCallback={this.props.valueCallback}
  100. onChange={this.props.onChange}
  101. data-test-id="wOptionsField-propertiesTable"
  102. />
  103. )
  104. }
  105. renderEnumDropdown() {
  106. let items = this.props.enum.map(e => {
  107. if (typeof e !== 'string' && e.separator === true) {
  108. return e
  109. }
  110. return {
  111. label: typeof e === 'string' ? e : e.name,
  112. value: typeof e === 'string' ? e : e.id,
  113. }
  114. })
  115. if (!this.props.skipNullValue) {
  116. items = [
  117. { label: 'Choose a value', value: null },
  118. ...items,
  119. ]
  120. }
  121. let selectedItem = items.find(i => i.value === this.props.value)
  122. return (
  123. <Dropdown
  124. width={this.props.width || StyleProps.inputSizes.wizard.width}
  125. data-test-id={`wOptionsField-dropdown-${this.props.name}`}
  126. noSelectionMessage="Choose a value"
  127. selectedItem={selectedItem}
  128. items={items}
  129. onChange={item => this.props.onChange(item.value)}
  130. />
  131. )
  132. }
  133. renderField() {
  134. let field = null
  135. switch (this.props.type) {
  136. case 'strict-boolean':
  137. field = this.renderSwitch({ triState: false })
  138. break
  139. case 'boolean':
  140. field = this.renderSwitch({ triState: true })
  141. break
  142. case 'string':
  143. if (this.props.enum) {
  144. field = this.renderEnumDropdown()
  145. } else {
  146. field = this.renderTextInput()
  147. }
  148. break
  149. case 'object':
  150. field = this.renderObjectTable()
  151. break
  152. default:
  153. }
  154. return field
  155. }
  156. renderLabel() {
  157. let description = LabelDictionary.getDescription(this.props.name)
  158. return (
  159. <Label>
  160. <LabelText data-test-id="wOptionsField-label">
  161. {LabelDictionary.get(this.props.name)}
  162. </LabelText>
  163. {description ? <InfoIcon text={description} marginLeft={-20} /> : null}
  164. {this.props.required ? <Asterisk marginLeft={description ? '4px' : '-16px'} /> : null}
  165. </Label>
  166. )
  167. }
  168. render() {
  169. let field = this.renderField()
  170. if (!field) {
  171. return null
  172. }
  173. return (
  174. <Wrapper type={this.props.type} className={this.props.className}>
  175. {this.renderLabel()}
  176. {field}
  177. </Wrapper>
  178. )
  179. }
  180. }
  181. export default WizardOptionsField