Skip to content

Commit

Permalink
Merge pull request #31 from lyft/launch-errors
Browse files Browse the repository at this point in the history
Handle validation of list inputs
  • Loading branch information
schottra authored Dec 20, 2019
2 parents ad6a545 + 78d9db2 commit 363864f
Show file tree
Hide file tree
Showing 29 changed files with 833 additions and 480 deletions.
2 changes: 1 addition & 1 deletion src/components/Cache/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as objectHash from 'object-hash';
/** Generic cache key generator. For object, will generate a unique hash.
* Strings are passed through for convenience.
*/
export function getCacheKey(id: object | string) {
export function getCacheKey(id: any[] | object | string) {
return typeof id === 'string' || typeof id === 'symbol'
? id
: objectHash(id);
Expand Down
5 changes: 4 additions & 1 deletion src/components/Launch/LaunchWorkflowForm/CollectionInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ function stringChangeHandler(onChange: InputChangeHandler) {
/** Handles rendering of the input component for a Collection of SimpleType values*/
export const CollectionInput: React.FC<InputProps> = props => {
const {
error,
label,
helperText,
onChange,
typeDefinition: { subtype },
value = ''
Expand All @@ -25,6 +25,8 @@ export const CollectionInput: React.FC<InputProps> = props => {
);
return <UnsupportedInput {...props} />;
}
const hasError = !!error;
const helperText = hasError ? error : props.helperText;
switch (subtype.type) {
case InputType.Blob:
case InputType.Boolean:
Expand All @@ -39,6 +41,7 @@ export const CollectionInput: React.FC<InputProps> = props => {
case InputType.Struct:
return (
<TextField
error={hasError}
helperText={helperText}
fullWidth={true}
label={label}
Expand Down
5 changes: 4 additions & 1 deletion src/components/Launch/LaunchWorkflowForm/DatetimeInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import { InputProps } from './types';
* keyboard.
*/
export const DatetimeInput: React.FC<InputProps> = props => {
const { label, helperText, onChange, value: propValue } = props;
const { error, label, onChange, value: propValue } = props;
const hasError = !!error;
const helperText = hasError ? error : props.helperText;
const value =
typeof propValue === 'string' && propValue.length > 0
? propValue
Expand All @@ -33,6 +35,7 @@ export const DatetimeInput: React.FC<InputProps> = props => {
return (
<MuiPickersUtilsProvider libInstance={moment} utils={momentDateUtils}>
<KeyboardDateTimePicker
error={hasError}
clearable={true}
fullWidth={true}
ampm={false}
Expand Down
63 changes: 9 additions & 54 deletions src/components/Launch/LaunchWorkflowForm/LaunchWorkflowForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,66 +6,23 @@ import {
FormHelperText,
Typography
} from '@material-ui/core';
import { makeStyles, Theme } from '@material-ui/core/styles';
import { WaitForData } from 'components/common';
import { ButtonCircularProgress } from 'components/common/ButtonCircularProgress';
import { APIContextValue, useAPIContext } from 'components/data/apiContext';
import { smallFontSize } from 'components/Theme';
import {
FilterOperationName,
NamedEntityIdentifier,
SortDirection,
workflowSortFields
} from 'models';
import * as React from 'react';
import { CollectionInput } from './CollectionInput';
import { LaunchWorkflowFormInputs } from './LaunchWorkflowFormInputs';
import { SearchableSelector } from './SearchableSelector';
import { SimpleInput } from './SimpleInput';
import { InputProps, InputType, LaunchWorkflowFormProps } from './types';
import { UnsupportedInput } from './UnsupportedInput';
import { useStyles } from './styles';
import { LaunchWorkflowFormProps } from './types';
import { useLaunchWorkflowFormState } from './useLaunchWorkflowFormState';
import { workflowsToSearchableSelectorOptions } from './utils';

const useStyles = makeStyles((theme: Theme) => ({
footer: {
padding: theme.spacing(2)
},
formControl: {
padding: `${theme.spacing(1.5)}px 0`
},
header: {
padding: theme.spacing(2),
width: '100%'
},
inputsSection: {
minHeight: theme.spacing(59),
padding: theme.spacing(2)
},
inputLabel: {
color: theme.palette.text.hint,
fontSize: smallFontSize
},
root: {
display: 'flex',
flexDirection: 'column',
width: '100%'
}
}));

function getComponentForInput(input: InputProps) {
switch (input.typeDefinition.type) {
case InputType.Collection:
return <CollectionInput {...input} />;
case InputType.Map:
case InputType.Schema:
case InputType.Unknown:
case InputType.None:
return <UnsupportedInput {...input} />;
default:
return <SimpleInput {...input} />;
}
}

function generateFetchSearchResults(
{ listWorkflows }: APIContextValue,
workflowId: NamedEntityIdentifier
Expand Down Expand Up @@ -142,14 +99,12 @@ export const LaunchWorkflowForm: React.FC<LaunchWorkflowFormProps> = props => {
spinnerVariant="medium"
{...state.inputLoadingState}
>
{state.inputs.map(input => (
<div
key={input.label}
className={styles.formControl}
>
{getComponentForInput(input)}
</div>
))}
<LaunchWorkflowFormInputs
key={state.formKey}
inputs={state.inputs}
ref={state.formInputsRef}
showErrors={state.showErrors}
/>
</WaitForData>
) : null}
</WaitForData>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as React from 'react';
import { CollectionInput } from './CollectionInput';
import { SimpleInput } from './SimpleInput';
import { useStyles } from './styles';
import {
InputProps,
InputType,
LaunchWorkflowFormInputsRef,
ParsedInput
} from './types';
import { UnsupportedInput } from './UnsupportedInput';
import { useFormInputsState } from './useFormInputsState';

function getComponentForInput(input: InputProps, showErrors: boolean) {
const props = { ...input, error: showErrors ? input.error : undefined };
switch (input.typeDefinition.type) {
case InputType.Collection:
return <CollectionInput {...props} />;
case InputType.Map:
case InputType.Schema:
case InputType.Unknown:
case InputType.None:
return <UnsupportedInput {...props} />;
default:
return <SimpleInput {...props} />;
}
}

export interface LaunchWorkflowFormInputsProps {
inputs: ParsedInput[];
showErrors?: boolean;
}

export const LaunchWorkflowFormInputsImpl: React.RefForwardingComponent<
LaunchWorkflowFormInputsRef,
LaunchWorkflowFormInputsProps
> = ({ inputs: parsedInputs, showErrors = true }, ref) => {
const { getValues, inputs, validate } = useFormInputsState(parsedInputs);
const styles = useStyles();
React.useImperativeHandle(ref, () => ({
getValues,
validate
}));

return (
<>
{inputs.map(input => (
<div key={input.label} className={styles.formControl}>
{getComponentForInput(input, showErrors)}
</div>
))}
</>
);
};

/** Renders an array of `ParsedInput` values using the appropriate
* components. A `ref` to this component is used to access the current
* form values and trigger manual validation if needed.
*/
export const LaunchWorkflowFormInputs = React.forwardRef(
LaunchWorkflowFormInputsImpl
);
5 changes: 4 additions & 1 deletion src/components/Launch/LaunchWorkflowForm/SimpleInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ function stringChangeHandler(onChange: InputChangeHandler) {
/** Handles rendering of the input component for any primitive-type input */
export const SimpleInput: React.FC<InputProps> = props => {
const {
error,
label,
helperText,
name,
onChange,
typeDefinition: { type },
value = ''
} = props;
const hasError = !!error;
const helperText = hasError ? error : props.helperText;
switch (type) {
case InputType.Boolean:
return (
Expand All @@ -57,6 +59,7 @@ export const SimpleInput: React.FC<InputProps> = props => {
case InputType.Duration:
return (
<TextField
error={hasError}
helperText={helperText}
fullWidth={true}
label={label}
Expand Down
Loading

0 comments on commit 363864f

Please sign in to comment.