Skip to content

Commit

Permalink
feat(core/presentation): Add a <Formik/> wrapper which applies fixes …
Browse files Browse the repository at this point in the history
…and opinions that we want in Spinnaker (#7272)

* fix(core/presentation): Remove all "isInitialValid" and use the SpinFormik code instead.
  • Loading branch information
christopherthielen authored Jul 25, 2019
1 parent 4adc3d4 commit 9c7885f
Show file tree
Hide file tree
Showing 19 changed files with 70 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as React from 'react';
import { Modal } from 'react-bootstrap';
import { Formik, Form, FormikErrors } from 'formik';
import { Form, FormikErrors } from 'formik';

import { TextInput, FormikFormField, ModalClose, ReactModal, SubmitButton, noop } from '@spinnaker/core';
import { TextInput, FormikFormField, ModalClose, ReactModal, SpinFormik, SubmitButton, noop } from '@spinnaker/core';

import { IAuthenticateOidcActionConfig } from 'amazon/loadBalancer/OidcConfigReader';

Expand Down Expand Up @@ -69,7 +69,7 @@ export class ConfigureOidcConfigModal extends React.Component<

return (
<div className="configure-config-modal">
<Formik<IAuthenticateOidcActionConfig>
<SpinFormik<IAuthenticateOidcActionConfig>
initialValues={initialValues}
onSubmit={this.submit}
validate={this.validate}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import { Modal } from 'react-bootstrap';
import { Formik, Form } from 'formik';
import { Form } from 'formik';
import { pickBy } from 'lodash';

import {
Expand All @@ -11,6 +11,7 @@ import {
SubmitButton,
noop,
SelectInput,
SpinFormik,
HelpField,
ReactSelectInput,
} from '@spinnaker/core';
Expand Down Expand Up @@ -66,7 +67,7 @@ export class ConfigureRedirectConfigModal extends React.Component<IConfigureRedi

return (
<div className="configure-config-modal">
<Formik<IRedirectActionConfig>
<SpinFormik<IRedirectActionConfig>
initialValues={this.initialValues}
onSubmit={this.submit}
render={({ isValid }) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
PlatformHealthOverride,
ReactInjector,
ReactModal,
SpinFormik,
TaskMonitor,
TaskReason,
ValidationMessage,
Expand Down Expand Up @@ -396,7 +397,7 @@ export class AmazonResizeServerGroupModal extends React.Component<
return (
<>
<TaskMonitorWrapper monitor={this.state.taskMonitor} />
<Formik<IAmazonResizeServerGroupValues>
<SpinFormik<IAmazonResizeServerGroupValues>
ref={this.formikRef}
initialValues={initialValues}
validate={this.validate}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
IPipeline,
IStageConfigProps,
NgReact,
SpinFormik,
StageConfigField,
StageConstants,
} from '@spinnaker/core';
Expand Down Expand Up @@ -99,7 +100,7 @@ export class CloudfoundryLoadBalancersStageConfig extends React.Component<
<StageConfigField label="Target">
<TargetSelect model={{ target }} options={StageConstants.TARGET_LIST} onChange={this.targetUpdated} />
</StageConfigField>
<Formik<ICloudFoundryLoadBalancersValues>
<SpinFormik<ICloudFoundryLoadBalancersValues>
ref={this.formikRef}
initialValues={initialValues}
onSubmit={null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
noop,
ReactInjector,
ReactModal,
SpinFormik,
TaskMonitor,
} from '@spinnaker/core';

Expand Down Expand Up @@ -101,7 +102,7 @@ export class CloudFoundryMapLoadBalancersModal extends React.Component<
return (
<>
<TaskMonitorWrapper monitor={this.state.taskMonitor} />
<Formik<ICloudFoundryLoadBalancerLinksModalValues>
<SpinFormik<ICloudFoundryLoadBalancerLinksModalValues>
ref={this.formikRef}
initialValues={initialValues}
onSubmit={this.submit}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
noop,
ReactInjector,
ReactModal,
SpinFormik,
TaskMonitor,
} from '@spinnaker/core';

Expand Down Expand Up @@ -101,7 +102,7 @@ export class CloudFoundryUnmapLoadBalancersModal extends React.Component<
return (
<>
<TaskMonitorWrapper monitor={this.state.taskMonitor} />
<Formik<ICloudFoundryLoadBalancerLinksModalValues>
<SpinFormik<ICloudFoundryLoadBalancerLinksModalValues>
ref={this.formikRef}
initialValues={initialValues}
onSubmit={this.submit}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
NumberInput,
ReactInjector,
ReactModal,
SpinFormik,
TaskMonitor,
TaskReason,
} from '@spinnaker/core';
Expand Down Expand Up @@ -193,7 +194,7 @@ export class CloudFoundryResizeServerGroupModal extends React.Component<
return (
<>
<TaskMonitorWrapper monitor={this.state.taskMonitor} />
<Formik<ICloudFoundryResizeServerGroupValues>
<SpinFormik<ICloudFoundryResizeServerGroupValues>
ref={this.formikRef}
initialValues={initialValues}
onSubmit={this.submit}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
ReactInjector,
ReactModal,
ReactSelectInput,
SpinFormik,
TaskMonitor,
TaskReason,
} from '@spinnaker/core';
Expand Down Expand Up @@ -110,7 +111,7 @@ export class CloudFoundryRollbackServerGroupModal extends React.Component<
return (
<>
<TaskMonitorWrapper monitor={this.state.taskMonitor} />
<Formik<ICloudFoundryRollbackServerGroupValues>
<SpinFormik<ICloudFoundryRollbackServerGroupValues>
ref={this.formikRef}
initialValues={initialValues}
onSubmit={this.submit}
Expand Down
14 changes: 11 additions & 3 deletions app/scripts/modules/core/src/entityTag/EntityTagEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import * as React from 'react';
import { Form, Formik } from 'formik';
import { Form } from 'formik';
import { Modal } from 'react-bootstrap';

import { Application } from 'core/application';
import { IEntityRef, IEntityTag } from 'core/domain';
import { HelpField } from 'core/help';
import { SubmitButton } from 'core/modal';
import { FormField, FormikFormField, Markdown, RadioButtonInput, ReactModal, TextAreaInput } from 'core/presentation';
import {
FormField,
FormikFormField,
Markdown,
RadioButtonInput,
ReactModal,
SpinFormik,
TextAreaInput,
} from 'core/presentation';
import { NgReact } from 'core/reactShims';
import { TaskMonitor } from 'core/task';
import { noop, UUIDGenerator } from 'core/utils';
Expand Down Expand Up @@ -144,7 +152,7 @@ export class EntityTagEditor extends React.Component<IEntityTagEditorProps, IEnt
<div>
<TaskMonitorWrapper monitor={this.state.taskMonitor} />

<Formik<IEntityTagEditorValues>
<SpinFormik<IEntityTagEditorValues>
initialValues={initialValues}
onSubmit={this.upsertTag}
render={({ isValid, values, setFieldValue }) => (
Expand Down
4 changes: 2 additions & 2 deletions app/scripts/modules/core/src/modal/wizard/WizardModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { without, merge } from 'lodash';
import { TaskMonitor } from 'core/task';
import { NgReact } from 'core/reactShims';
import { Spinner } from 'core/widgets';
import { SpinFormik } from 'core/presentation';

import { IModalComponentProps } from '../../presentation/ReactModal';
import { ModalClose } from '../buttons/ModalClose';
Expand Down Expand Up @@ -163,10 +164,9 @@ export class WizardModal<T = {}> extends React.Component<IWizardModalProps<T>, I
<>
{taskMonitor && <TaskMonitorWrapper monitor={taskMonitor} />}

<Formik<T>
<SpinFormik<T>
ref={this.formikRef}
initialValues={initialValues}
isInitialValid={() => !(Object.keys(this.validate(initialValues)).length > 0)}
onSubmit={closeModal}
validate={this.validate}
render={formik => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';

import { Formik, Form } from 'formik';
import { Modal } from 'react-bootstrap';
import { buildValidators, IModalComponentProps, ReactModal } from 'core/presentation';
import { buildValidators, IModalComponentProps, ReactModal, SpinFormik } from 'core/presentation';
import { INotification } from 'core/domain';
import { SubmitButton, ModalClose } from 'core/modal';

Expand Down Expand Up @@ -37,10 +37,9 @@ export class EditNotificationModal extends React.Component<IEditNotificationModa
public render(): React.ReactElement<EditNotificationModal> {
const { dismissModal, level, notification, stageType } = this.props;
return (
<Formik<INotification>
<SpinFormik<INotification>
ref={this.formikRef}
initialValues={notification}
isInitialValid={() => !(Object.keys(this.validate(notification)).length > 0)}
onSubmit={this.submit}
validate={this.validate}
render={formik => (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as React from 'react';
import { Formik, FormikProps, FormikErrors } from 'formik';
import { FormikProps, FormikErrors } from 'formik';

import { IStage, IPipeline, ITrigger } from 'core/domain';
import { Application } from 'core/application';
import { LayoutProvider, ResponsiveFieldLayout, WatchValue } from 'core/presentation';
import { LayoutProvider, ResponsiveFieldLayout, WatchValue, SpinFormik } from 'core/presentation';

export interface IFormikStageConfigInjectedProps {
application: Application;
Expand Down Expand Up @@ -36,7 +36,7 @@ export class FormikStageConfig extends React.Component<IFormikStageConfigProps>
public render() {
const { render, onChange, stage, validate, application, pipeline } = this.props;
return (
<Formik<IStage>
<SpinFormik<IStage>
validate={validate && decorate(validate, this.props)}
initialValues={stage}
onSubmit={() => {}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { assign, clone, compact, extend, get, head, uniq, isArray } from 'lodash
import { SubmitButton, ModalClose } from 'core/modal';
import { Application } from 'core/application';
import { AuthenticationService } from 'core/authentication';
import { buildValidators, IModalComponentProps, ReactModal } from 'core/presentation';
import { buildValidators, IModalComponentProps, ReactModal, SpinFormik } from 'core/presentation';
import {
IExecution,
IExecutionTrigger,
Expand Down Expand Up @@ -323,10 +323,9 @@ export class ManualExecutionModal extends React.Component<IManualExecutionModalP
const notifications = applicationNotifications.concat(pipelineNotifications);
const pipelineCommand = this.generateInitialValues(pipeline);
return (
<Formik<IPipelineCommand>
<SpinFormik<IPipelineCommand>
ref={this.formikRef}
initialValues={pipelineCommand}
isInitialValid={() => !(Object.keys(pipelineCommand).length > 0)}
onSubmit={this.submit}
validate={this.validate}
render={formik => (
Expand Down
20 changes: 20 additions & 0 deletions app/scripts/modules/core/src/presentation/forms/SpinFormik.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as React from 'react';
import { Formik, FormikConfig } from 'formik';

/**
* This component wraps the <Formik/> component, applying fixes and spinnaker opinions
* Use this component like you would use the <Formik/> component
*/
function SpinFormikImpl<Values extends {}>(props: FormikConfig<Values>, ref?: React.MutableRefObject<Formik<Values>>) {
const formikRef = ref || React.useRef<Formik<Values>>();
const defaultIsInitialValid = () => formikRef.current && Object.keys(formikRef.current.state.errors).length === 0;

// Run initial validation when the form is first rendered
React.useEffect(() => {
formikRef.current && formikRef.current.getFormikActions().validateForm();
}, [formikRef.current]);

return <Formik<Values> ref={formikRef} isInitialValid={props.isInitialValid || defaultIsInitialValid} {...props} />;
}

export const SpinFormik = (React.forwardRef(SpinFormikImpl) as any) as typeof Formik;
1 change: 1 addition & 0 deletions app/scripts/modules/core/src/presentation/forms/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './FormField';
export * from './FormikForm';
export * from './FormikFormField';
export * from './SpinFormik';
export * from './fields';
export * from './inputs';
export * from './interface';
Expand Down
6 changes: 3 additions & 3 deletions app/scripts/modules/core/src/task/modal/TaskMonitorModal.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as React from 'react';
import { Form, Formik, FormikProps } from 'formik';
import { Form, FormikProps } from 'formik';
import { Modal } from 'react-bootstrap';

import { IModalComponentProps, LayoutProvider, ResponsiveFieldLayout } from 'core/presentation';
import { IModalComponentProps, LayoutProvider, ResponsiveFieldLayout, SpinFormik } from 'core/presentation';
import { Application } from 'core/application';
import { NgReact } from 'core/reactShims';
import { SubmitButton } from 'core/modal';
Expand Down Expand Up @@ -80,7 +80,7 @@ export class TaskMonitorModal<T> extends React.Component<ITaskMonitorModalProps<
<div>
<TaskMonitorWrapper monitor={this.state.taskMonitor} />

<Formik<T>
<SpinFormik<T>
initialValues={this.props.initialValues}
onSubmit={this.submitTask}
render={formik => (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as React from 'react';
import { Formik, FormikProps } from 'formik';
import { FormikProps } from 'formik';
import { get } from 'lodash';

import { FormikFormField, ChecklistInput, NumberInput, HelpField, ReactSelectInput } from '@spinnaker/core';
import { FormikFormField, ChecklistInput, NumberInput, HelpField, ReactSelectInput, SpinFormik } from '@spinnaker/core';

import { ITitusServerGroupCommand } from '../../../serverGroupConfiguration.service';
import { IJobTimeWindow } from 'titus/domain';
Expand Down Expand Up @@ -131,7 +131,7 @@ export class WindowPicker extends React.Component<IWindowPickerProps, IWindowPic
);
}
return (
<Formik<IJobTimeWindowForm>
<SpinFormik<IJobTimeWindowForm>
initialValues={{
days: [],
startHour: 10,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as React from 'react';
import { Modal } from 'react-bootstrap';
import { Formik } from 'formik';

import {
Application,
Expand All @@ -9,6 +8,7 @@ import {
IModalComponentProps,
ModalClose,
SubmitButton,
SpinFormik,
NgReact,
ReactModal,
TaskExecutor,
Expand Down Expand Up @@ -62,9 +62,8 @@ export class EditDisruptionBudgetModal extends React.Component<IEditDisruptionBu
return (
<>
<TaskMonitorWrapper monitor={taskMonitor} />
<Formik<ITitusServerGroupCommand>
<SpinFormik<ITitusServerGroupCommand>
initialValues={command}
isInitialValid={true}
onSubmit={(values: ITitusServerGroupCommand) => this.submit(values, taskMonitor)}
render={formik => (
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import { Modal } from 'react-bootstrap';
import { Form, Formik, FormikContext } from 'formik';
import { Form, FormikContext } from 'formik';
import { ITitusServerGroup } from 'titus/domain';
import {
Application,
Expand All @@ -13,6 +13,7 @@ import {
NumberInput,
PlatformHealthOverride,
ReactInjector,
SpinFormik,
UserVerification,
ValidationMessage,
} from '@spinnaker/core';
Expand Down Expand Up @@ -246,7 +247,7 @@ export function TitusResizeServerGroupModal(props: ITitusResizeServerGroupModalP
<>
<TaskMonitorWrapper monitor={taskMonitor} />

<Formik<ITitusResizeServerGroupCommand>
<SpinFormik<ITitusResizeServerGroupCommand>
initialValues={initialValues}
validate={validateResizeCommand}
onSubmit={submit}
Expand Down

0 comments on commit 9c7885f

Please sign in to comment.