Skip to content

Commit

Permalink
Connect mutation to add blocks to relevant UI elements
Browse files Browse the repository at this point in the history
As part of inline editing for Elemental it has previously been only
possible to edit existing elements. Now it is possible to add new
ones to the relevant list for the pop over - both the add button
above the list, or the in-between button that pops up on hovering
the area close to the junction between two elements in the list.

For this work it was necessary to expose the ID of the elemental area
they belong to, in order to ensure elements are added to it as children.

There have been a few scss linting issues tidied up also.
  • Loading branch information
Dylan Wagstaff committed Oct 15, 2018
1 parent 13836aa commit 4452874
Show file tree
Hide file tree
Showing 19 changed files with 139 additions and 86 deletions.
2 changes: 1 addition & 1 deletion client/dist/js/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion client/dist/styles/bundle.css

Large diffs are not rendered by default.

49 changes: 44 additions & 5 deletions client/src/components/ElementEditor/AddElementPopover.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Button, Input, InputGroup, InputGroupAddon, Popover } from 'reactstrap'
import classNames from 'classnames';
import { elementTypeType } from 'types/elementTypeType';
import i18n from 'i18n';
import addElementMutation from 'state/editor/addElementMutation';

/**
* The AddElementPopover component used in the context of an ElementEditor shows the
Expand All @@ -17,12 +18,45 @@ class AddElementPopover extends Component {
this.handleSearchValueChange = this.handleSearchValueChange.bind(this);
this.handleClear = this.handleClear.bind(this);
this.handleToggle = this.handleToggle.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.getElementButtonClickHandler = this.getElementButtonClickHandler.bind(this);

this.state = {
searchValue: ''
};
}

/**
* click handler that preserves the details of what was clicked
* @param {object} elementType in the shape of types/elmementTypeType
* @returns {function}
*/
getElementButtonClickHandler(elementType) {
return (event) => {
const {
actions: { handleAddElementToArea },
elementalAreaId,
insertAfterElement
} = this.props;

event.preventDefault();
handleAddElementToArea(elementType.name.replace(/-/g, '\\'), elementalAreaId, insertAfterElement);
this.handleToggle();
};
}

/**
* Allow closure via `esc` from within popover
*/
handleKeyDown(event) {
switch (event.key) {
case 'Escape':
this.handleToggle();
break;
default:
}
}

/**
* Pass toggle to parent and clear the search input
*/
Expand Down Expand Up @@ -81,7 +115,7 @@ class AddElementPopover extends Component {
* @returns {DOMElement}
*/
renderElementButtons() {
const { baseAddHref } = this.props;
const { elementalAreaId } = this.props;
let { elementTypes } = this.props;
const { searchValue } = this.state;

Expand Down Expand Up @@ -110,8 +144,9 @@ class AddElementPopover extends Component {
)
}
key={elementType.name}
href={`${baseAddHref}/${elementType.name}`}
onClick={this.handleToggle}
name={elementType.name}
onClick={this.getElementButtonClickHandler(elementType)}
elementalAreaId={elementalAreaId}
>
{elementType.title}
</Button>
Expand Down Expand Up @@ -152,6 +187,7 @@ class AddElementPopover extends Component {
placement={placement}
target={target}
toggle={this.handleToggle}
onKeyDown={this.handleKeyDown}
>
<InputGroup className="element-editor-add-element__search">
<Input
Expand All @@ -172,14 +208,17 @@ class AddElementPopover extends Component {
}

AddElementPopover.propTypes = {
baseAddHref: PropTypes.string.isRequired,
container: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
elementTypes: PropTypes.arrayOf(elementTypeType),
extraClass: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object]),
isOpen: PropTypes.bool.isRequired,
placement: PropTypes.string,
target: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]).isRequired,
toggle: PropTypes.func.isRequired,
elementalAreaId: PropTypes.number.isRequired,
insertAfterElement: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
};

export default AddElementPopover;
export { AddElementPopover as Component };

export default addElementMutation(AddElementPopover);
8 changes: 5 additions & 3 deletions client/src/components/ElementEditor/AddElementPopover.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
:nth-child(odd) {
margin-right: $panel-padding-x/2;
}

:nth-child(even) {
margin-right: 0;
}

