Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NEW Show CMS field (top-level) tabs in more actions dropdown #373

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions _config/cache.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
Name: elementalcache
---
SilverStripe\Core\Injector\Injector:
Psr\SimpleCache\CacheInterface.ElementTabCache:
factory: SilverStripe\Core\Cache\CacheFactory
constructor:
namespace: 'ElementTabCache'
DNADesign\Elemental\Services\ElementTabProvider:
properties:
cache: '%$Psr\SimpleCache\CacheInterface.ElementTabCache'
2 changes: 1 addition & 1 deletion client/dist/js/bundle.js

Large diffs are not rendered by default.

14 changes: 3 additions & 11 deletions client/src/components/ElementEditor/AddElementPopoverContent.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/* global window */

import React, { Component, PropTypes } from 'react';
import { Button } from 'reactstrap';
import classNames from 'classnames';
import { elementTypeType } from 'types/elementTypeType';

/**
* The AddElementPopoverContent component used in the context of an ElementEditor shows the
Expand All @@ -28,7 +27,7 @@ class AddElementPopoverContent extends Component {
'element-editor-add-element-content__button'
)
}
key={elementType.ID}
key={elementType.name}
>
{elementType.title}
</Button>
Expand All @@ -47,14 +46,7 @@ class AddElementPopoverContent extends Component {
}
}
AddElementPopoverContent.propTypes = {
elementTypes: PropTypes.arrayOf(PropTypes.shape({
title: PropTypes.string,
icon: PropTypes.string,
})),
};

AddElementPopoverContent.defaultProps = {

elementTypes: PropTypes.arrayOf(elementTypeType),
};

export default AddElementPopoverContent;
9 changes: 5 additions & 4 deletions client/src/components/ElementEditor/AddNewButton.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { Component, PropTypes } from 'react';
import { InputGroup, Input, InputGroupAddon, Button } from 'reactstrap';
import i18n from 'i18n';
import { elementTypeType } from 'types/elementTypeType';

class AddNewButton extends Component {
constructor(props) {
Expand All @@ -20,7 +21,7 @@ class AddNewButton extends Component {
handleTypeChange(event) {
const type = event.target
? this.props.elementTypes.find(
candidateType => candidateType.value === event.target.value
candidateType => candidateType.name === event.target.value
) || null
: null;

Expand All @@ -35,7 +36,7 @@ class AddNewButton extends Component {
*/
renderAddButton() {
const { selectedType } = this.state;
const buttonHref = selectedType ? `${this.props.baseAddHref}/${selectedType.value}` : '#';
const buttonHref = selectedType ? `${this.props.baseAddHref}/${selectedType.name}` : '#';
const title = selectedType
? i18n.inject(i18n._t('ElementalAddNewButton.TITLE', 'Add a "{type}" block'), { type: selectedType.title })
: '';
Expand Down Expand Up @@ -67,7 +68,7 @@ class AddNewButton extends Component {
}

return elementTypes.map(type => (
<option key={type.value} value={type.value}>{type.title}</option>
<option key={type.name} value={type.name}>{type.title}</option>
));
}

