diff --git a/src/js/components/containerImages/ContainerImagePrepareParameterForm.js b/src/js/components/containerImages/ContainerImagePrepareParameterForm.js new file mode 100644 index 00000000..718252dd --- /dev/null +++ b/src/js/components/containerImages/ContainerImagePrepareParameterForm.js @@ -0,0 +1,111 @@ +/** + * Copyright 2018 Red Hat Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { injectIntl, defineMessages } from 'react-intl'; +import { reduxForm, Field, SubmissionError } from 'redux-form'; +import { Form, Wizard } from 'patternfly-react'; +import yaml from 'js-yaml'; + +import { OverlayLoader } from '../ui/Loader'; +import ModalFormErrorList from '../ui/forms/ModalFormErrorList'; +import AceEditorInput from '../ui/reduxForm/AceEditorInput'; +import { updateParameters } from '../../actions/ParametersActions'; + +const messages = defineMessages({ + updatingConfiguration: { + id: 'ContainerImagePrepareParameterForm.updatingConfiguration', + defaultMessage: 'Updating configuration...' + }, + yamlSyntaxError: { + id: 'ContainerImagePrepareParameterForm.yamlSyntaxError', + defaultMessage: 'Invalid Yaml Syntax:' + } +}); + +const ContainerImagePrepareParameterForm = ({ + currentPlanName, + error, + handleSubmit, + intl: { formatMessage }, + parameter, + submitting +}) => ( +
+ + + + + + + + +
+); +ContainerImagePrepareParameterForm.propTypes = { + currentPlanName: PropTypes.string.isRequired, + error: PropTypes.object, + handleSubmit: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + parameter: ImmutablePropTypes.record.isRequired, + submitting: PropTypes.bool.isRequired +}; + +const form = reduxForm({ + form: 'parametersForm', + onSubmit: ( + { ContainerImagePrepare }, + dispatch, + { currentPlanName, intl: { formatMessage } } + ) => { + try { + ContainerImagePrepare = yaml.safeLoad(ContainerImagePrepare, { + json: true + }); + dispatch(updateParameters(currentPlanName, { ContainerImagePrepare })); + } catch (e) { + return Promise.reject( + new SubmissionError({ + _error: { + title: formatMessage(messages.yamlSyntaxError), + message: e.message + } + }) + ); + } + }, + validate: ({ ContainerImagePrepare }) => { + try { + yaml.safeLoad(ContainerImagePrepare, { json: true }); + return {}; + } catch (e) { + return { ContainerImagePrepare: e.message }; + } + } +}); + +export default injectIntl(form(ContainerImagePrepareParameterForm)); diff --git a/src/js/components/containerImages/ContainerImagePrepareParameterFormActions.js b/src/js/components/containerImages/ContainerImagePrepareParameterFormActions.js new file mode 100644 index 00000000..d17d6aab --- /dev/null +++ b/src/js/components/containerImages/ContainerImagePrepareParameterFormActions.js @@ -0,0 +1,88 @@ +/** + * Copyright 2018 Red Hat Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +import React, { Fragment } from 'react'; +import { connect } from 'react-redux'; +import { defineMessages, FormattedMessage, injectIntl } from 'react-intl'; +import PropTypes from 'prop-types'; +import { Icon, Button } from 'patternfly-react'; +import { submit, isSubmitting, isPristine, isInvalid } from 'redux-form'; + +import { CloseModalButton } from '../ui/Modals'; + +const messages = defineMessages({ + back: { + id: 'ContainerImagePrepareParameterFormActions.back', + defaultMessage: 'Back' + }, + save: { + id: 'ContainerImagePrepareParameterFormActions.save', + defaultMessage: 'Save Changes' + }, + close: { + id: 'ContainerImagePrepareParameterFormActions.close', + defaultMessage: 'Close' + } +}); + +const ContainerImagePrepareParameterFormActions = ({ + goBack, + isSubmitting, + isInvalid, + isPristine, + submitForm +}) => ( + + + + + + + +); +ContainerImagePrepareParameterFormActions.propTypes = { + goBack: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + isInvalid: PropTypes.bool.isRequired, + isPristine: PropTypes.bool.isRequired, + isSubmitting: PropTypes.bool.isRequired, + submitForm: PropTypes.func.isRequired +}; + +const mapStateToProps = state => ({ + isSubmitting: isSubmitting('parametersForm')(state), + isPristine: isPristine('parametersForm')(state), + isInvalid: isInvalid('parametersForm')(state) +}); + +const mapDispatchToProps = dispatch => ({ + submitForm: () => dispatch(submit('parametersForm')) +}); + +export default injectIntl( + connect(mapStateToProps, mapDispatchToProps)( + ContainerImagePrepareParameterFormActions + ) +); diff --git a/src/js/components/containerImages/ContainerImagesPrepareFormActions.js b/src/js/components/containerImages/ContainerImagesPrepareFormActions.js index ab25d7da..8a8ecbd1 100644 --- a/src/js/components/containerImages/ContainerImagesPrepareFormActions.js +++ b/src/js/components/containerImages/ContainerImagesPrepareFormActions.js @@ -21,11 +21,20 @@ import PropTypes from 'prop-types'; import { Icon, Button } from 'patternfly-react'; import { submit, isSubmitting, isPristine, isInvalid } from 'redux-form'; +import { CloseModalButton } from '../ui/Modals'; import { startContainerImagesPrepare } from '../../actions/ContainerImagesActions'; const messages = defineMessages({ + cancel: { + id: 'ContainerImagesWizard.cancel', + defaultMessage: 'Cancel' + }, + save: { + id: 'ContainerImagesPrepareFormActions.save', + defaultMessage: 'Save Changes' + }, next: { - id: 'ContainerImagesWizard.next', + id: 'ContainerImagesPrepareFormActions.next', defaultMessage: 'Next' }, reset: { @@ -35,38 +44,44 @@ const messages = defineMessages({ }); const ContainerImagesPrepareFormActions = ({ + goForward, isSubmitting, isInvalid, isPristine, resetToDefaults, - submitImagesPrepareForm + submitForm }) => ( + + + + ); ContainerImagesPrepareFormActions.propTypes = { + goForward: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, - isFetchingParameters: PropTypes.bool.isRequired, isInvalid: PropTypes.bool.isRequired, isPristine: PropTypes.bool.isRequired, isSubmitting: PropTypes.bool.isRequired, resetToDefaults: PropTypes.func.isRequired, - submitImagesPrepareForm: PropTypes.func.isRequired + submitForm: PropTypes.func.isRequired }; const mapStateToProps = state => ({ - isFetchingParameters: state.parameters.isFetching, isSubmitting: isSubmitting('containerImagesPrepareForm')(state), isPristine: isPristine('containerImagesPrepareForm')(state), isInvalid: isInvalid('containerImagesPrepareForm')(state) @@ -74,7 +89,7 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ({ resetToDefaults: () => dispatch(startContainerImagesPrepare({})), - submitImagesPrepareForm: () => dispatch(submit('containerImagesPrepareForm')) + submitForm: () => dispatch(submit('containerImagesPrepareForm')) }); export default injectIntl( diff --git a/src/js/components/containerImages/ContainerImagesWizard.js b/src/js/components/containerImages/ContainerImagesWizard.js index efcab469..ac98fa38 100644 --- a/src/js/components/containerImages/ContainerImagesWizard.js +++ b/src/js/components/containerImages/ContainerImagesWizard.js @@ -14,43 +14,30 @@ * under the License. */ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import { connect } from 'react-redux'; import { defineMessages, FormattedMessage, injectIntl } from 'react-intl'; import PropTypes from 'prop-types'; -import { Wizard, Modal, Icon, Button } from 'patternfly-react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { Wizard, Modal } from 'patternfly-react'; +import yaml from 'js-yaml'; import { checkRunningDeployment } from '../utils/checkRunningDeploymentHOC'; import { getCurrentPlanName } from '../../selectors/plans'; import { Loader } from '../ui/Loader'; import { fetchParameters } from '../../actions/ParametersActions'; import { startContainerImagesPrepare } from '../../actions/ContainerImagesActions'; -import { - RoutedWizard, - CloseModalXButton, - CloseModalButton -} from '../ui/Modals'; +import { RoutedWizard, CloseModalXButton } from '../ui/Modals'; import ContainerImagesPrepareForm from './ContainerImagesPrepareForm'; -import { getContainerImagePrepareParameterSeed } from '../../selectors/parameters'; +import { + getContainerImagePrepareParameterSeed, + getContainerImagePrepareParameter +} from '../../selectors/parameters'; import ContainerImagesPrepareFormActions from './ContainerImagesPrepareFormActions'; +import ContainerImagePrepareParameterForm from './ContainerImagePrepareParameterForm'; +import ContainerImagePrepareParameterFormActions from './ContainerImagePrepareParameterFormActions'; const messages = defineMessages({ - close: { - id: 'ContainerImagesWizard.close', - defaultMessage: 'Close' - }, - cancel: { - id: 'ContainerImagesWizard.cancel', - defaultMessage: 'Cancel' - }, - back: { - id: 'ContainerImagesWizard.back', - defaultMessage: 'Back' - }, - save: { - id: 'ContainerImagesWizard.save', - defaultMessage: 'Save Changes' - }, title: { id: 'ContainerImagesWizard.title', defaultMessage: 'Prepare Container Images' @@ -93,6 +80,7 @@ class ContainerImagesWizard extends Component { intl: { formatMessage }, isFetchingParameters, containerImagePrepareParameterSeed, + containerImagePrepareParameter, resetToDefaults } = this.props; @@ -152,31 +140,28 @@ class ContainerImagesWizard extends Component { resetToDefaults={resetToDefaults} /> ) : ( -

