Skip to content

Commit

Permalink
Merge pull request #3509 from himdel/form-buttons-react
Browse files Browse the repository at this point in the history
FormButtons component, with redux
  • Loading branch information
martinpovolny authored Jul 20, 2018
2 parents 6a4c870 + 034ad78 commit e9707a6
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 30 deletions.
79 changes: 79 additions & 0 deletions app/javascript/forms/form-buttons-redux.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { combineReducers } from 'redux';

import FormButtons from './form-buttons';

function FormButtonsRedux(props) {
initReducer();

return <FormButtons {...props} />;
}

FormButtonsRedux.propTypes = {
callbackOverrides: PropTypes.object,
};

FormButtonsRedux.defaultProps = {
callbackOverrides: {},
};


function mapStateToProps(state, ownProps) {
let props = {...state.FormButtons};

if (state.FormButtons && ownProps.callbackOverrides) {
// allow overriding click handlers
// the original handler is passed as the only argument to the new one
Object.keys(ownProps.callbackOverrides).forEach((name) => {
props[name] = () => {
let origHandler = state.FormButtons[name];
ownProps.callbackOverrides[name](origHandler);
};
});
}

return props;
}

export default connect(mapStateToProps)(FormButtonsRedux);

function initReducer() {
if (! ManageIQ.redux || ! ManageIQ.redux.addReducer) {
// login screen
return;
}
if (initReducer.done) {
// don't init twice
return;
}
initReducer.done = true;


const initialState = {...FormButtons.defaultProps};

const actions = {
'FormButtons.init': (_state, payload) => ({ ...initialState, ...payload }),
'FormButtons.customLabel': (state, payload) => ({ ...state, customLabel: payload || ''}),
'FormButtons.newRecord': (state, payload) => ({ ...state, newRecord: !! payload }),
'FormButtons.pristine': (state, payload) => ({ ...state, pristine: !! payload }),
'FormButtons.saveable': (state, payload) => ({ ...state, saveable: !! payload }),

'FormButtons.callbacks': (state, payload) => ({ ...state, ...payload }),
};


ManageIQ.redux.addReducer(combineReducers({
FormButtons: function FormButtonsReducer(state = initialState, action) {

if (actions[action.type]) {
return actions[action.type](state, action.payload);
} else if (action.type.match(/^FormButtons\./)) {
console.warn(`FormButtonsReducer - unknown action type: ${action.type}`, action);
}

return state;
},
}));
}
50 changes: 50 additions & 0 deletions app/javascript/forms/form-buttons.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';
import PropTypes from 'prop-types';

import MiqButton from './miq-button';

function FormButtons(props) {
let primaryTitle = props.customLabel || (props.newRecord ? __('Add') : __('Save'));
let primaryHandler = (props.newRecord ? props.addClicked : props.saveClicked) || props.addClicked || props.saveClicked;

let resetTitle = __("Reset");
let cancelTitle = __("Cancel");

return (
<React.Fragment>
<div className="clearfix"></div>
<div className="pull-right button-group edit_buttons">
<MiqButton name={primaryTitle} title={primaryTitle} enabled={props.saveable} onClick={primaryHandler} primary={true} />
{props.newRecord || <MiqButton name={resetTitle} title={resetTitle} enabled={! props.pristine} onClick={props.resetClicked} />}
<MiqButton name={cancelTitle} title={cancelTitle} enabled={true} onClick={props.cancelClicked} />

</div>
</React.Fragment>
);
}

FormButtons.propTypes = {
newRecord: PropTypes.bool,
customLabel: PropTypes.string,
saveable: PropTypes.bool,
pristine: PropTypes.bool,
addClicked: PropTypes.func,
saveClicked: PropTypes.func,
resetClicked: PropTypes.func,
cancelClicked: PropTypes.func,
};

const noop = () => null;

FormButtons.defaultProps = {
newRecord: false,
customLabel: '',
saveable: false,
pristine: true,
addClicked: noop,
saveClicked: noop,
resetClicked: noop,
cancelClicked: noop,
}

export default FormButtons;
60 changes: 60 additions & 0 deletions app/javascript/forms/miq-button.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';
import PropTypes from 'prop-types';
import ClassNames from 'classnames';

