Skip to content

Commit

Permalink
refactor(core/account): Refactor AccountSelectInput to use 'value' prop
Browse files Browse the repository at this point in the history
  • Loading branch information
christopherthielen committed Dec 7, 2018
1 parent 6f7f543 commit 307da1b
Show file tree
Hide file tree
Showing 16 changed files with 85 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -343,11 +343,10 @@ class LoadBalancerLocationImpl extends React.Component<ILoadBalancerLocationProp
<div className="col-md-3 sm-label-right">Account</div>
<div className="col-md-7">
<AccountSelectInput
component={values}
field="credentials"
value={values.credentials}
onChange={evt => this.accountUpdated(evt.target.value)}
accounts={accounts}
provider="aws"
onChange={this.accountUpdated}
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,12 +317,11 @@ class ServerGroupBasicSettingsImpl extends React.Component<
<div className="col-md-3 sm-label-right">Account</div>
<div className="col-md-7">
<AccountSelectInput
value={values.credentials}
onChange={evt => this.accountUpdated(evt.target.value)}
readOnly={readOnlyFields.credentials}
component={values}
field="credentials"
accounts={accounts}
provider="aws"
onChange={this.accountUpdated}
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,10 @@ class LoadBalancerDetailsImpl extends React.Component<ILoadBalancerDetailsProps,
<div className="col-md-3 sm-label-right">Account</div>
<div className="col-md-7">
<AccountSelectInput
component={values}
field="credentials"
value={values.credentials}
onChange={evt => this.accountUpdated(evt.target.value)}
accounts={accounts}
provider="cloudfoundry"
onChange={this.accountUpdated}
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,11 +424,10 @@ export class CloudfoundryDeployServiceStageConfig extends React.Component<
<div className="form-horizontal cloudfoundry-deploy-service-stage">
<StageConfigField label="Account">
<AccountSelectInput
value={stage.credentials}
onChange={evt => this.accountUpdated(evt.target.value)}
accounts={accounts}
component={stage}
field="credentials"
provider="cloudfoundry"
onChange={this.accountUpdated}
/>
</StageConfigField>
<RegionSelectField
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,10 @@ export class CloudfoundryDestroyServiceStageConfig extends React.Component<
<div className="form-horizontal">
<StageConfigField label="Account">
<AccountSelectInput
value={stage.credentials}
onChange={evt => this.accountUpdated(evt.target.value)}
accounts={accounts}
component={stage}
field="credentials"
provider="cloudfoundry"
onChange={this.accountUpdated}
/>
</StageConfigField>
<RegionSelectField
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,11 +239,10 @@ class ArtifactSettingsImpl extends React.Component<
<div className="col-md-3 sm-label-right">Account</div>
<div className="col-md-7">
<AccountSelectInput
component={artifact}
field="credentials"
value={(artifact as any).credentials}
onChange={evt => this.packageAccountUpdated(evt.target.value)}
accounts={allCloudFoundryCredentials}
provider="cloudfoundry"
onChange={this.packageAccountUpdated}
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,10 @@ class BasicSettingsImpl extends React.Component<
<div className="col-md-3 sm-label-right">Account</div>
<div className="col-md-7">
<AccountSelectInput
component={values}
field="credentials"
value={values.credentials}
onChange={evt => this.accountUpdated(evt.target.value)}
accounts={accounts}
provider="cloudfoundry"
onChange={this.accountUpdated}
/>
</div>
</div>
Expand Down
89 changes: 42 additions & 47 deletions app/scripts/modules/core/src/account/AccountSelectInput.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
import * as React from 'react';
import { $q } from 'ngimport';
import { flatten, has, isEqual, map, uniq, xor } from 'lodash';
import { flatten, isEqual, map, uniq, xor } from 'lodash';

import { IAccount } from 'core/account';
import { AccountService } from 'core/account/AccountService';
import { createFakeReactSyntheticEvent } from 'core/presentation/forms/inputs/utils';
import { IFormInputProps } from 'core/presentation';

