Skip to content

Commit

Permalink
Merge pull request silverstripe#314 from creative-commoners/pulls/4.0…
Browse files Browse the repository at this point in the history
…/del-element-del

NEW Elements can be deleted via the inline delete button. Fix versioning state in readBlocks query
  • Loading branch information
robbieaverill authored Aug 13, 2018
2 parents 5a17153 + b9d89f3 commit 9d9cb7b
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 9d9cb7b

Please sign in to comment.