diff --git a/src/features/linodes/LinodesCreate/LinodeCreateContainer.tsx b/src/features/linodes/LinodesCreate/LinodeCreateContainer.tsx index 9fa47434959..36b80b482da 100644 --- a/src/features/linodes/LinodesCreate/LinodeCreateContainer.tsx +++ b/src/features/linodes/LinodesCreate/LinodeCreateContainer.tsx @@ -9,6 +9,7 @@ import { compose as recompose } from 'recompose'; import regionsContainer from 'src/containers/regions.container'; import withImages from 'src/containers/withImages.container'; import withLinodes from 'src/containers/withLinodes.container'; +import { CreateTypes } from 'src/store/linodeCreate/linodeCreate.actions'; import { LinodeActionsProps, withLinodeActions @@ -20,6 +21,9 @@ import Grid from 'src/components/Grid'; import { Tag } from 'src/components/TagsInput'; import { dcDisplayNames } from 'src/constants'; +import withLabelGenerator, { + LabelProps +} from 'src/features/linodes/LinodesCreate/withLabelGenerator'; import { typeLabelDetails } from 'src/features/linodes/presentation'; import userSSHKeyHoc, { State as userSSHKeyProps @@ -72,11 +76,13 @@ interface State { } type CombinedProps = InjectedNotistackProps & + CreateType & ReduxStateProps & LinodeActionsProps & WithLinodesImagesTypesAndRegions & userSSHKeyProps & DispatchProps & + LabelProps & RouteComponentProps<{}>; const defaultState: State = { @@ -97,6 +103,20 @@ const defaultState: State = { class LinodeCreateContainer extends React.PureComponent { state: State = defaultState; + componentDidUpdate(prevProps: CombinedProps) { + /** + * if we're clicking on the stackscript create flow, we need to stop + * defaulting to Debian 9 because it's possible the user chooses a stackscript + * that isn't compatible with the defaulted image + */ + if ( + prevProps.createType !== 'fromStackScript' && + this.props.createType === 'fromStackScript' + ) { + this.setState({ selectedImageID: undefined }); + } + } + clearCreationState = () => { this.props.resetSSHKeys(); this.setState(defaultState); @@ -143,17 +163,13 @@ class LinodeCreateContainer extends React.PureComponent { selectedStackScriptUsername: username, availableUserDefinedFields: userDefinedFields, availableStackScriptImages: images, - udfs: defaultData + udfs: defaultData, + /** reset image because stackscript might not be compatible with selected one */ + selectedImageID: undefined }); setDiskSize = (size: number) => this.setState({ selectedDiskSize: size }); - setLabel = ( - event: React.ChangeEvent< - HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement - > - ) => this.setState({ label: event.target.value }); - setPassword = (password: string) => this.setState({ password }); toggleBackupsEnabled = () => @@ -166,13 +182,50 @@ class LinodeCreateContainer extends React.PureComponent { setUDFs = (udfs: any[]) => this.setState({ udfs }); + generateLabel = () => { + const { getLabel, imagesData, regionsData } = this.props; + const { selectedImageID, selectedRegionID } = this.state; + + /* tslint:disable-next-line */ + let arg1, + arg2, + arg3 = ''; + + if (selectedImageID) { + const selectedImage = imagesData.find(img => img.id === selectedImageID); + /** + * Use 'vendor' if it's a public image, otherwise use label (because 'vendor' will be null) + * + * If we have no selectedImage, just use an empty string + */ + arg1 = selectedImage + ? selectedImage.is_public + ? selectedImage.vendor + : selectedImage.label + : ''; + } + + if (selectedRegionID) { + const selectedRegion = regionsData.find( + region => region.id === selectedRegionID + ); + + arg2 = selectedRegion ? selectedRegion.id : ''; + } + + arg3 = this.props.createType; + + return getLabel(arg1, arg2, arg3); + }; + submitForm: HandleSubmit = (type, payload, linodeID?: number) => { + const { createType } = this.props; /** * run a certain linode action based on the type * if clone, run clone service request and upsert linode * if create, run create action */ - if (type === 'clone' && !linodeID) { + if (createType === 'fromLinode' && !linodeID) { return this.setState( () => ({ errors: [ @@ -186,7 +239,7 @@ class LinodeCreateContainer extends React.PureComponent { ); } - if (type === 'createFromStackScript' && !this.state.selectedStackScriptID) { + if (createType === 'fromStackScript' && !this.state.selectedStackScriptID) { return this.setState( () => ({ errors: [ @@ -201,7 +254,7 @@ class LinodeCreateContainer extends React.PureComponent { } const request = - type === 'clone' + createType === 'fromLinode' ? () => cloneLinode(linodeID!, payload) : () => this.props.linodeActions.createLinode(payload); @@ -212,7 +265,7 @@ class LinodeCreateContainer extends React.PureComponent { this.setState({ formIsSubmitting: false }); /** if cloning a Linode, upsert Linode in redux */ - if (type === 'clone') { + if (createType === 'fromLinode') { this.props.upsertLinode(response); } @@ -227,7 +280,9 @@ class LinodeCreateContainer extends React.PureComponent { /** * allocate private IP if we have one * - * @todo we need to update redux state here as well + * @todo we need to update redux state here as well but it's not + * crucial now because the networking tab already makes a request to + * /ips on componentDidMount */ if (payload.private_ip) { allocatePrivateIP(response.id); @@ -359,8 +414,8 @@ class LinodeCreateContainer extends React.PureComponent { this.state.selectedStackScriptUsername } updateStackScript={this.setStackScript} - label={this.state.label} - updateLabel={this.setLabel} + label={this.generateLabel()} + updateLabel={this.props.updateCustomLabel} password={this.state.password} updatePassword={this.setPassword} backupsEnabled={this.state.backupsEnabled} @@ -384,7 +439,14 @@ class LinodeCreateContainer extends React.PureComponent { } } -const mapStateToProps: MapState = state => ({ +interface CreateType { + createType: CreateTypes; +} + +const mapStateToProps: MapState< + ReduxStateProps & CreateType, + CombinedProps +> = state => ({ accountBackupsEnabled: pathOr( false, ['__resources', 'accountSettings', 'data', 'backups_enabled'], @@ -395,7 +457,8 @@ const mapStateToProps: MapState = state => ({ * and do not have the "add_linodes" grant */ userCannotCreateLinode: - isRestrictedUser(state) && !hasGrant(state, 'add_linodes') + isRestrictedUser(state) && !hasGrant(state, 'add_linodes'), + createType: state.createLinode.type }); interface DispatchProps { @@ -461,5 +524,6 @@ export default recompose( connected, withRouter, withSnackbar, - userSSHKeyHoc + userSSHKeyHoc, + withLabelGenerator )(LinodeCreateContainer); diff --git a/src/features/linodes/LinodesCreate/withLabelGenerator.tsx b/src/features/linodes/LinodesCreate/withLabelGenerator.tsx index f9beb4993a7..2f639b186c5 100644 --- a/src/features/linodes/LinodesCreate/withLabelGenerator.tsx +++ b/src/features/linodes/LinodesCreate/withLabelGenerator.tsx @@ -5,7 +5,11 @@ import { deriveDefaultLabel, LabelArgTypes } from './deriveDefaultLabel'; export interface LabelProps { customLabel: string; - updateCustomLabel: (e: any) => void; + updateCustomLabel: ( + e: React.ChangeEvent< + HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement + > + ) => void; getLabel: (...args: any[]) => string; } diff --git a/src/store/linodeCreate/linodeCreate.reducer.ts b/src/store/linodeCreate/linodeCreate.reducer.ts index ebc2ab45e8c..a7bb1990a55 100644 --- a/src/store/linodeCreate/linodeCreate.reducer.ts +++ b/src/store/linodeCreate/linodeCreate.reducer.ts @@ -54,8 +54,6 @@ const reducer: Reducer = reducerWithInitialState( ).caseWithAction(handleChangeCreateType, (state, action) => { const { payload } = action; - console.log(payload); - return { ...state, type: payload