From 0dee3b127771fd5adc73fdd94e4156ac9818fcdd Mon Sep 17 00:00:00 2001 From: Rob Gietema Date: Wed, 26 Apr 2017 21:22:19 +0200 Subject: [PATCH] Batch properties in folder contents --- CHANGELOG.md | 2 + src/components/index.js | 1 + src/components/manage/Contents/Contents.jsx | 48 +++++- .../Contents/ContentsPropertiesModal.jsx | 147 ++++++++++++++++++ .../Contents/ContentsPropertiesModal.test.jsx | 35 +++++ .../ContentsPropertiesModal.test.jsx.snap | 7 + 6 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 src/components/manage/Contents/ContentsPropertiesModal.jsx create mode 100644 src/components/manage/Contents/ContentsPropertiesModal.test.jsx create mode 100644 src/components/manage/Contents/__snapshots__/ContentsPropertiesModal.test.jsx.snap diff --git a/CHANGELOG.md b/CHANGELOG.md index 2932ec0235..af5a266ea0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## [Unreleased] ### Added +- Batch properties in folder contents @robgietema +- Batch tags in folder contents @robgietema - Batch rename in folder contents @robgietema - Diff view @robgietema - Add revert to version @robgietema diff --git a/src/components/index.js b/src/components/index.js index 0029a19266..54fb09f1db 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -34,6 +34,7 @@ export Contents from './manage/Contents/Contents'; export ContentsIndexHeader from './manage/Contents/ContentsIndexHeader'; export ContentsItem from './manage/Contents/ContentsItem'; export ContentsUploadModal from './manage/Contents/ContentsUploadModal'; +export ContentsPropertiesModal from './manage/Contents/ContentsPropertiesModal'; export ContentsRenameModal from './manage/Contents/ContentsRenameModal'; export ContentsTagsModal from './manage/Contents/ContentsTagsModal'; export Delete from './manage/Delete/Delete'; diff --git a/src/components/manage/Contents/Contents.jsx b/src/components/manage/Contents/Contents.jsx index 3d212870f8..48ec4d8de7 100644 --- a/src/components/manage/Contents/Contents.jsx +++ b/src/components/manage/Contents/Contents.jsx @@ -41,6 +41,7 @@ import { ContentsRenameModal, ContentsUploadModal, ContentsTagsModal, + ContentsPropertiesModal, Pagination, } from '../../../components'; @@ -159,6 +160,8 @@ export default class ContentsComponent extends Component { this.onRenameCancel = this.onRenameCancel.bind(this); this.onTagsOk = this.onTagsOk.bind(this); this.onTagsCancel = this.onTagsCancel.bind(this); + this.onPropertiesOk = this.onPropertiesOk.bind(this); + this.onPropertiesCancel = this.onPropertiesCancel.bind(this); this.onChangeFilter = this.onChangeFilter.bind(this); this.onChangePage = this.onChangePage.bind(this); this.onChangePageSize = this.onChangePageSize.bind(this); @@ -173,6 +176,7 @@ export default class ContentsComponent extends Component { this.upload = this.upload.bind(this); this.rename = this.rename.bind(this); this.tags = this.tags.bind(this); + this.properties = this.properties.bind(this); this.paste = this.paste.bind(this); this.fetchContents = this.fetchContents.bind(this); this.state = { @@ -181,6 +185,7 @@ export default class ContentsComponent extends Component { showUpload: false, showRename: false, showTags: false, + showProperties: false, itemsToDelete: [], items: this.props.items, filter: '', @@ -525,6 +530,30 @@ export default class ContentsComponent extends Component { }); } + /** + * On properties ok + * @method onPropertiesOk + * @returns {undefined} + */ + onPropertiesOk() { + this.fetchContents(); + this.setState({ + showProperties: false, + selected: [], + }); + } + + /** + * On properties cancel + * @method onPropertiesCancel + * @returns {undefined} + */ + onPropertiesCancel() { + this.setState({ + showProperties: false, + }); + } + /** * Get field by id * @method getFieldById @@ -625,6 +654,17 @@ export default class ContentsComponent extends Component { }); } + /** + * Properties handler + * @method properties + * @returns {undefined} + */ + properties() { + this.setState({ + showProperties: true, + }); + } + /** * Paste handler * @method paste @@ -697,6 +737,12 @@ export default class ContentsComponent extends Component { subjects: this.getFieldById(item, 'Subject'), }))} /> +