Expand Down Expand Up @@ -97,7 +98,7 @@ class AddNewButton extends Component {
AddNewButton.defaultProps = {};
AddNewButton.propTypes = {
baseAddHref: PropTypes.string.isRequired,
elementTypes: PropTypes.array.isRequired,
elementTypes: PropTypes.arrayOf(elementTypeType).isRequired,
};

export default AddNewButton;
3 changes: 3 additions & 0 deletions client/src/components/ElementEditor/Element.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class Element extends Component {
HeaderComponent,
ContentComponent,
link,
editTabs,
} = this.props;

const { previewExpanded } = this.state;
Expand Down Expand Up @@ -118,6 +119,7 @@ class Element extends Component {
elementType={element.BlockSchema.type}
fontIcon={element.BlockSchema.iconClass}
link={link}
editTabs={editTabs}
caretClickCallback={this.handleExpand}
previewExpanded={previewExpanded}
expandable={element.InlineEditable}
Expand All @@ -136,6 +138,7 @@ class Element extends Component {
Element.propTypes = {
element: elementType,
link: PropTypes.string.isRequired,
editTabs: PropTypes.arrayOf(PropTypes.string),
};

Element.defaultProps = {
Expand Down
3 changes: 2 additions & 1 deletion client/src/components/ElementEditor/ElementEditor.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { PureComponent, PropTypes } from 'react';
import { inject } from 'lib/Injector';
import { elementTypeType } from 'types/elementTypeType';

/**
* The ElementEditor is used in the CMS to manage a list or nested lists of
Expand All @@ -19,7 +20,7 @@ class ElementEditor extends PureComponent {
}

ElementEditor.propTypes = {
elementTypes: PropTypes.array.isRequired,
elementTypes: PropTypes.arrayOf(elementTypeType).isRequired,
pageId: PropTypes.number.isRequired,
baseAddHref: PropTypes.string.isRequired,
};
Expand Down
23 changes: 22 additions & 1 deletion client/src/components/ElementEditor/ElementList.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,27 @@ import React, { Component, PropTypes } from 'react';
import { elementType } from 'types/elementType';
import { inject } from 'lib/Injector';
import AddElementPopoverContent from 'components/ElementEditor/AddElementPopoverContent';
import { elementTypeType } from 'types/elementTypeType';

class ElementList extends Component {
/**
* Given an elementType, return a list of tabs that should be available in the edit form for an
* element.
*
* @param {elementTypeType} element
* @returns {string[]}
*/
getEditTabs(element) {
const { elementTypes } = this.props;
const matchingType = elementTypes.find(type => element.BlockSchema.type === type.title);

if (!matchingType || !matchingType.tabs) {
return [];
}

return matchingType.tabs;
}

/**
* Renders a list of Element components, each with an elementType object
* of data mapped into it. The data is provided by a GraphQL HOC registered
Expand All @@ -16,10 +35,12 @@ class ElementList extends Component {
return null;
}


return blocks.map((element) => (
<ElementComponent
key={element.ID}
element={element}
editTabs={this.getEditTabs(element)}
link={element.BlockSchema.actions.edit}
/>
));
Expand Down Expand Up @@ -54,7 +75,7 @@ class ElementList extends Component {
ElementList.propTypes = {
// @todo support either ElementList or Element children in an array (or both)
blocks: PropTypes.arrayOf(elementType),
elementTypes: PropTypes.array.isRequired,
elementTypes: PropTypes.arrayOf(elementTypeType).isRequired,
loading: PropTypes.bool,
};

Expand Down
22 changes: 20 additions & 2 deletions client/src/components/ElementEditor/Header.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* global confirm */

import React, { Component, PropTypes } from 'react';
import { Tooltip } from 'reactstrap';
import { Tooltip, DropdownItem } from 'reactstrap';
import { compose } from 'redux';
import { inject } from 'lib/Injector';
import archiveBlockMutation from 'state/editor/archiveBlockMutation';
Expand Down Expand Up @@ -155,7 +155,7 @@ class Header extends Component {
* @returns {ActionMenuComponent|null}
*/
renderActionsMenu() {
const { id, expandable, ActionMenuComponent } = this.props;
const { id, expandable, editTabs, ActionMenuComponent } = this.props;

// Don't show the menu when inline editing is not enabled
if (!expandable) {
Expand All @@ -171,6 +171,8 @@ class Header extends Component {
dropdownMenuProps={{ right: true }}
toggleCallback={(event) => event.stopPropagation()}
>
{ this.renderEditTabs() }
{ !editTabs || !editTabs.length || <DropdownItem divider /> }
<button
onClick={this.handleArchive}
title={archiveTitle}
Expand All @@ -185,6 +187,21 @@ class Header extends Component {
);
}

/**
* Render buttons for the edit form tabs that will be a part of the edit form (if they exist)
*
* @returns {DOMElement[]|null}
*/
renderEditTabs() {
robbieaverill marked this conversation as resolved.
Show resolved Hide resolved
const { editTabs } = this.props;

if (!editTabs || !editTabs.length) {
return null;
}

return editTabs.map((tab) => <button key={tab} className="dropdown-item">{tab}</button>);
ScopeyNZ marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Renders a message indicating the current versioned state of the element
*
Expand Down Expand Up @@ -281,6 +298,7 @@ Header.propTypes = {
isPublished: PropTypes.bool,
elementType: PropTypes.string,
fontIcon: PropTypes.string,
editTabs: PropTypes.arrayOf(PropTypes.string),
actions: PropTypes.shape({
handleArchiveBlock: PropTypes.func.isRequired,
handlePublishBlock: PropTypes.func,
Expand Down
3 changes: 2 additions & 1 deletion client/src/components/ElementEditor/Toolbar.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { PureComponent, PropTypes } from 'react';
import { inject } from 'lib/Injector';
import { elementTypeType } from 'types/elementTypeType';

// eslint-disable-next-line react/prefer-stateless-function
class Toolbar extends PureComponent {
Expand All @@ -15,7 +16,7 @@ class Toolbar extends PureComponent {

Toolbar.defaultProps = {};
Toolbar.propTypes = {
elementTypes: PropTypes.array.isRequired,
elementTypes: PropTypes.arrayOf(elementTypeType).isRequired,
baseAddHref: PropTypes.string.isRequired,
AddNewButtonComponent: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ Enzyme.configure({ adapter: new Adapter() });
describe('ElementEditor', () => {
const ToolbarComponent = () => <div />;
const ListComponent = () => <div className="elemental-editor__list" />;
const testElementType = {
name: 'TestElement',
title: 'Test Block',
icon: 'nothing',
tabs: ['Content', 'History'],
};

describe('render()', () => {
it('should render ElementList and Toolbar', () => {
Expand All @@ -20,7 +26,7 @@ describe('ElementEditor', () => {
ListComponent={ListComponent}
pageId={8}
baseAddHref="#"
elementTypes={['TestElement']}
elementTypes={[testElementType]}
/>
);

Expand Down
20 changes: 20 additions & 0 deletions client/src/components/ElementEditor/tests/Header-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Enzyme.configure({ adapter: new Adapter() });

describe('Header', () => {
const ActionMenuComponent = () => <div />;
const testTabs = ['Content', 'Settings', 'History'];

describe('render()', () => {
it('should render the icon', () => {
Expand All @@ -35,6 +36,7 @@ describe('Header', () => {
title="Sample File Block"
elementType="File"
fontIcon="font-icon-block-file"
editTabs={testTabs}
ActionMenuComponent={ActionMenuComponent}
/>
);
Expand All @@ -50,6 +52,7 @@ describe('Header', () => {
title="Sample File Block"
elementType="File"
fontIcon="font-icon-block-file"
editTabs={testTabs}
ActionMenuComponent={ActionMenuComponent}
/>
);
Expand Down Expand Up @@ -123,6 +126,23 @@ describe('Header', () => {

expect(wrapper.text()).not.toContain('ActionMenuComponent');
});

it('should render the given "edit tabs" in the action menu', () => {
const wrapper = shallow(
<Header
expandable
editTabs={testTabs}
ActionMenuComponent={ActionMenuComponent}
/>
);

// See the dropdown separator
expect(wrapper.find(ActionMenuComponent).children().find('DropdownItem').length).toBe(1);
// See all the relevant action menu options
expect(wrapper.find(ActionMenuComponent).children().map(node => node.text())).toEqual(
expect.arrayContaining(testTabs)
);
});
});

describe('renderVersionedStateMessage()', () => {
Expand Down
11 changes: 11 additions & 0 deletions client/src/types/elementTypeType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { PropTypes } from 'react';

// Describes the structure of an element coming in via GraphQL
const elementTypeType = PropTypes.shape({
name: PropTypes.string,
title: PropTypes.string,
icon: PropTypes.string,
tabs: PropTypes.arrayOf(PropTypes.string),
});

export { elementTypeType };
8 changes: 7 additions & 1 deletion src/Forms/ElementalAreaField.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use DNADesign\Elemental\Models\BaseElement;
use DNADesign\Elemental\Models\ElementalArea;
use DNADesign\Elemental\Services\ElementTabProvider;
use SilverStripe\Control\Controller;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector;
Expand Down Expand Up @@ -115,11 +116,16 @@ public function getSchemaDataDefaults()

$blockTypes = [];

// Use the internal (temporary) provider to get cached tab names.
/** @var ElementTabProvider $tabProvider */
$tabProvider = Injector::inst()->get(ElementTabProvider::class);
robbieaverill marked this conversation as resolved.
Show resolved Hide resolved

foreach ($this->getTypes() as $className => $blockTitle) {
$blockTypes[] = [
'value' => str_replace('\\', '-', $className),
'name' => str_replace('\\', '-', $className),
'title' => $blockTitle,
'icon' => Config::inst()->get($className, 'icon'),
'tabs' => array_values($tabProvider->getTabsForElement($className)),
];
}

Expand Down
Loading