:nth-last-child(-n+2) {
margin-bottom: 0;
}
Expand All @@ -44,7 +46,7 @@
padding: $panel-padding-x/2;
text-align: start;
margin-bottom: 4px;
border: 0px;
border: 0;
max-height: 40px;
}

Expand All @@ -64,7 +66,7 @@
&:hover,
&:active {
box-shadow: none;
border: none;
border: 0;
border-bottom: 1px solid $border-color-light;
border-radius: 0;
outline: none;
Expand All @@ -85,7 +87,7 @@
}

&__inline {
z-index:1;
z-index: 1;
}
}

Expand Down
9 changes: 5 additions & 4 deletions client/src/components/ElementEditor/AddNewButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ class AddNewButton extends Component {
* @returns {DOMElement}
*/
render() {
const { AddElementPopoverComponent, elementTypes, baseAddHref } = this.props;
const { AddElementPopoverComponent, elementTypes, elementalAreaId } = this.props;
const buttonAttributes = {
id: 'AddButton',
id: `ElementalArea${elementalAreaId}_AddButton`,
color: 'primary',
onClick: this.toggle
};
Expand All @@ -43,8 +43,9 @@ class AddNewButton extends Component {
target={buttonAttributes.id}
isOpen={this.state.popoverOpen}
elementTypes={elementTypes}
baseAddHref={baseAddHref}
toggle={this.toggle}
elementalAreaId={elementalAreaId}
insertAfterElement={0}
/>
</div>
);
Expand All @@ -53,8 +54,8 @@ class AddNewButton extends Component {

AddNewButton.defaultProps = {};
AddNewButton.propTypes = {
baseAddHref: PropTypes.string.isRequired, // temp until elements can be added inline
elementTypes: PropTypes.arrayOf(elementTypeType).isRequired,
elementalAreaId: PropTypes.number.isRequired,
};

export { AddNewButton as Component };
Expand Down
4 changes: 1 addition & 3 deletions client/src/components/ElementEditor/Element.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ class Element extends Component {
ContentComponent,
link,
editTabs,
pageId,
activeTab
activeTab,
} = this.props;

const { previewExpanded } = this.state;
Expand Down Expand Up @@ -187,7 +186,6 @@ class Element extends Component {
elementType={element.BlockSchema.type}
fontIcon={element.BlockSchema.iconClass}
link={link}
pageId={pageId}
editTabs={editTabs}
previewExpanded={previewExpanded}
expandable={element.InlineEditable}
Expand Down
21 changes: 17 additions & 4 deletions client/src/components/ElementEditor/ElementEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,25 @@ import { elementTypeType } from 'types/elementTypeType';
*/
class ElementEditor extends PureComponent {
render() {
const { ToolbarComponent, ListComponent, pageId, elementTypes, baseAddHref } = this.props;
const {
ToolbarComponent,
ListComponent,
pageId,
elementalAreaId,
elementTypes,
} = this.props;

return (
<div className="element-editor">
<ToolbarComponent elementTypes={elementTypes} baseAddHref={baseAddHref} />
<ListComponent elementTypes={elementTypes} pageId={pageId} baseAddHref={baseAddHref} />
<ToolbarComponent
elementTypes={elementTypes}
elementalAreaId={elementalAreaId}
/>
<ListComponent
elementTypes={elementTypes}
pageId={pageId}
elementalAreaId={elementalAreaId}
/>
</div>
);
}
Expand All @@ -22,7 +35,7 @@ class ElementEditor extends PureComponent {
ElementEditor.propTypes = {
elementTypes: PropTypes.arrayOf(elementTypeType).isRequired,
pageId: PropTypes.number.isRequired,
baseAddHref: PropTypes.string.isRequired,
elementalAreaId: PropTypes.number.isRequired,
};

ElementEditor.defaultProps = {};
Expand Down
8 changes: 3 additions & 5 deletions client/src/components/ElementEditor/ElementList.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ class ElementList extends Component {
ElementComponent,
HoverBarComponent,
blocks,
pageId,
elementTypes,
baseAddHref
elementalAreaId,
} = this.props;

// Blocks can be either null or an empty array
Expand All @@ -51,12 +50,11 @@ class ElementList extends Component {
<div key={element.ID}>
<ElementComponent
element={element}
pageId={pageId}
editTabs={this.getEditTabs(element)}
link={element.BlockSchema.actions.edit}
/>
<HoverBarComponent
baseAddHref={baseAddHref}
elementalAreaId={elementalAreaId}
elementId={element.ID}
elementTypes={elementTypes}
/>
Expand Down Expand Up @@ -98,7 +96,7 @@ ElementList.propTypes = {
// @todo support either ElementList or Element children in an array (or both)
blocks: PropTypes.arrayOf(elementType),
loading: PropTypes.bool,
baseAddHref: PropTypes.string.isRequired,
elementalAreaId: PropTypes.number.isRequired,
};

ElementList.defaultProps = {
Expand Down
4 changes: 0 additions & 4 deletions client/src/components/ElementEditor/Header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@
width: unset;
}

&__version-state {
box-shadow: $gallery-item-shadow;
}

// Draft/modified state icons copied from asset-admin - see GalleryItem.scss
&__version-state {
border: 1px solid $state-draft;
Expand Down
72 changes: 38 additions & 34 deletions client/src/components/ElementEditor/HoverBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ class HoverBar extends Component {
this.setState({
timeoutRef,
});
} else { // mouse entered within 'instantaneous' hover area
} else {
// mouse entered within 'instantaneous' hover area
this.setState({
instAreaActive: true,
});
Expand All @@ -69,15 +70,14 @@ class HoverBar extends Component {
*
*/
handleMouseLeave() {
clearTimeout(this.state.timeoutRef);
clearTimeout(this.state.timeoutRef);

if (this.state.popoverOpen) {
return;
}
this.setState({ delayAreaActive: false, instAreaActive: false });
if (this.state.popoverOpen) {
return;
}
this.setState({ delayAreaActive: false, instAreaActive: false });
}


toggle() {
this.setState({
popoverOpen: !this.state.popoverOpen
Expand Down Expand Up @@ -117,45 +117,49 @@ class HoverBar extends Component {
* @returns {DOMElement}
*/
render() {
const { AddElementPopoverComponent, elementTypes, baseAddHref, elementId } = this.props;
const {
AddElementPopoverComponent,
elementTypes,
elementId,
elementalAreaId,
} = this.props;
const { popoverOpen } = this.state;

return (
<div className="element-editor__add-block-area-container" id={`AddBlockArea_${elementId}`}>
<div
className="element-editor__add-block-area"
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
>

{/* render the hover bar in the corresponding style */}
{
return (
<div className="element-editor__add-block-area-container" id={`AddBlockArea_${elementId}`}>
<div
className="element-editor__add-block-area"
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
>
{/* render the hover bar in the corresponding style */}
{
(this.state.delayAreaActive && this.renderHoverBar(false)) ||
(this.state.instAreaActive && this.renderHoverBar(true))
}

{/* render the popover */}
{(this.state.delayAreaActive || this.state.instAreaActive) &&
<AddElementPopoverComponent
placement="bottom-end"
target={`AddBlockHoverBar_${elementId}`}
isOpen={popoverOpen}
elementTypes={elementTypes}
baseAddHref={baseAddHref}
toggle={this.toggle}
container={`#AddBlockArea_${elementId}`}
/>
{/* render the popover */}
{(this.state.delayAreaActive || this.state.instAreaActive) &&
<AddElementPopoverComponent
placement="bottom-end"
target={`AddBlockHoverBar_${elementId}`}
isOpen={popoverOpen}
elementTypes={elementTypes}
toggle={this.toggle}
container={`#AddBlockArea_${elementId}`}
elementalAreaId={elementalAreaId}
insertAfterElement={elementId}
/>
}
</div>
</div>
</div>
</div>
);
}
}

HoverBar.propTypes = {
baseAddHref: PropTypes.string.isRequired,
elementTypes: PropTypes.arrayOf(elementTypeType).isRequired,
elementId: PropTypes.string.isRequired,
elementId: PropTypes.number.isRequired,
elementalAreaId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
};
export { HoverBar as Component };

Expand Down
2 changes: 1 addition & 1 deletion client/src/components/ElementEditor/HoverBar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
align-self: center;
width: 100%;
-webkit-appearance: button;
border: none;
border: 0;
position: absolute;
display: flex;
justify-content: flex-end;
Expand Down
Loading

0 comments on commit 4452874

Please sign in to comment.