Contents

@@ -733,7 +779,7 @@ export default class ContentsComponent extends Component { State - + Properties diff --git a/src/components/manage/Contents/ContentsPropertiesModal.jsx b/src/components/manage/Contents/ContentsPropertiesModal.jsx new file mode 100644 index 0000000000..ca777b3bae --- /dev/null +++ b/src/components/manage/Contents/ContentsPropertiesModal.jsx @@ -0,0 +1,147 @@ +/** + * Contents properties modal. + * @module components/manage/Contents/ContentsPropertiesModal + */ + +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { isEmpty, map } from 'lodash'; + +import { editContent } from '../../../actions'; +import { ModalForm } from '../../../components'; + +/** + * ContentsPropertiesModal class. + * @class ContentsPropertiesModal + * @extends Component + */ +@connect( + state => ({ + request: state.content.edit, + }), + dispatch => bindActionCreators({ editContent }, dispatch), +) +export default class ContentsPropertiesModal extends Component { + /** + * Property types. + * @property {Object} propTypes Property types. + * @static + */ + static propTypes = { + editContent: PropTypes.func.isRequired, + items: PropTypes.arrayOf(PropTypes.string).isRequired, + request: PropTypes.shape({ + loading: PropTypes.bool, + loaded: PropTypes.bool, + }).isRequired, + open: PropTypes.bool.isRequired, + onOk: PropTypes.func.isRequired, + onCancel: PropTypes.func.isRequired, + }; + + /** + * Constructor + * @method constructor + * @param {Object} props Component properties + * @constructs ContentsUploadModal + */ + constructor(props) { + super(props); + this.onSubmit = this.onSubmit.bind(this); + } + + /** + * Component will receive props + * @method componentWillReceiveProps + * @param {Object} nextProps Next properties + * @returns {undefined} + */ + componentWillReceiveProps(nextProps) { + if (this.props.request.loading && nextProps.request.loaded) { + this.props.onOk(); + } + } + + /** + * Submit handler + * @method onSubmit + * @param {Object} data Form data + * @returns {undefined} + */ + onSubmit(data) { + if (isEmpty(data)) { + this.props.onOk(); + } else { + this.props.editContent( + this.props.items, + map(this.props.items, () => data), + ); + } + } + + /** + * Render method. + * @method render + * @returns {string} Markup for the component. + */ + render() { + return ( + this.props.open && + + ); + } +} diff --git a/src/components/manage/Contents/ContentsPropertiesModal.test.jsx b/src/components/manage/Contents/ContentsPropertiesModal.test.jsx new file mode 100644 index 0000000000..1bec3dd22b --- /dev/null +++ b/src/components/manage/Contents/ContentsPropertiesModal.test.jsx @@ -0,0 +1,35 @@ +import React from 'react'; +import renderer from 'react-test-renderer'; +import configureStore from 'redux-mock-store'; +import { Provider } from 'react-redux'; + +import ContentsPropertiesModal from './ContentsPropertiesModal'; + +const mockStore = configureStore(); + +jest.mock('../Form/ModalForm', () => jest.fn(() =>
)); + +describe('ContentsPropertiesModal', () => { + it('renders a contents properties modal component', () => { + const store = mockStore({ + content: { + edit: { + loading: false, + loaded: true, + }, + }, + }); + const component = renderer.create( + + {}} + onCancel={() => {}} + items={['/blog']} + /> + , + ); + const json = component.toJSON(); + expect(json).toMatchSnapshot(); + }); +}); diff --git a/src/components/manage/Contents/__snapshots__/ContentsPropertiesModal.test.jsx.snap b/src/components/manage/Contents/__snapshots__/ContentsPropertiesModal.test.jsx.snap new file mode 100644 index 0000000000..3c382ace86 --- /dev/null +++ b/src/components/manage/Contents/__snapshots__/ContentsPropertiesModal.test.jsx.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ContentsPropertiesModal renders a contents properties modal component 1`] = ` +
+`;