parameter form here

+ )} - - - - {activeStepIndex === 0 && } + {activeStepIndex === 0 && ( + this.setActiveStepIndex(1)} + /> + )} {activeStepIndex === 1 && ( - - - - - - - + this.setActiveStepIndex(0)} + /> )} @@ -184,6 +169,7 @@ class ContainerImagesWizard extends Component { } } ContainerImagesWizard.propTypes = { + containerImagePrepareParameter: ImmutablePropTypes.record.isRequired, containerImagePrepareParameterSeed: PropTypes.object.isRequired, currentPlanName: PropTypes.string.isRequired, fetchParameters: PropTypes.func.isRequired, @@ -196,6 +182,7 @@ const mapStateToProps = state => ({ containerImagePrepareParameterSeed: getContainerImagePrepareParameterSeed( state ), + containerImagePrepareParameter: getContainerImagePrepareParameter(state), currentPlanName: getCurrentPlanName(state), isFetchingParameters: state.parameters.isFetching }); diff --git a/src/js/components/ui/reduxForm/AceEditorInput.js b/src/js/components/ui/reduxForm/AceEditorInput.js new file mode 100644 index 00000000..627bca21 --- /dev/null +++ b/src/js/components/ui/reduxForm/AceEditorInput.js @@ -0,0 +1,51 @@ +/** + * Copyright 2018 Red Hat Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +import React, { Fragment } from 'react'; +import PropTypes from 'prop-types'; +import AceEditor from 'react-ace'; +import 'brace/theme/textmate'; +import 'brace/mode/yaml'; + +import AceEditorInputToolbar from './AceEditorInputToolbar'; + +const aceOnBlur = onBlur => (_event, editor) => { + const value = editor && editor.getValue(); + onBlur(value); +}; + +const AceEditorInput = ({ input, meta, description, ...rest }) => ( + + + + +); +AceEditorInput.propTypes = { + description: PropTypes.string.isRequired, + input: PropTypes.object.isRequired, + meta: PropTypes.object.isRequired, + mode: PropTypes.string.isRequired, + theme: PropTypes.string.isRequired +}; +AceEditorInput.defaultProps = { + theme: 'textmate', + mode: 'yaml', + tabSize: 2, + enableBasicAutocompletion: false, + editorProps: { $blockScrolling: true } +}; + +export default AceEditorInput; diff --git a/src/js/components/ui/reduxForm/AceEditorInputToolbar.js b/src/js/components/ui/reduxForm/AceEditorInputToolbar.js new file mode 100644 index 00000000..7c4cd6f6 --- /dev/null +++ b/src/js/components/ui/reduxForm/AceEditorInputToolbar.js @@ -0,0 +1,56 @@ +/** + * Copyright 2018 Red Hat Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +import React, { Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { Icon } from 'patternfly-react'; + +const AceEditorInputToolbar = ({ + valid, + invalid, + warning, + error, + description +}) => ( +
+ {invalid && ( + + {error} + + )} + {warning && ( + + {warning} + + )} + {valid && + !warning && + description && ( + + {description} + + )} +
+); +AceEditorInputToolbar.propTypes = { + description: PropTypes.string, + error: PropTypes.string, + invalid: PropTypes.bool.isRequired, + valid: PropTypes.bool.isRequired, + warning: PropTypes.string +}; + +export default AceEditorInputToolbar; diff --git a/src/less/base.less b/src/less/base.less index 754d1a3b..19a14cf3 100644 --- a/src/less/base.less +++ b/src/less/base.less @@ -163,6 +163,7 @@ @import "ui/Sidebar"; @import "ui/Toolbar"; @import "ui/Tooltips"; +@import "ui/AceEditorToolbar"; @import "components/Breadcrumbs"; @import "components/MainContent"; @import "components/EnvironmentConfiguration"; diff --git a/src/less/components/Loader.less b/src/less/components/Loader.less index 8c986c57..89d25926 100644 --- a/src/less/components/Loader.less +++ b/src/less/components/Loader.less @@ -25,6 +25,7 @@ display: flex; flex-direction: column; justify-content: center; + z-index: 100; &.card-loader { box-sizing: content-box; diff --git a/src/less/ui/AceEditorToolbar.less b/src/less/ui/AceEditorToolbar.less new file mode 100644 index 00000000..3e773e48 --- /dev/null +++ b/src/less/ui/AceEditorToolbar.less @@ -0,0 +1,23 @@ +/** + * Copyright 2018 Red Hat Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +.ace-editor-toolbar { + background: #f0f0f0; + color: #333; + border-top: 1px solid #e8e8e8; + padding: 3px 5px; + min-height: 24px; +} \ No newline at end of file