diff --git a/client/src/components/ElementEditor/Element.js b/client/src/components/ElementEditor/Element.js index c04b2ab2..c1dc3a57 100644 --- a/client/src/components/ElementEditor/Element.js +++ b/client/src/components/ElementEditor/Element.js @@ -239,19 +239,15 @@ class Element extends Component { key={element.ID} >
diff --git a/client/src/components/ElementEditor/ElementEditor.js b/client/src/components/ElementEditor/ElementEditor.js index e3f15654..d644e34f 100644 --- a/client/src/components/ElementEditor/ElementEditor.js +++ b/client/src/components/ElementEditor/ElementEditor.js @@ -1,13 +1,13 @@ import React, { PureComponent, PropTypes } from 'react'; import { inject } from 'lib/Injector'; +import { compose } from 'redux'; import { elementTypeType } from 'types/elementTypeType'; import { connect } from 'react-redux'; -import { compose } from 'redux'; import { loadElementFormStateName } from 'state/editor/loadElementFormStateName'; import { DragDropContext } from 'react-dnd'; import HTML5Backend from 'react-dnd-html5-backend'; import classnames from 'classnames'; - +import sortBlockMutation from 'state/editor/sortBlockMutation'; import ElementDragPreview from 'components/ElementEditor/ElementDragPreview'; /** @@ -19,7 +19,7 @@ class ElementEditor extends PureComponent { super(props); this.state = { - dragTargetElementId: false, + dragTargetElementId: undefined, isDragging: false, draggedElement: null, }; @@ -47,10 +47,19 @@ class ElementEditor extends PureComponent { } handleDragDrop() { + const { dragTargetElementId: targetId, draggedElement: element } = this.state; + const { actions: { handleSortBlock }, pageId } = this.props; + this.setState({ - dragTargetElementId: false, + dragTargetElementId: undefined, isDragging: false, }); + + if (!handleSortBlock || (targetId && targetId === element.ID)) { + return; + } + + handleSortBlock(element.ID, targetId || 0, pageId); } render() { @@ -99,6 +108,9 @@ ElementEditor.propTypes = { elementTypes: PropTypes.arrayOf(elementTypeType).isRequired, pageId: PropTypes.number.isRequired, elementalAreaId: PropTypes.number.isRequired, + actions: PropTypes.shape({ + handleSortBlock: PropTypes.func, + }), }; ElementEditor.defaultProps = {}; @@ -132,5 +144,6 @@ export default compose( ListComponent, }), () => 'ElementEditor' - ) + ), + sortBlockMutation )(ElementEditor); diff --git a/client/src/components/ElementEditor/ElementList.js b/client/src/components/ElementEditor/ElementList.js index e8138bea..5ffa9863 100644 --- a/client/src/components/ElementEditor/ElementList.js +++ b/client/src/components/ElementEditor/ElementList.js @@ -58,6 +58,7 @@ class ElementList extends Component { element={element} editTabs={this.getEditTabs(element)} link={element.BlockSchema.actions.edit} + isDragging={isDragging} onDragOver={onDragOver} onDragDrop={onDragDrop} onDragStart={onDragStart} diff --git a/client/src/components/ElementEditor/Header.js b/client/src/components/ElementEditor/Header.js index 9196c50c..f33c9b49 100644 --- a/client/src/components/ElementEditor/Header.js +++ b/client/src/components/ElementEditor/Header.js @@ -1,5 +1,6 @@ import React, { Component, PropTypes } from 'react'; import { Tooltip } from 'reactstrap'; +import { elementType } from 'types/elementType'; import { compose } from 'redux'; import { inject } from 'lib/Injector'; import i18n from 'i18n'; @@ -16,6 +17,19 @@ class Header extends Component { }; } + componentDidUpdate() { + if (this.state.tooltipOpen && this.props.disableTooltip) { + // This addresses an issue where the tooltip will stick around after dragging. The + // ability to have a tooltip is back (props.disableTooltip) but the old state remains. + // Using `setState` in `componentDidUpdate` is dangerous but is okay within a reasonable + // condition that avoids the (potential) infinite loop. + // eslint-disable-next-line react/no-did-update-set-state + this.setState({ + tooltipOpen: false + }); + } + } + toggle() { this.setState({ tooltipOpen: !this.state.tooltipOpen @@ -28,7 +42,7 @@ class Header extends Component { * @returns {DOMElement|null} */ renderVersionedStateMessage() { - const { isLiveVersion, isPublished } = this.props; + const { element: { IsLiveVersion: isLiveVersion, IsPublished: isPublished } } = this.props; // No indication required for published elements if (isPublished && isLiveVersion) { @@ -58,20 +72,18 @@ class Header extends Component { render() { const { - id, - title, - elementType, - fontIcon, - expandable, + element, previewExpanded, simple, + disableTooltip, + expandable, ElementActionsComponent, } = this.props; const noTitle = i18n.inject(i18n._t('ElementHeader.NOTITLE', 'Untitled {type} block'), { type: elementType }); const titleClasses = classNames({ 'element-editor-header__title': true, - 'element-editor-header__title--none': !title, + 'element-editor-header__title--none': !element.Title, }); const expandTitle = i18n._t('ElementHeader.EXPAND', 'Show editable fields'); const containerClasses = classNames( @@ -87,6 +99,7 @@ class Header extends Component { 'font-icon-down-open-big': expandable && !previewExpanded, } ); + const blockIconId = `element-icon-${element.ID}`; return (
@@ -95,18 +108,18 @@ class Header extends Component {
- + {this.renderVersionedStateMessage()} {!simple && - {elementType} + {element.BlockSchema.type} }
-

{title || noTitle}

+

{element.Title || noTitle}

{!simple &&
{expandable && @@ -125,17 +138,12 @@ class Header extends Component { } Header.propTypes = { - id: PropTypes.string, - title: PropTypes.string, - version: PropTypes.number, - isLiveVersion: PropTypes.bool, - isPublished: PropTypes.bool, + element: elementType.isRequired, elementType: PropTypes.string, - fontIcon: PropTypes.string, simple: PropTypes.bool, ElementActionsComponent: React.PropTypes.oneOfType([React.PropTypes.node, React.PropTypes.func]), - expandable: PropTypes.bool, previewExpanded: PropTypes.bool, + disableTooltip: PropTypes.bool, }; Header.defaultProps = { diff --git a/client/src/components/ElementEditor/tests/Header-test.js b/client/src/components/ElementEditor/tests/Header-test.js index 9582ede0..73689a4b 100644 --- a/client/src/components/ElementEditor/tests/Header-test.js +++ b/client/src/components/ElementEditor/tests/Header-test.js @@ -11,60 +11,62 @@ Enzyme.configure({ adapter: new Adapter() }); describe('Header', () => { const ElementActionsComponent = () =>
; const testTabs = ['Content', 'Settings', 'History']; + const element = { + ID: '0', + InlineEditable: true, + Title: 'Sample File Block', + BlockSchema: { + type: 'File', + iconClass: 'font-icon-block-file', + }, + }; describe('render()', () => { it('should render the icon', () => { + element.ID = '11'; const wrapper = shallow(
); - expect(wrapper.instance().props.id).toBe('11'); expect(wrapper.find('i.font-icon-block-file')).toHaveLength(1); - expect(wrapper.find('#element-editor-header__icon11')).toHaveLength(1); + expect(wrapper.find('#element-icon-11')).toHaveLength(1); }); it('should render the title', () => { + element.ID = '12'; const wrapper = shallow(
); - expect(wrapper.instance().props.id).toBe('12'); expect(wrapper.text()).toContain('Sample File Block'); }); it('should contain a Tooltip', () => { + element.ID = '13'; const wrapper = shallow(
); - expect(wrapper.instance().props.id).toBe('13'); - expect(wrapper.find('Tooltip').length).toBe(1); - expect(wrapper.instance().props.elementType).toBe('File'); + const tooltip = wrapper.find('Tooltip'); + expect(tooltip.length).toBe(1); + expect(tooltip.children().text()).toBe('File'); }); it('should render a "right caret" button when not expandable', () => { const wrapper = shallow(
@@ -78,6 +80,7 @@ describe('Header', () => { it('should render a "down caret" button when not expanded', () => { const wrapper = shallow(
{ it('should render an "up caret" button when expanded', () => { const wrapper = shallow(
{ it('should render an ElementActions component when the element is expandable', () => { const wrapper = shallow(
@@ -117,6 +122,7 @@ describe('Header', () => { it('should not render an ElementActions when the element is not expandable', () => { const wrapper = shallow(
@@ -128,11 +134,12 @@ describe('Header', () => { describe('renderVersionedStateMessage()', () => { it('identifies draft versions', () => { + element.IsPublished = false; + element.IsLiveVersion = false; const wrapper = shallow(
); @@ -142,11 +149,12 @@ describe('Header', () => { }); it('identifies modified versions', () => { + element.IsPublished = true; + element.IsLiveVersion = false; const wrapper = shallow(
); @@ -156,11 +164,12 @@ describe('Header', () => { }); it('ignores live versions', () => { + element.IsPublished = true; + element.IsLiveVersion = true; const wrapper = shallow(
); diff --git a/client/src/state/editor/sortBlockMutation.js b/client/src/state/editor/sortBlockMutation.js index 2d3050a5..7bc96b8b 100644 --- a/client/src/state/editor/sortBlockMutation.js +++ b/client/src/state/editor/sortBlockMutation.js @@ -22,8 +22,10 @@ const config = { afterBlockId, }, optimisticResponse: { - ID: blockId, - __typename: 'Block', + sortBlock: { + ID: blockId, + __typename: 'Block', + }, }, update: store => { const variables = readBlocksConfig.options({ pageId }).variables;