export interface IAccountSelectInputProps {
import { AccountService, IAccount } from './AccountService';

export interface IAccountSelectInputProps extends IFormInputProps {
accounts: IAccount[] | string[];
component: { [key: string]: any };
field: string;
provider: string;
loading?: boolean;
onChange?: (account: string) => void;
labelColumns?: number;
readOnly?: boolean;
}

export interface IAccountSelectInputState {
accountContainsExpression: boolean;
mergedAccounts: string[];
primaryAccounts: string[];
secondaryAccounts: string[];
Expand All @@ -27,20 +24,19 @@ const isExpression = (account: string) => !!account && account.includes('${');

export class AccountSelectInput extends React.Component<IAccountSelectInputProps, IAccountSelectInputState> {
public state: IAccountSelectInputState = {
accountContainsExpression: false,
mergedAccounts: [],
primaryAccounts: [],
secondaryAccounts: [],
};

private groupAccounts = (accounts: IAccount[] | string[]) => {
const { component, field, onChange, provider } = this.props;
const { name, value, onChange, provider } = this.props;

if (!accounts || !accounts.length) {
return;
}
if (has(component, field) && isExpression(component[field])) {
this.setState({ accountContainsExpression: true });

if (isExpression(value)) {
return;
}

Expand Down Expand Up @@ -76,10 +72,8 @@ export class AccountSelectInput extends React.Component<IAccountSelectInputProps
mergedAccounts = flatten([primaryAccounts, secondaryAccounts]);
}

if (component) {
if (!mergedAccounts.includes(component[field])) {
onChange('');
}
if (!mergedAccounts.includes(value)) {
onChange(createFakeReactSyntheticEvent({ value: '', name }));
}

this.setState({ mergedAccounts, primaryAccounts, secondaryAccounts });
Expand All @@ -97,12 +91,10 @@ export class AccountSelectInput extends React.Component<IAccountSelectInputProps
}

public render() {
const { component, field, onChange, readOnly } = this.props;
const { accountContainsExpression, primaryAccounts, secondaryAccounts } = this.state;

const value = component[field];
const { value, onChange, readOnly } = this.props;
const { primaryAccounts, secondaryAccounts } = this.state;

if (accountContainsExpression) {
if (isExpression(value)) {
return (
<div className="sm-control-field">
<span>
Expand All @@ -112,34 +104,37 @@ export class AccountSelectInput extends React.Component<IAccountSelectInputProps
);
}

if (readOnly) {
return (
<div>
<p className="form-control-static">{value}</p>
</div>
);
}

const showSeparator = primaryAccounts.length > 0 && secondaryAccounts.length > 0;

return (
<div>
{!readOnly && (
<select
className="form-control input-sm"
value={value}
onChange={e => onChange(e.target.value)}
required={true}
>
<option value="" disabled={true}>
Select...
<select className="form-control input-sm" value={value} onChange={onChange} required={true}>
<option value="" disabled={true}>
Select...
</option>

{primaryAccounts.map(account => (
<option key={account} value={account}>
{account}
</option>
))}

{showSeparator && <option disabled={true}>---------------</option>}

{secondaryAccounts.map(account => (
<option key={account} value={account}>
{account}
</option>
{primaryAccounts.map(account => (
<option key={account} value={account}>
{account}
</option>
))}
{primaryAccounts.length > 0 &&
secondaryAccounts.length > 0 && <option disabled={true}>---------------</option>}
{secondaryAccounts.map(account => (
<option key={account} value={account}>
{account}
</option>
))}
</select>
)}

{readOnly && <p className="form-control-static">{value}</p>}
))}
</select>
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Select, { Option, ReactSelectProps } from 'react-select';
import { OmitControlledInputPropsFrom, StringsAsOptions, TetheredSelect } from 'core/presentation';
import { noop } from 'core/utils';

import { isStringArray, orEmptyString } from './utils';
import { createFakeReactSyntheticEvent, isStringArray, orEmptyString } from './utils';
import { IFormInputProps } from '../interface';

interface IReactSelectInputProps extends IFormInputProps, OmitControlledInputPropsFrom<ReactSelectProps> {
Expand All @@ -20,13 +20,6 @@ export const reactSelectValidationErrorStyle = {
boxShadow: 'inset 0 1px 1px rgba(0, 0, 0, 0.075)',
};

export const createFakeReactSyntheticEvent = (target: { name: string; value?: any }) => ({
persist: noop,
stopPropagation: noop,
preventDefault: noop,
target,
});

/**
* Given a IControlledInputProps "field" (i.e., from Formik), returns an onChange handler
* somewhat compatible with the controlled input pattern
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './StringsAsOptions';
export * from './TextAreaInput';
export * from './TextInput';
export * from './expression';
export * from './utils';
10 changes: 10 additions & 0 deletions app/scripts/modules/core/src/presentation/forms/inputs/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as classNames from 'classnames';
import { isUndefined, isString } from 'lodash';

import { noop } from 'core/utils';

import { IValidationProps } from '../interface';

export const orEmptyString = (val: any) => (isUndefined(val) ? '' : val);
Expand All @@ -14,4 +16,12 @@ export const validationClassName = (validation: IValidationProps) => {
});
};

export const createFakeReactSyntheticEvent = (target: { name?: string; value?: any }) =>
({
persist: noop,
stopPropagation: noop,
preventDefault: noop,
target,
} as React.ChangeEvent<any>);

export const isStringArray = (opts: any[]): opts is string[] => opts && opts.length && opts.every(isString);
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ import { Creatable, Option } from 'react-select';
import { $q } from 'ngimport';
import Spy = jasmine.Spy;

import { noop, ScopeClusterSelector, AccountService, AccountSelectInput } from '@spinnaker/core';
import {
AccountSelectInput,
AccountService,
ScopeClusterSelector,
createFakeReactSyntheticEvent,
noop,
} from '@spinnaker/core';

import { ManifestKindSearchService } from 'kubernetes/v2/manifest/ManifestKindSearch';
import { ManifestSelector } from 'kubernetes/v2/manifest/selector/ManifestSelector';
Expand Down Expand Up @@ -271,7 +277,7 @@ describe('<ManifestSelector />', () => {
});

const account = wrapper.find(AccountSelectInput).first();
account.props().onChange('my-other-account');
account.props().onChange(createFakeReactSyntheticEvent({ value: 'my-other-account' }));
expect(searchService).toHaveBeenCalledWith('configMap', 'default', 'my-other-account');
});

Expand All @@ -289,7 +295,7 @@ describe('<ManifestSelector />', () => {
});

const account = wrapper.find(AccountSelectInput).first();
account.props().onChange('my-other-account');
account.props().onChange(createFakeReactSyntheticEvent({ value: 'my-other-account' }));
expect(wrapper.instance().state.selector.location).toBeFalsy();
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,10 +274,9 @@ export class ManifestSelector extends React.Component<IManifestSelectorProps, IM
<>
<StageConfigField label="Account">
<AccountSelectInput
component={selector}
field="account"
value={selector.account}
onChange={evt => this.handleAccountChange(evt.target.value)}
accounts={accounts}
onChange={this.handleAccountChange}
provider="'kubernetes'"
/>
</StageConfigField>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,11 @@ class ManifestBasicSettingsImpl extends React.Component<IManifestBasicSettingsPr
</div>
<div className="col-md-7">
<AccountSelectInput
value={formik.values.command.account}
onChange={evt => this.accountUpdated(evt.target.value)}
readOnly={false}
component={formik.values.command}
field="account"
accounts={accounts}
provider="kubernetes"
onChange={this.accountUpdated}
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,9 @@ export class TitusRunJobStageConfig extends React.Component<IStageConfigProps, I
</label>
<div className="col-md-5">
<AccountSelectInput
component={stage}
field="credentials"
value={stage.credentials}
onChange={evt => this.accountChanged(evt.target.value)}
accounts={credentials}
onChange={this.accountChanged}
provider="titus"
/>
{stage.credentials !== undefined && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,11 @@ class ServerGroupBasicSettingsImpl extends React.Component<
<div className="col-md-3 sm-label-right">Account</div>
<div className="col-md-7">
<AccountSelectInput
value={values.credentials}
onChange={evt => this.accountUpdated(evt.target.value)}
readOnly={readOnlyFields.credentials}
component={values}
field="credentials"
accounts={accounts}
provider="titus"
onChange={this.accountUpdated}
/>
{values.credentials !== undefined && (
<div className="small">
Expand Down

0 comments on commit 307da1b

Please sign in to comment.