function MiqButton(props) {
let title = props.title;
if (props.enabled && props.enabledTitle) {
title = props.enabledTitle;
}
if (! props.enabled && props.disabledTitle) {
title = props.disabledTitle;
}

let klass = ClassNames({
'btn': true,
'btn-xs': props.xs,
'btn-primary': props.primary,
'btn-default': !props.primary,
'disabled': !props.enabled,
});

let buttonClicked = (event) => {
if (props.enabled) {
props.onClick();
}

event.preventDefault();
event.target.blur();
};

return (
<button className={klass}
onClick={buttonClicked}
title={title}
alt={title}>
{props.name}
</button>
);
}

MiqButton.propTypes = {
name: PropTypes.string.isRequired,
enabled: PropTypes.bool.isRequired,
title: PropTypes.string,
enabledTitle: PropTypes.string,
disabledTitle: PropTypes.string,
primary: PropTypes.bool,
xs: PropTypes.bool,
onClick: PropTypes.func.isRequired,
};

MiqButton.defaultProps = {
title: '',
enabledTitle: '',
disabledTitle: '',
primary: false,
xs: false,
};

export default MiqButton;
2 changes: 2 additions & 0 deletions app/javascript/packs/component-definitions-common.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import TableListViewWrapper from '../react/table_list_view_wrapper';
import GenericGroupWrapper from '../react/generic_group_wrapper';
import { addReact } from '../miq-component/helpers';
import VmSnapshotFormComponent from '../components/vm-snapshot-form-component';
import FormButtonsRedux from '../forms/form-buttons-redux';

/**
* Add component definitions to this file.
Expand All @@ -19,3 +20,4 @@ addReact('GenericGroup', GenericGroup);
addReact('GenericGroupWrapper', GenericGroupWrapper);
addReact('TextualSummaryWrapper', TextualSummaryWrapper);
addReact('VmSnapshotFormComponent', VmSnapshotFormComponent);
addReact('FormButtonsRedux', FormButtonsRedux);
71 changes: 41 additions & 30 deletions app/javascript/provider-dialogs/modal.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Button, Icon, Modal } from 'patternfly-react';
import { Provider } from 'react-redux';
import FormButtonsRedux from '../forms/form-buttons-redux';

function closeModal(id) {
// this should have been div.remove();
Expand All @@ -25,36 +27,45 @@ export default function renderModal(title = __("Modal"), Inner = () => <div>Empt
}

function modal(title, Inner, closed, removeId) {
const overrides = {
addClicked: function(orig) {
Promise.resolve(orig()).then(closed);
},
saveClicked: function(orig) {
Promise.resolve(orig()).then(closed);
},
cancelClicked: function(orig) {
Promise.resolve(orig()).then(closed);
},
// don't close on reset
};

return (
<Modal
show={true}
onHide={closed}
onExited={closed}
>
<Modal.Header>
<button
className="close"
onClick={closed}
aria-hidden="true"
aria-label="Close"
>
<Icon type="pf" name="close" />
</button>
<Modal.Title>{title}</Modal.Title>
</Modal.Header>
<Modal.Body>
<Inner />
<div id={/* see closeModal */ removeId}></div>
</Modal.Body>
<Modal.Footer>
<Button
bsStyle="primary"
onClick={closed}
autoFocus
>
{__('Close')}
</Button>
</Modal.Footer>
</Modal>
<Provider store={ManageIQ.redux.store}>
<Modal
show={true}
onHide={closed}
onExited={closed}
>
<Modal.Header>
<button
className="close"
onClick={closed}
aria-hidden="true"
aria-label="Close"
>
<Icon type="pf" name="close" />
</button>
<Modal.Title>{title}</Modal.Title>
</Modal.Header>
<Modal.Body>
<Inner />
<div id={/* see closeModal */ removeId}></div>
</Modal.Body>
<Modal.Footer>
<FormButtonsRedux callbackOverrides={overrides} />
</Modal.Footer>
</Modal>
</Provider>
);
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"angular.validators": "~4.4.2",
"base64-js": "~1.2.3",
"bootstrap-switch": "~3.3.4",
"classnames": "^2.2.5",
"graphiql": "^0.11.11",
"graphql": "^0.13.2",
"jquery": "~2.2.4",
Expand Down

0 comments on commit e9707a6

Please sign in to comment.