Skip to content

Commit

Permalink
Merge pull request #30 from lyft/more-input-types
Browse files Browse the repository at this point in the history
Add support for Collections in the launch UI
  • Loading branch information
schottra authored Dec 18, 2019
2 parents 5899794 + fafaa8d commit ad6a545
Show file tree
Hide file tree
Showing 10 changed files with 486 additions and 83 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"@types/js-yaml": "^3.10.1",
"@types/lodash": "^4.14.68",
"@types/long": "^3.0.32",
"@types/lossless-json": "^1.0.0",
"@types/memoize-one": "^4.1.0",
"@types/memory-fs": "^0.3.0",
"@types/node": "^9.6.6",
Expand Down Expand Up @@ -144,6 +145,7 @@
"intersection-observer": "^0.7.0",
"jest": "^24.9.0",
"lint-staged": "^7.0.4",
"lossless-json": "^1.0.3",
"memoize-one": "^5.0.0",
"moment": "^2.18.1",
"object-hash": "^1.3.1",
Expand Down
55 changes: 55 additions & 0 deletions src/components/Launch/LaunchWorkflowForm/CollectionInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { TextField } from '@material-ui/core';
import * as React from 'react';
import { InputChangeHandler, InputProps, InputType } from './types';
import { UnsupportedInput } from './UnsupportedInput';

