Skip to content

Commit

Permalink
NEW Elements can be deleted via the inline delete button. Fix version…
Browse files Browse the repository at this point in the history
…ing state in readBlocks query
  • Loading branch information
robbieaverill committed Aug 13, 2018
1 parent 5a17153 commit b9d89f3
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 14 deletions.
1 change: 1 addition & 0 deletions _config/graphql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ SilverStripe\GraphQL\Manager:
operations:
copyToStage: true
readOne: true
delete: true
# Expose access to Elements via Page -> ElementalArea -> Elements (see resolver)
DNADesign\Elemental\Models\ElementalArea:
fields: [ID]
Expand Down
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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 41 additions & 5 deletions client/src/components/ElementEditor/Element.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React, { PureComponent, PropTypes } from 'react';
/* global window */

import React, { Component, PropTypes } from 'react';
import { elementType } from 'types/elementType';
import { inject } from 'lib/Injector';
import i18n from 'i18n';
Expand All @@ -7,14 +9,41 @@ import i18n from 'i18n';
* The Element component used in the context of an ElementEditor shows the summary
* of an element's details when used in the CMS, including ID, Title and Summary.
*/
class Element extends PureComponent {
class Element extends Component {
constructor(props) {
super(props);

this.handleClick = this.handleClick.bind(this);
this.handleKeyUp = this.handleKeyUp.bind(this);
}

/**
* Take the user to the GridFieldDetailForm to edit the record
*/
handleClick() {
const { link } = this.props;

window.location = link;
}

/**
* If pressing enter key, treat it like a mouse click
*
* @param {Object} event
*/
handleKeyUp(event) {
if (event.keyCode === 13) {
this.handleClick();
}
}

render() {
const {
element: { ID, Title, BlockSchema },
HeaderComponent,
ContentComponent,
link,
} = this.props;

const linkTitle = i18n.inject(
i18n._t('ElementalElement.TITLE', 'Edit this {type} block'), { type: BlockSchema.type }
);
Expand All @@ -24,7 +53,14 @@ class Element extends PureComponent {
}

return (
<a className="element-editor__element" href={link} title={linkTitle}>
<span
className="element-editor__element"
onClick={this.handleClick}
onKeyUp={this.handleKeyUp}
role="button"
tabIndex={0}
title={linkTitle}
>
<HeaderComponent
id={ID}
title={Title}
Expand All @@ -36,7 +72,7 @@ class Element extends PureComponent {
fileTitle={BlockSchema.fileTitle}
content={BlockSchema.content}
/>
</a>
</span>
);
}
}
Expand Down
1 change: 1 addition & 0 deletions client/src/components/ElementEditor/Element.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.element-editor__element {
display: block;
color: inherit;
cursor: pointer;
padding: $panel-padding-x;

&:hover {
Expand Down
57 changes: 52 additions & 5 deletions client/src/components/ElementEditor/Header.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,51 @@
/* global confirm */

import React, { Component, PropTypes } from 'react';
import { Tooltip } from 'reactstrap';
import { compose } from 'redux';
import { inject } from 'lib/Injector';
import deleteBlockMutation from 'state/editor/deleteBlockMutation';
import i18n from 'i18n';

class Header extends Component {
constructor(props) {
super(props);

this.toggle = this.toggle.bind(this);
this.handleDelete = this.handleDelete.bind(this);

this.state = {
tooltipOpen: false
};
}

/**
* Handle the deletion of a block, passing the block ID in
*/
handleDelete(event) {
event.stopPropagation();

const { id, actions: { handleDeleteBlock } } = this.props;

const deleteMessage = i18n._t(
'ElementHeader.CONFIRM_DELETE',
'Are you sure you want to delete this block?'
);

// eslint-disable-next-line no-alert
if (handleDeleteBlock && confirm(deleteMessage)) {
handleDeleteBlock(id);
}
}

toggle() {
this.setState({
tooltipOpen: !this.state.tooltipOpen
});
}

render() {
const { id, title, elementType, fontIcon } = this.props;
const { id, title, elementType, fontIcon, FormActionComponent } = this.props;

return (
<div className="element-editor-header">
Expand All @@ -36,19 +63,39 @@ class Header extends Component {
</div>
<h3 className="element-editor-header__title">{title}</h3>
</div>
<FormActionComponent
onClick={this.handleDelete}
title={null}
icon="trash-bin"
attributes={{
title: i18n._t('ElementHeader.DELETE', 'Delete'),
}}
/>
</div>
);
}
}

Header.defaultProps = {
};

Header.propTypes = {
id: PropTypes.number,
title: PropTypes.string,
elementType: PropTypes.string,
fontIcon: PropTypes.string,
actions: PropTypes.shape({
handleDeleteBlock: PropTypes.func.isRequired,
}),
FormActionComponent: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
};

export default Header;
export { Header as Component };

export default compose(
inject(
['FormAction'],
(FormActionComponent) => ({
FormActionComponent,
}),
() => 'ElementEditor.ElementList.Element'
),
deleteBlockMutation
)(Header);
7 changes: 6 additions & 1 deletion client/src/components/ElementEditor/tests/Header-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
/* global jest, describe, it, expect */

import React from 'react';
import Header from '../Header';
import { Component as Header } from '../Header';
import Enzyme, { shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-15.4/build/index';

Enzyme.configure({ adapter: new Adapter() });

const mockFormAction = jest.fn();

describe('Header', () => {
describe('render()', () => {
it('should render the icon', () => {
Expand All @@ -17,6 +19,7 @@ describe('Header', () => {
title="Sample File Block"
elementType="File"
fontIcon="font-icon-block-file"
FormActionComponent={mockFormAction}
/>
);

Expand All @@ -32,6 +35,7 @@ describe('Header', () => {
title="Sample File Block"
elementType="File"
fontIcon="font-icon-block-file"
FormActionComponent={mockFormAction}
/>
);

Expand All @@ -46,6 +50,7 @@ describe('Header', () => {
title="Sample File Block"
elementType="File"
fontIcon="font-icon-block-file"
FormActionComponent={mockFormAction}
/>
);

Expand Down
34 changes: 34 additions & 0 deletions client/src/state/editor/deleteBlockMutation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';

// GraphQL query for deleting a specific block
const mutation = gql`
mutation DeleteBlock($blockId: ID!) {
deleteBlock(IDs: [$blockId]) {
ID
}
}
`;

const config = {
props: ({ mutate, ownProps: { actions } }) => {
const handleDeleteBlock = (blockId) => mutate({
variables: { blockId },
});

return {
actions: {
...actions,
handleDeleteBlock,
},
};
},
options: {
// Refetch versions after mutation is completed
refetchQueries: ['ReadBlocksForPage']
}
};

export { mutation, config };

export default graphql(mutation, config);
2 changes: 1 addition & 1 deletion client/src/state/editor/readBlocksForPageQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import gql from 'graphql-tag';
const query = gql`
query ReadBlocksForPage($id:ID!) {
readOnePage(ID: $id, Versioning: {
Mode: LATEST
Mode: DRAFT
}){
ID
ElementalAreaIfExists {
Expand Down

0 comments on commit b9d89f3

Please sign in to comment.