diff --git a/cypress/tests/core/basic/workflow.js b/cypress/tests/core/basic/workflow.js new file mode 100644 index 0000000000..0a58077af6 --- /dev/null +++ b/cypress/tests/core/basic/workflow.js @@ -0,0 +1,25 @@ +describe('workflow Tests', () => { + beforeEach(() => { + cy.autologin(); + cy.createContent({ + contentType: 'Document', + contentId: 'my-page', + contentTitle: 'My Page', + allow_discussion: true, + } + ); + cy.visit('/contents'); + }); + it('change workflow state recursively', function () { + cy.get('tr[aria-label="/my-page"]').within(() => { + cy.get('button[class="ui basic icon button"]').click({ multiple: true }); + }) + cy.get('button[class="ui button icon item"]').eq(1).click(); + cy.findByText('Select…').click(); + cy.findByText('Publish').click(); + cy.findByTitle('Save').click(); + cy.get('tr[aria-label="/my-page"]').within(() => { + cy.get('td > div').should('contain','Published'); + }) + }); + }); \ No newline at end of file diff --git a/news/4902.feature b/news/4902.feature new file mode 100644 index 0000000000..7b9df8bbf2 --- /dev/null +++ b/news/4902.feature @@ -0,0 +1 @@ +Refactor workflow -@Tishasoumya-02 \ No newline at end of file diff --git a/src/components/manage/Workflow/Workflow.jsx b/src/components/manage/Workflow/Workflow.jsx index adc5a5b579..90996c5a02 100644 --- a/src/components/manage/Workflow/Workflow.jsx +++ b/src/components/manage/Workflow/Workflow.jsx @@ -1,22 +1,17 @@ -/** - * Workflow component. - * @module components/manage/Workflow/Workflow - */ - -import React, { Component, Fragment } from 'react'; +import { useEffect } from 'react'; import PropTypes from 'prop-types'; import { compose } from 'redux'; -import { connect } from 'react-redux'; +import { useDispatch, useSelector, shallowEqual } from 'react-redux'; import { uniqBy } from 'lodash'; import { toast } from 'react-toastify'; -import { defineMessages, injectIntl } from 'react-intl'; +import { defineMessages, useIntl } from 'react-intl'; + import { FormFieldWrapper, Icon, Toast } from '@plone/volto/components'; import { flattenToAppURL, - getCurrentStateMapping, getWorkflowOptions, + getCurrentStateMapping, } from '@plone/volto/helpers'; - import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable'; import { @@ -24,7 +19,6 @@ import { getWorkflow, transitionWorkflow, } from '@plone/volto/actions'; - import downSVG from '@plone/volto/icons/down-key.svg'; import upSVG from '@plone/volto/icons/up-key.svg'; import checkSVG from '@plone/volto/icons/check.svg'; @@ -165,191 +159,88 @@ const customSelectStyles = { }), }; -/** - * Workflow container class. - * @class Workflow - * @extends Component - */ -class Workflow extends Component { - /** - * Property types. - * @property {Object} propTypes Property types. - * @static - */ - static propTypes = { - getContent: PropTypes.func.isRequired, - getWorkflow: PropTypes.func.isRequired, - transitionWorkflow: PropTypes.func.isRequired, - loaded: PropTypes.bool.isRequired, - pathname: PropTypes.string.isRequired, - history: PropTypes.arrayOf( - PropTypes.shape({ - review_state: PropTypes.string, - }), - ), - transitions: PropTypes.arrayOf( - PropTypes.shape({ - '@id': PropTypes.string, - title: PropTypes.string, - }), - ), - }; +function useWorkflow() { + const history = useSelector((state) => state.workflow.history, shallowEqual); + const transitions = useSelector( + (state) => state.workflow.transitions, + shallowEqual, + ); + const loaded = useSelector((state) => state.workflow.transition.loaded); + const currentStateValue = useSelector( + (state) => getCurrentStateMapping(state.workflow.currentState), + shallowEqual, + ); - /** - * Default properties - * @property {Object} defaultProps Default properties. - * @static - */ - static defaultProps = { - history: [], - transitions: [], - }; + return { loaded, history, transitions, currentStateValue }; +} - componentDidMount() { - this.props.getWorkflow(this.props.pathname); - } +const Workflow = (props) => { + const intl = useIntl(); + const dispatch = useDispatch(); + const { loaded, transitions, currentStateValue } = useWorkflow(); + const content = useSelector((state) => state.content?.data, shallowEqual); + const { pathname } = props; - /** - * Component will receive props - * @method componentWillReceiveProps - * @param {Object} nextProps Next properties - * @returns {undefined} - */ - UNSAFE_componentWillReceiveProps(nextProps) { - if (nextProps.pathname !== this.props.pathname) { - this.props.getWorkflow(nextProps.pathname); - } - if (!this.props.loaded && nextProps.loaded) { - this.props.getWorkflow(nextProps.pathname); - this.props.getContent(nextProps.pathname); - } - } + useEffect(() => { + dispatch(getWorkflow(pathname)); + dispatch(getContent(pathname)); + }, [dispatch, pathname, loaded]); - /** - * On transition handler - * @method transition - * @param {string} event Event object - * @returns {undefined} - */ - transition = (selectedOption) => { - this.props.transitionWorkflow(flattenToAppURL(selectedOption.url)); + const transition = (selectedOption) => { + dispatch(transitionWorkflow(flattenToAppURL(selectedOption.url))); toast.success( , ); }; - selectValue = (option) => { - const stateDecorator = { - marginLeft: '10px', - marginRight: '10px', - display: 'inline-block', - backgroundColor: option.color || null, - content: ' ', - height: '10px', - width: '10px', - borderRadius: '50%', - }; - return ( - - - {option.label} - - ); - }; - - optionRenderer = (option) => { - const stateDecorator = { - marginLeft: '10px', - marginRight: '10px', - display: 'inline-block', - backgroundColor: - this.props.currentStateValue.value === option.value - ? option.color - : null, - content: ' ', - height: '10px', - width: '10px', - borderRadius: '50%', - border: - this.props.currentStateValue.value !== option.value - ? `1px solid ${option.color}` - : null, - }; - - return ( - - - {option.label} - - - ); - }; + const { Placeholder } = props.reactSelect.components; + const Select = props.reactSelect.default; - render() { - const { Placeholder } = this.props.reactSelect.components; - const Select = this.props.reactSelect.default; + return ( + + - getWorkflowOptions(transition), - ), - 'label', - ).concat(this.props.currentStateValue)} - styles={customSelectStyles} - theme={selectTheme} - components={{ - DropdownIndicator, - Placeholder, - Option, - SingleValue, - }} - onChange={this.transition} - value={ - this.props.content.review_state - ? this.props.currentStateValue - : { - label: this.props.intl.formatMessage( - messages.messageNoWorkflow, - ), - value: 'noworkflow', - } - } - isSearchable={false} - /> - - ); - } -} +Workflow.propTypes = { + pathname: PropTypes.string.isRequired, +}; -export default compose( - injectIntl, - injectLazyLibs(['reactSelect']), - connect( - (state) => ({ - loaded: state.workflow.transition.loaded, - content: state.content.data, - history: state.workflow.history, - transitions: state.workflow.transitions, - currentStateValue: getCurrentStateMapping(state.workflow.currentState), - }), - { getContent, getWorkflow, transitionWorkflow }, - ), -)(Workflow); +export default compose(injectLazyLibs(['reactSelect']))(Workflow);