function stringChangeHandler(onChange: InputChangeHandler) {
return ({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {
onChange(value);
};
}

/** Handles rendering of the input component for a Collection of SimpleType values*/
export const CollectionInput: React.FC<InputProps> = props => {
const {
label,
helperText,
onChange,
typeDefinition: { subtype },
value = ''
} = props;
if (!subtype) {
console.error(
'Unexpected missing subtype for collection input',
props.typeDefinition
);
return <UnsupportedInput {...props} />;
}
switch (subtype.type) {
case InputType.Blob:
case InputType.Boolean:
case InputType.Collection:
case InputType.Datetime:
case InputType.Duration:
case InputType.Error:
case InputType.Float:
case InputType.Integer:
case InputType.Map:
case InputType.String:
case InputType.Struct:
return (
<TextField
helperText={helperText}
fullWidth={true}
label={label}
multiline={true}
onChange={stringChangeHandler(onChange)}
rowsMax={8}
value={value}
variant="outlined"
/>
);
default:
return <UnsupportedInput {...props} />;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
workflowSortFields
} from 'models';
import * as React from 'react';
import { CollectionInput } from './CollectionInput';
import { SearchableSelector } from './SearchableSelector';
import { SimpleInput } from './SimpleInput';
import { InputProps, InputType, LaunchWorkflowFormProps } from './types';
Expand Down Expand Up @@ -54,6 +55,7 @@ const useStyles = makeStyles((theme: Theme) => ({
function getComponentForInput(input: InputProps) {
switch (input.typeDefinition.type) {
case InputType.Collection:
return <CollectionInput {...input} />;
case InputType.Map:
case InputType.Schema:
case InputType.Unknown:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,55 +1,88 @@
import { action } from '@storybook/addon-actions';
import { storiesOf } from '@storybook/react';
import { resolveAfter } from 'common/promiseUtils';
import { mockAPIContextValue } from 'components/data/__mocks__/apiContext';
import { APIContext } from 'components/data/apiContext';
import { Workflow } from 'models';
import { Admin } from 'flyteidl';
import { mapValues } from 'lodash';
import { Variable, Workflow } from 'models';
import { createMockLaunchPlan } from 'models/__mocks__/launchPlanData';
import {
createMockWorkflow,
createMockWorkflowClosure,
createMockWorkflowVersions
} from 'models/__mocks__/workflowData';
import { mockExecution } from 'models/Execution/__mocks__/mockWorkflowExecutionsData';
import * as React from 'react';
import { LaunchWorkflowForm } from '../LaunchWorkflowForm';
import { mockParameterMap, mockWorkflowInputsInterface } from './mockInputs';
import {
createMockWorkflowInputsInterface,
mockCollectionVariables,
mockNestedCollectionVariables,
mockSimpleVariables
} from './mockInputs';

const mockWorkflow = createMockWorkflow('MyWorkflow');
const mockLaunchPlan = createMockLaunchPlan(
mockWorkflow.id.name,
mockWorkflow.id.version
);
const submitAction = action('createWorkflowExecution');

const mockWorkflowVersions = createMockWorkflowVersions(
mockWorkflow.id.name,
10
);
const renderForm = (variables: Record<string, Variable>) => {
const mockWorkflow = createMockWorkflow('MyWorkflow');
const mockLaunchPlan = createMockLaunchPlan(
mockWorkflow.id.name,
mockWorkflow.id.version
);

const mockWorkflowVersions = createMockWorkflowVersions(
mockWorkflow.id.name,
10
);

mockLaunchPlan.closure!.expectedInputs = mockParameterMap;
const parameterMap = {
parameters: mapValues(variables, v => ({ var: v }))
};

const mockApi = mockAPIContextValue({
getLaunchPlan: () => resolveAfter(500, mockLaunchPlan),
getWorkflow: id => {
const workflow: Workflow = {
id
};
workflow.closure = createMockWorkflowClosure();
workflow.closure!.compiledWorkflow!.primary.template.interface = mockWorkflowInputsInterface;
mockLaunchPlan.closure!.expectedInputs = parameterMap;

return resolveAfter(500, workflow);
},
listWorkflows: () => resolveAfter(500, { entities: mockWorkflowVersions }),
listLaunchPlans: () => resolveAfter(500, { entities: [mockLaunchPlan] })
});
const mockApi = mockAPIContextValue({
createWorkflowExecution: input => {
console.log(input);
submitAction('See console for data');
return Promise.reject('Not implemented');
},
getLaunchPlan: () => resolveAfter(500, mockLaunchPlan),
getWorkflow: id => {
const workflow: Workflow = {
id
};
workflow.closure = createMockWorkflowClosure();
workflow.closure!.compiledWorkflow!.primary.template.interface = createMockWorkflowInputsInterface(
variables
);

const onClose = () => console.log('Close');
return resolveAfter(500, workflow);
},
listWorkflows: () =>
resolveAfter(500, { entities: mockWorkflowVersions }),
listLaunchPlans: () => resolveAfter(500, { entities: [mockLaunchPlan] })
});

const onClose = () => console.log('Close');

return (
<APIContext.Provider value={mockApi}>
<div style={{ width: 600, height: '95vh' }}>
<LaunchWorkflowForm
onClose={onClose}
workflowId={mockWorkflow.id}
/>
</div>
</APIContext.Provider>
);
};

const stories = storiesOf('Launch/LaunchWorkflowForm', module);
stories.addDecorator(story => (
<APIContext.Provider value={mockApi}>
<div style={{ width: 600, height: '95vh' }}>{story()}</div>
</APIContext.Provider>
));

stories.add('Basic', () => (
<LaunchWorkflowForm onClose={onClose} workflowId={mockWorkflow.id} />
));

stories.add('Simple', () => renderForm(mockSimpleVariables));
stories.add('Collections', () => renderForm(mockCollectionVariables));
stories.add('Nested Collections', () =>
renderForm(mockNestedCollectionVariables)
);
34 changes: 25 additions & 9 deletions src/components/Launch/LaunchWorkflowForm/__stories__/mockInputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function simpleType(primitiveType: SimpleType, description?: string): Variable {
};
}

export const mockVariables: Record<string, Variable> = {
export const mockSimpleVariables: Record<string, Variable> = {
simpleString: simpleType(SimpleType.STRING, 'a simple string value'),
stringNoLabel: simpleType(SimpleType.STRING),
simpleInteger: simpleType(SimpleType.INTEGER, 'a simple integer value'),
Expand All @@ -32,12 +32,28 @@ export const mockVariables: Record<string, Variable> = {
// blob: {}
};

export const mockWorkflowInputsInterface: TypedInterface = {
inputs: {
variables: { ...mockVariables }
}
};
export const mockCollectionVariables: Record<string, Variable> = mapValues(
mockSimpleVariables,
v => ({
description: `A collection of: ${v.description}`,
type: { collectionType: v.type }
})
);

export const mockParameterMap: ParameterMap = {
parameters: mapValues(mockVariables, v => ({ var: v }))
};
export const mockNestedCollectionVariables: Record<
string,
Variable
> = mapValues(mockCollectionVariables, v => ({
description: `${v.description} (nested)`,
type: { collectionType: v.type }
}));

export function createMockWorkflowInputsInterface(
variables: Record<string, Variable>
): TypedInterface {
return {
inputs: {
variables: { ...variables }
}
};
}
Loading

0 comments on commit ad6a545

Please sign in to comment.