-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
dd74c71
commit e0699db
Showing
6 changed files
with
210 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
110 changes: 110 additions & 0 deletions
110
packages/zapp/console/src/components/Launch/LaunchForm/MapInput.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { FormControl, FormHelperText, TextField } from '@material-ui/core'; | ||
import { makeStyles, Theme } from '@material-ui/core/styles'; | ||
import * as React from 'react'; | ||
import { requiredInputSuffix } from './constants'; | ||
import { InputProps, InputType } from './types'; | ||
import { formatType, getLaunchInputId, parseMappedTypeValue } from './utils'; | ||
|
||
const useStyles = makeStyles((theme: Theme) => ({ | ||
formControl: { | ||
width: '100%', | ||
}, | ||
controls: { | ||
width: '100%', | ||
display: 'flex', | ||
alignItems: 'center', | ||
flexDirection: 'row', | ||
}, | ||
keyControl: { | ||
marginRight: theme.spacing(1), | ||
}, | ||
valueControl: { | ||
flexGrow: 1, | ||
}, | ||
})); | ||
|
||
/** Handles rendering of the input component for any primitive-type input */ | ||
export const MapInput: React.FC<InputProps> = (props) => { | ||
const { | ||
error, | ||
name, | ||
onChange, | ||
value = '', | ||
typeDefinition: { subtype }, | ||
} = props; | ||
const hasError = !!error; | ||
const helperText = hasError ? error : props.helperText; | ||
const classes = useStyles(); | ||
|
||
const { key: mapKey, value: mapValue } = parseMappedTypeValue(value); | ||
|
||
const valueError = error?.startsWith("Value's value"); | ||
|
||
const handleKeyChange = React.useCallback( | ||
(e: React.ChangeEvent<HTMLInputElement>) => { | ||
onChange(JSON.stringify({ [e.target.value || '']: mapValue })); | ||
}, | ||
[mapValue], | ||
); | ||
|
||
const handleValueChange = React.useCallback( | ||
(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { | ||
onChange(JSON.stringify({ [mapKey]: e.target.value || '' })); | ||
}, | ||
[mapKey], | ||
); | ||
|
||
const keyControl = ( | ||
<TextField | ||
error={hasError && !valueError} | ||
id={`${getLaunchInputId(name)}-key`} | ||
label={`string${requiredInputSuffix}`} | ||
onChange={handleKeyChange} | ||
value={mapKey} | ||
variant="outlined" | ||
className={classes.keyControl} | ||
/> | ||
); | ||
let valueControl: JSX.Element; | ||
|
||
switch (subtype?.type) { | ||
case InputType.String: | ||
case InputType.Integer: | ||
valueControl = ( | ||
<TextField | ||
error={valueError} | ||
id={`${getLaunchInputId(name)}-value`} | ||
label={`${formatType(subtype)}${requiredInputSuffix}`} | ||
onChange={handleValueChange} | ||
value={mapValue} | ||
variant="outlined" | ||
className={classes.valueControl} | ||
type={subtype.type === InputType.Integer ? 'number' : 'text'} | ||
/> | ||
); | ||
break; | ||
default: | ||
valueControl = ( | ||
<TextField | ||
error={valueError} | ||
id={`${getLaunchInputId(name)}-value`} | ||
label={subtype ? formatType(subtype) + requiredInputSuffix : ''} | ||
onChange={handleValueChange} | ||
value={mapValue} | ||
variant="outlined" | ||
multiline | ||
className={classes.valueControl} | ||
/> | ||
); | ||
} | ||
|
||
return ( | ||
<FormControl className={classes.formControl}> | ||
<div className={classes.controls}> | ||
{keyControl} | ||
{valueControl} | ||
</div> | ||
<FormHelperText id={`${getLaunchInputId(name)}-helper`}>{helperText}</FormHelperText> | ||
</FormControl> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
81 changes: 81 additions & 0 deletions
81
packages/zapp/console/src/components/Launch/LaunchForm/inputHelpers/map.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { stringifyValue } from 'common/utils'; | ||
import { Core } from 'flyteidl'; | ||
import { InputTypeDefinition, InputValue } from '../types'; | ||
import { getHelperForInput } from './getHelperForInput'; | ||
import { ConverterInput, InputHelper, InputValidatorParams } from './types'; | ||
|
||
const missingSubTypeError = 'Unexpected missing subtype for map'; | ||
|
||
function fromLiteral(literal: Core.ILiteral, { subtype }: InputTypeDefinition): InputValue { | ||
if (!subtype) { | ||
throw new Error(missingSubTypeError); | ||
} | ||
if (!literal.map) { | ||
throw new Error('Map literal missing `map` property'); | ||
} | ||
if (!literal.map.literals) { | ||
throw new Error('Map literal missing `map.literals` property'); | ||
} | ||
if (typeof literal.map.literals !== 'object') { | ||
throw new Error('Map literal is not an object'); | ||
} | ||
if (!Object.keys(literal.map.literals).length) { | ||
throw new Error('Map literal object is empty'); | ||
} | ||
|
||
const key = Object.keys(literal.map.literals)[0]; | ||
const childLiteral = literal.map.literals[key]; | ||
const helper = getHelperForInput(subtype.type); | ||
|
||
return stringifyValue({ [key]: helper.fromLiteral(childLiteral, subtype) }); | ||
} | ||
|
||
function toLiteral({ value, typeDefinition: { subtype } }: ConverterInput): Core.ILiteral { | ||
if (!subtype) { | ||
throw new Error(missingSubTypeError); | ||
} | ||
const obj = JSON.parse(value.toString()); | ||
const key = Object.keys(obj)?.[0]; | ||
|
||
const helper = getHelperForInput(subtype.type); | ||
|
||
return { | ||
map: { literals: { [key]: helper.toLiteral({ value: obj[key], typeDefinition: subtype }) } }, | ||
}; | ||
} | ||
|
||
function validate({ value, typeDefinition: { subtype } }: InputValidatorParams) { | ||
if (!subtype) { | ||
throw new Error(missingSubTypeError); | ||
} | ||
if (typeof value !== 'string') { | ||
throw new Error('Value is not a string'); | ||
} | ||
if (!value.toString().length) { | ||
throw new Error('Value is required'); | ||
} | ||
try { | ||
JSON.parse(value.toString()); | ||
} catch (e) { | ||
throw new Error(`Value did not parse to an object`); | ||
} | ||
const obj = JSON.parse(value.toString()); | ||
if (!Object.keys(obj).length || !Object.keys(obj)[0].trim().length) { | ||
throw new Error("Value's key is required"); | ||
} | ||
const key = Object.keys(obj)[0]; | ||
const helper = getHelperForInput(subtype.type); | ||
const subValue = obj[key]; | ||
|
||
try { | ||
helper.validate({ value: subValue, typeDefinition: subtype, name: '', required: false }); | ||
} catch (e) { | ||
throw new Error("Value's value is invalid"); | ||
} | ||
} | ||
|
||
export const mapHelper: InputHelper = { | ||
fromLiteral, | ||
toLiteral, | ||
validate, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters