Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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