@carbon/ibm-security
Version:
Carbon for Cloud & Cognitive IBM Security UI components
454 lines (453 loc) • 18 kB
JavaScript
import _extends from "@babel/runtime/helpers/extends";
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
import _createClass from "@babel/runtime/helpers/createClass";
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
import _inherits from "@babel/runtime/helpers/inherits";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
var _excluded = ["labels", "focusTrap", "title", "subTitle", "className", "navLabel"];
import _regeneratorRuntime from "@babel/runtime/regenerator";
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
/**
* @file Wizard.
* @copyright IBM Security 2019 - 2021
*/
import classnames from 'classnames';
import PropTypes from 'prop-types';
import React, { Children, Component } from 'react';
import { getComponentNamespace } from '../../globals/namespace';
import * as defaultLabels from '../../globals/nls';
import { isClient, isNode } from '../../globals/utils/capabilities';
import Nav from '../Nav';
import { ProgressIndicator, ProgressStep } from '../ProgressIndicator';
import NavItem from '../Nav/NavItem';
import { Tearsheet } from '../Tearsheet';
import WizardStep from './WizardStep';
var namespace = getComponentNamespace('wizard');
var Wizard = /*#__PURE__*/function (_Component) {
function Wizard() {
var _this;
_classCallCheck(this, Wizard);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _callSuper(this, Wizard, [].concat(args));
_defineProperty(_this, "state", {
steps: _this.steps,
componentState: _this.props.initState,
stepInitState: _this.props.initState,
currentStep: 0,
isOpen: true,
loading: false,
controlledOpen: _this.props.isOpen != null,
deleteIsLoading: false
});
_defineProperty(_this, "setNextState", /*#__PURE__*/function () {
var _ref = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee(cb) {
var changes, isLastStep;
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
_this.setState({
loading: true
});
_context.prev = 1;
_context.next = 4;
return _this.currentStep.next(_this.state.componentState);
case 4:
changes = _context.sent;
isLastStep = _this.isLastStep();
_this.setState(function (_ref2) {
var currentStep = _ref2.currentStep,
componentState = _ref2.componentState;
return {
currentStep: currentStep + (isLastStep ? 0 : 1),
componentState: _objectSpread(_objectSpread(_objectSpread({}, componentState), changes), {}, {
error: undefined
}),
loading: false,
valid: undefined
};
}, function () {
_this.setState(function (_ref3) {
var componentState = _ref3.componentState;
return {
stepInitState: componentState
};
});
if (isLastStep) {
if (cb) {
cb();
}
_this.props.onClose(_this.state.componentState);
_this.setState({
isOpen: false
});
}
});
_context.next = 12;
break;
case 9:
_context.prev = 9;
_context.t0 = _context["catch"](1);
_this.setState(function (_ref4) {
var componentState = _ref4.componentState;
return {
loading: false,
componentState: _objectSpread(_objectSpread({}, componentState), {}, {
error: _context.t0
})
};
});
case 12:
case "end":
return _context.stop();
}
}, _callee, null, [[1, 9]]);
}));
return function (_x) {
return _ref.apply(this, arguments);
};
}());
_defineProperty(_this, "setComponentState", function (changes, cb) {
if (typeof changes === 'function') {
_this.setState(function (_ref5) {
var componentState = _ref5.componentState;
var newComponentState = _objectSpread(_objectSpread({}, componentState), changes(componentState));
return {
componentState: newComponentState,
valid: _this.currentStep.validate(newComponentState)
};
}, cb);
} else {
var newComponentState = _objectSpread(_objectSpread({}, _this.state.componentState), changes);
_this.setState({
componentState: newComponentState,
valid: _this.currentStep.validate(newComponentState)
}, cb);
}
});
_defineProperty(_this, "isLastStep", function () {
return _this.state.currentStep === _this.steps.length - 1;
});
/**
* Keeps the state in sync with the current props.
* @param {Props} nextProps The current props passed to the component.
* @returns {Record<object, any>} The updated state for the component.
* @static
*/
_defineProperty(_this, "secondaryAction", function () {
_this.setState(function (_ref6) {
var componentState = _ref6.componentState,
currentStep = _ref6.currentStep,
steps = _ref6.steps;
var previousStep = currentStep - 1;
return {
currentStep: previousStep,
valid: steps[previousStep].validate(componentState)
};
});
});
_defineProperty(_this, "closeAction", function () {
_this.props.onClose(_this.state.componentState);
if (!_this.state.controlledOpen) {
_this.setState({
isOpen: false
});
}
});
_defineProperty(_this, "deleteAction", /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
_this.setState({
deleteIsLoading: true
});
_context2.prev = 1;
_context2.next = 4;
return _this.props.onDelete(_this.state.componentState);
case 4:
_this.setState(function (_ref8) {
var controlledOpen = _ref8.controlledOpen;
return {
deleteIsLoading: false,
isOpen: !controlledOpen
};
});
_context2.next = 11;
break;
case 7:
_context2.prev = 7;
_context2.t0 = _context2["catch"](1);
_this.setState({
deleteIsLoading: false,
deleteButtonText: 'Failed'
});
setTimeout(function () {
return _this.setState({
deleteButtonText: undefined
});
}, 5000);
case 11:
case "end":
return _context2.stop();
}
}, _callee2, null, [[1, 7]]);
})));
_defineProperty(_this, "handleItemSelect", function (event) {
var id = event.target.id;
_this.setState(function (_ref9) {
var componentState = _ref9.componentState,
steps = _ref9.steps;
return {
currentStep: Number(id),
valid: steps[id].validate(componentState)
};
});
});
_defineProperty(_this, "renderSidebar", function () {
var _this$state = _this.state,
currentStep = _this$state.currentStep,
valid = _this$state.valid;
return !_this.sequentialMode ? /*#__PURE__*/React.createElement(Nav, {
label: _this.props.navLabel
}, _this.steps.map(function (_ref10, index) {
var title = _ref10.title;
return /*#__PURE__*/React.createElement(NavItem, {
key: title,
id: String(index),
className: "".concat(namespace, "__navItem"),
current: String(currentStep),
disabled: !valid,
handleItemSelect: _this.handleItemSelect,
link: false
}, title);
})) : /*#__PURE__*/React.createElement(ProgressIndicator, {
currentIndex: currentStep,
vertical: true
}, _this.steps.map(function (_ref11, index) {
var title = _ref11.title;
return /*#__PURE__*/React.createElement(ProgressStep, {
key: title,
disabled: index > currentStep,
label: title
});
}));
});
return _this;
}
_inherits(Wizard, _Component);
return _createClass(Wizard, [{
key: "editMode",
get: function get() {
return this.props.onDelete !== Wizard.defaultProps.onDelete;
}
}, {
key: "sequentialMode",
get: function get() {
if (typeof this.props.isSequential !== 'undefined') {
return this.props.isSequential;
}
return !this.editMode;
}
}, {
key: "currentStep",
get: function get() {
if (this.state.steps[this.state.currentStep]) {
return _objectSpread(_objectSpread({}, WizardStep.defaultProps), this.state.steps[this.state.currentStep]);
}
return undefined;
}
}, {
key: "steps",
get: function get() {
if (Children.count(this.props.children)) {
return Children.map(this.props.children, WizardStep.getPropsFromElement).filter(function (props) {
return props != null;
});
} else if (Array.isArray(this.props.steps)) {
return this.props.steps;
}
return [];
}
}, {
key: "isValid",
get: function get() {
if (this.state.valid == null) {
var valid = this.currentStep.validate(this.state.componentState);
this.setState({
valid: valid
});
return valid;
}
return this.state.valid;
}
}, {
key: "render",
value:
/**
* Renders the component.
*/
function render() {
var _this2 = this;
var _this$props = this.props,
labels = _this$props.labels,
focusTrap = _this$props.focusTrap,
title = _this$props.title,
subTitle = _this$props.subTitle,
className = _this$props.className,
_ = _this$props.navLabel,
other = _objectWithoutProperties(_this$props, _excluded);
var componentLabels = _objectSpread(_objectSpread({}, defaultLabels.labels), labels);
var _this$state$isOpen = this.state.isOpen,
isOpen = _this$state$isOpen === void 0 ? true : _this$state$isOpen;
if (!this.currentStep) {
return null;
}
var buttons = {
primary: {
onClick: function onClick() {
return _this2.setNextState();
},
isDisabled: !this.isValid || this.state.loading
},
secondary: this.state.currentStep === 0 ? {
onClick: this.closeAction,
isDisabled: true
} : {
onClick: function onClick() {
return _this2.setState(function (_ref12) {
var componentState = _ref12.componentState,
currentStep = _ref12.currentStep,
steps = _ref12.steps;
var previousStep = currentStep - 1;
return {
currentStep: previousStep,
valid: steps[previousStep].validate(componentState)
};
});
},
isDisabled: false
},
cancelSetup: {
onClick: this.closeAction,
isDisabled: false,
secondaryText: componentLabels.WIZARD_TERTIARY_SECONDARY_TEXT
},
// Wizard should not have a close button in the top right.
close: {
isDisabled: true
},
delete: this.editMode ? {
onClick: this.deleteAction,
isDisabled: this.state.deleteIsLoading
} : undefined
};
var buttonLabels = {
TEARSHEET_PRIMARY_BUTTON: this.isLastStep() ? componentLabels.WIZARD_FINISH_BUTTON : componentLabels.WIZARD_NEXT_BUTTON,
TEARSHEET_SECONDARY_BUTTON: this.state.currentStep === 0 ? componentLabels.WIZARD_CANCEL_BUTTON : componentLabels.WIZARD_BACK_BUTTON,
TEARSHEET_DELETE_BUTTON: this.editMode ? this.state.deleteButtonText || componentLabels.WIZARD_TEARSHEET_DELETE_BUTTON : undefined,
TEARSHEET_TERTIARY_BUTTON: componentLabels.WIZARD_TERTIARY_BUTTON
};
var renderMain = function renderMain() {
return _this2.currentStep.renderMain(_this2.state.componentState, _this2.setComponentState);
};
return /*#__PURE__*/React.createElement(Tearsheet, _extends({
focusTrap: focusTrap,
sidebarTitle: title,
sidebarSubtitle: subTitle,
mainTitle: this.currentStep.title,
renderSidebar: this.renderSidebar,
renderMain: renderMain,
rootNode: this.props.rootNode,
primaryButton: buttons.primary,
secondaryButton: buttons.secondary,
tertiaryButton: buttons.cancelSetup,
closeButton: buttons.close,
deleteButton: buttons.delete,
isOpen: isOpen,
labels: _objectSpread(_objectSpread({}, componentLabels), defaultLabels.filterFalsey(buttonLabels)),
loading: this.state.loading,
loadingMessage: this.props.loadingMessage,
className: classnames(namespace, className)
}, other));
}
}], [{
key: "getDerivedStateFromProps",
value: function getDerivedStateFromProps(props, state) {
var nextState = state;
if (state.controlledOpen && props.isOpen && !state.isOpen) {
nextState = _objectSpread(_objectSpread({}, nextState), {}, {
currentStep: 0,
componentState: props.initState
});
}
nextState = _objectSpread(_objectSpread({}, nextState), {}, {
isOpen: state.controlledOpen ? props.isOpen : state.isOpen
});
return nextState;
}
}]);
}(Component);
Wizard.propTypes = {
/** @type {...React.Element(WizardStep)} Provide one element of the WizardStep component for each step of your wizard (see the WizardStep docs).
*/
children: PropTypes.node,
/** Optional class name for the wrapper node. */
className: PropTypes.string,
/** @type {boolean} Focus trap. */
focusTrap: PropTypes.bool,
/** @type {object} The initial state object of the wizard
* (useful to prefill some values in your forms). */
initState: PropTypes.instanceOf(Object),
/** @type {boolean} The open state.
* Leave this property undefined, to give control over the open state to the Wizard component.
* When defined at component creation time, the open state is controlled only by this property.
*/
isOpen: PropTypes.bool,
/** @type {boolean} Defines whether the wizard is sequential or not. */
isSequential: PropTypes.bool,
/** @type {object} The labels to be used with the wizard
* (useful to override default labels)
*/
labels: defaultLabels.propType,
/** @type {string} The message to be displayed during loading. */
loadingMessage: PropTypes.string,
/** Provide an accessible label that describes the Wizard sidebar navigation. */
navLabel: PropTypes.string,
/** @type {function(wizardState: Object): any} This is called whenever the wizard closes (or wants to close) */
onClose: PropTypes.func,
/** @type {function(wizardState: Object): Promise} onDelete handler, enables edit mode */
onDelete: PropTypes.func,
/** @type {DOM Node} Target to render the wizard. */
rootNode: isNode() ? PropTypes.instanceOf(Node) : PropTypes.any,
/** @type {Array<object>} An object list of step props. __(deprecated)__ */
steps: PropTypes.arrayOf(PropTypes.shape(WizardStep.propTypes)),
/** @type {string} The subtitle of the Wizard. */
subTitle: PropTypes.string,
/** @type {string} The title of the Wizard. */
title: PropTypes.string.isRequired
};
Wizard.defaultProps = {
className: '',
focusTrap: true,
rootNode: isClient() ? document.body : undefined,
subTitle: '',
isOpen: undefined,
initState: {},
onClose: function onClose() {},
steps: [],
children: undefined,
onDelete: function onDelete() {
return Promise.resolve();
},
isSequential: undefined,
labels: {},
navLabel: 'Steps navigation'
};
export default Wizard;