From 385e9e105c20d42687cd6713d975a1b45e28e32c Mon Sep 17 00:00:00 2001 From: Luke Edwards Date: Tue, 29 May 2018 16:10:52 +1200 Subject: [PATCH] Create GridField Actions Menu component (#8083) * WIP GridField action menu work, the gist of the idea is using a new gridfield component * Add delete action to actions menu * Actions are added automatically to action menu (allows for extension) * Add test and minor changes * Add docs and minor changes * Refactor ActionMenuItem into distinct types, general ActionMenu cleanup * Add icons and fix title * Pass columnName, so it can be used by components * Update test to open and find action menu buttons * Add section in changelog upgrade section for GridField_ActionMenu --- .../03_Forms/Field_types/04_GridField.md | 28 ++++ .../04_Create_a_GridField_ActionProvider.md | 45 ++++++ docs/en/04_Changelogs/4.2.0.md | 23 +++ lang/en.yml | 2 +- .../GridFieldConfig_RecordEditor.php | 3 +- .../GridFieldConfig_RelationEditor.php | 1 + src/Forms/GridField/GridFieldDeleteAction.php | 138 +++++++++++++----- src/Forms/GridField/GridFieldEditButton.php | 51 ++++++- src/Forms/GridField/GridFieldViewButton.php | 16 +- src/Forms/GridField/GridField_ActionMenu.php | 101 +++++++++++++ .../GridField/GridField_ActionMenuItem.php | 53 +++++++ .../GridField/GridField_ActionMenuLink.php | 21 +++ .../Forms/GridField/GridFieldEditButton.ss | 4 +- .../Forms/GridField/GridFieldViewButton.ss | 4 +- .../Forms/GridField/GridField_ActionMenu.ss | 1 + tests/behat/src/CmsFormsContext.php | 6 +- .../GridField/GridFieldActionMenuTest.php | 94 ++++++++++++ 17 files changed, 532 insertions(+), 59 deletions(-) create mode 100644 src/Forms/GridField/GridField_ActionMenu.php create mode 100644 src/Forms/GridField/GridField_ActionMenuItem.php create mode 100644 src/Forms/GridField/GridField_ActionMenuLink.php create mode 100644 templates/SilverStripe/Forms/GridField/GridField_ActionMenu.ss create mode 100644 tests/php/Forms/GridField/GridFieldActionMenuTest.php diff --git a/docs/en/02_Developer_Guides/03_Forms/Field_types/04_GridField.md b/docs/en/02_Developer_Guides/03_Forms/Field_types/04_GridField.md index 1e76e925c..e5f50bee3 100644 --- a/docs/en/02_Developer_Guides/03_Forms/Field_types/04_GridField.md +++ b/docs/en/02_Developer_Guides/03_Forms/Field_types/04_GridField.md @@ -157,6 +157,7 @@ $component = $config->getComponentByType(GridFieldFilterHeader::class) Here is a list of components for use bundled with the core framework. Many more components are provided by third-party modules and extensions. + - [GridField_ActionMenu](api:SilverStripe\Forms\GridField\GridField_ActionMenu) - [GridFieldToolbarHeader](api:SilverStripe\Forms\GridField\GridFieldToolbarHeader) - [GridFieldSortableHeader](api:SilverStripe\Forms\GridField\GridFieldSortableHeader) - [GridFieldFilterHeader](api:SilverStripe\Forms\GridField\GridFieldFilterHeader) @@ -266,6 +267,33 @@ This configuration adds the ability to searched for existing records and add a r Records created or deleted through the `GridFieldConfig_RelationEditor` automatically update the relationship in the database. +## GridField_ActionMenu + +The `GridField_ActionMenu` component provides a dropdown menu which automatically bundles GridField actions into a react based dropdown. It is included by default on `GridFieldConfig_RecordEditor` and `GridFieldConfig_RelationEditor` configs. + +To add it to a GridField, add the `GridField_ActionMenu` component and any action(s) that implement `GridField_ActionMenuItem` (such as `GridFieldEditButton` or `GridFieldDeleteAction`) to the `GridFieldConfig`. + +```php +use SilverStripe\Forms\GridField\GridFieldConfig; +use SilverStripe\Forms\GridField\GridFieldDataColumns; + +// `GridFieldConfig::create()` will create an empty configuration (no components). +$config = GridFieldConfig::create(); + +// add a component +$config->addComponent(); + +$config->addComponents( + new GridFieldDataColumns(), + new GridFieldEditButton(), + new GridField_ActionMenu() +); + +// Update the GridField with our custom configuration +$gridField->setConfig($config); + +``` + ## GridFieldDetailForm The `GridFieldDetailForm` component drives the record viewing and editing form. It takes its' fields from diff --git a/docs/en/02_Developer_Guides/03_Forms/How_Tos/04_Create_a_GridField_ActionProvider.md b/docs/en/02_Developer_Guides/03_Forms/How_Tos/04_Create_a_GridField_ActionProvider.md index a8d469d25..33f056048 100644 --- a/docs/en/02_Developer_Guides/03_Forms/How_Tos/04_Create_a_GridField_ActionProvider.md +++ b/docs/en/02_Developer_Guides/03_Forms/How_Tos/04_Create_a_GridField_ActionProvider.md @@ -153,6 +153,51 @@ called method is available as a parameter. To finish off our basic example, the `handleAction` method simply returns a message to the user interface indicating a successful message. +## Add the GridFieldCustomAction to the `GridField_ActionMenu` + +For an action to be included in the action menu dropdown, which appears on each row if `GridField_ActionMenu` is included in the `GridFieldConfig`, it must implement `GridField_ActionMenuItem` and relevant `get` functions to provide information to the frontend react action menu component. + +## Basic GridFieldCustomAction boilerplate implementing GridField_ActionMenuItem + +```php +use SilverStripe\Forms\GridField\GridField_ColumnProvider; +use SilverStripe\Forms\GridField\GridField_ActionProvider; +use SilverStripe\Forms\GridField\GridField_ActionMenuItem; +use SilverStripe\Forms\GridField\GridField_FormAction; +use SilverStripe\Control\Controller; + +class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_ActionProvider, GridField_ActionMenuItem +{ + + public function augmentColumns($gridField, &$columns) + { + if(!in_array('Actions', $columns)) { + $columns[] = 'Actions'; + } + } + + public function getTitle($gridField, $record) + { + return _t(__CLASS__ . '.Delete', "Delete"); + } + + public function getGroup($gridField, $record) + { + return GridField_ActionMenuItem::DEFAULT_GROUP; + } + + public function getExtraData($gridField, $record, $columnName) + { + if ($field) { + return $field->getAttributes(); + } + + return null; + } + + // ...rest of boilerplate code +``` + ## Related * [GridField Reference](/developer_guides/forms/field_types/gridfield) diff --git a/docs/en/04_Changelogs/4.2.0.md b/docs/en/04_Changelogs/4.2.0.md index 4bb9a41f8..bd6ebc7db 100644 --- a/docs/en/04_Changelogs/4.2.0.md +++ b/docs/en/04_Changelogs/4.2.0.md @@ -148,3 +148,26 @@ cd ~/my-project-root upgrade-code environment --write upgrade-code reorganise --write ``` + +### New GridField Action Menu + +A new `GridField_ActionMenu` is included by default in GridFields configured with `GridFieldConfig_RecordEditor` +or `GridFieldConfig_RelationEditor`. +In addition to this `GridFieldDeleteAction` and `GridFieldEditButton` now implement `GridField_ActionMenuItem`, +this means that any GridField that uses a config of or based on `GridFieldConfig_RecordEditor` +or `GridFieldConfig_RelationEditor` will have an action menu on each item row with +the 'Delete/Unlink' and 'Edit' actions moved into it. + +If you wish to opt out of having this menu and the respective actions moved into it, you can remove the `GridField_ActionMenu` +component from the config that is passed into your GridField. + +```php +// method 1: removing GridField_ActionMenu from a new GridField +$config = GridFieldConfig_RecordEditor::create(); +$config->removeComponentsByType(GridField_ActionMenu); + +$gridField = new GridField('Teams', 'Teams', $this->Teams(), $config); + +// method 2: removing GridField_ActionMenu from an existing GridField +$gridField->getConfig()->removeComponentsByType(GridField_ActionMenu); +``` diff --git a/lang/en.yml b/lang/en.yml index 2f1ee75e0..dc3b2042e 100644 --- a/lang/en.yml +++ b/lang/en.yml @@ -98,7 +98,7 @@ en: DeletePermissionsFailure: 'No delete permissions' Deleted: 'Deleted {type} {name}' Save: Save - SilverStripe\Forms\GridField\GridFieldEditButton_ss: + SilverStripe\Forms\GridField\GridFieldEditButton: EDIT: Edit SilverStripe\Forms\GridField\GridFieldGroupDeleteAction: UnlinkSelfFailure: 'Cannot remove yourself from this group, you will lose admin rights' diff --git a/src/Forms/GridField/GridFieldConfig_RecordEditor.php b/src/Forms/GridField/GridFieldConfig_RecordEditor.php index c67d116bb..b06e4a032 100644 --- a/src/Forms/GridField/GridFieldConfig_RecordEditor.php +++ b/src/Forms/GridField/GridFieldConfig_RecordEditor.php @@ -22,7 +22,8 @@ class GridFieldConfig_RecordEditor extends GridFieldConfig $this->addComponent($filter = new GridFieldFilterHeader()); $this->addComponent(new GridFieldDataColumns()); $this->addComponent(new GridFieldEditButton()); - $this->addComponent(new GridFieldDeleteAction()); + $this->addComponent(new GridFieldDeleteAction(true)); + $this->addComponent(new GridField_ActionMenu()); $this->addComponent(new GridFieldPageCount('toolbar-header-right')); $this->addComponent($pagination = new GridFieldPaginator($itemsPerPage)); $this->addComponent(new GridFieldDetailForm()); diff --git a/src/Forms/GridField/GridFieldConfig_RelationEditor.php b/src/Forms/GridField/GridFieldConfig_RelationEditor.php index 1cf4c29c3..83cc40004 100644 --- a/src/Forms/GridField/GridFieldConfig_RelationEditor.php +++ b/src/Forms/GridField/GridFieldConfig_RelationEditor.php @@ -38,6 +38,7 @@ class GridFieldConfig_RelationEditor extends GridFieldConfig $this->addComponent(new GridFieldDataColumns()); $this->addComponent(new GridFieldEditButton()); $this->addComponent(new GridFieldDeleteAction(true)); + $this->addComponent(new GridField_ActionMenu()); $this->addComponent(new GridFieldPageCount('toolbar-header-right')); $this->addComponent($pagination = new GridFieldPaginator($itemsPerPage)); $this->addComponent(new GridFieldDetailForm()); diff --git a/src/Forms/GridField/GridFieldDeleteAction.php b/src/Forms/GridField/GridFieldDeleteAction.php index 513109b17..a0b46ff62 100644 --- a/src/Forms/GridField/GridFieldDeleteAction.php +++ b/src/Forms/GridField/GridFieldDeleteAction.php @@ -2,6 +2,7 @@ namespace SilverStripe\Forms\GridField; +use SilverStripe\Control\Controller; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\ValidationException; @@ -21,7 +22,7 @@ use SilverStripe\ORM\ValidationException; * $action = new GridFieldDeleteAction(true); * */ -class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_ActionProvider +class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_ActionProvider, GridField_ActionMenuItem { /** @@ -44,6 +45,47 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio $this->setRemoveRelation($removeRelation); } + /** + * @inheritdoc + */ + public function getTitle($gridField, $record, $columnName) + { + $field = $this->getRemoveAction($gridField, $record, $columnName); + + if ($field) { + return $field->getAttribute('title'); + } + + return _t(__CLASS__ . '.Delete', "Delete"); + } + + /** + * @inheritdoc + */ + public function getGroup($gridField, $record, $columnName) + { + return GridField_ActionMenuItem::DEFAULT_GROUP; + } + + /** + * + * @param GridField $gridField + * @param DataObject $record + * @param string $columnName + * @return string|null the attribles for the action + */ + public function getExtraData($gridField, $record, $columnName) + { + + $field = $this->getRemoveAction($gridField, $record, $columnName); + + if ($field) { + return $field->getAttributes(); + } + + return null; + } + /** * Add a column 'Delete' * @@ -67,7 +109,7 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio */ public function getColumnAttributes($gridField, $record, $columnName) { - return array('class' => 'grid-field__col-compact'); + return ['class' => 'grid-field__col-compact']; } /** @@ -80,7 +122,7 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio public function getColumnMetadata($gridField, $columnName) { if ($columnName == 'Actions') { - return array('title' => ''); + return ['title' => '']; } } @@ -92,7 +134,7 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio */ public function getColumnsHandled($gridField) { - return array('Actions'); + return ['Actions']; } /** @@ -103,7 +145,7 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio */ public function getActions($gridField) { - return array('deleterecord', 'unlinkrelation'); + return ['deleterecord', 'unlinkrelation']; } /** @@ -111,43 +153,17 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio * @param GridField $gridField * @param DataObject $record * @param string $columnName - * @return string the HTML for the column + * @return string|null the HTML for the column */ public function getColumnContent($gridField, $record, $columnName) { - if ($this->getRemoveRelation()) { - if (!$record->canEdit()) { - return null; - } - $title = _t(__CLASS__ . '.UnlinkRelation', "Unlink"); + $field = $this->getRemoveAction($gridField, $record, $columnName); - $field = GridField_FormAction::create( - $gridField, - 'UnlinkRelation' . $record->ID, - false, - "unlinkrelation", - array('RecordID' => $record->ID) - ) - ->addExtraClass('btn btn--no-text btn--icon-md font-icon-link-broken grid-field__icon-action gridfield-button-unlink') - ->setAttribute('title', $title) - ->setAttribute('aria-label', $title); - } else { - if (!$record->canDelete()) { - return null; - } - - $field = GridField_FormAction::create( - $gridField, - 'DeleteRecord' . $record->ID, - false, - "deleterecord", - array('RecordID' => $record->ID) - ) - ->addExtraClass('gridfield-button-delete btn--icon-md font-icon-trash-bin btn--no-text grid-field__icon-action') - ->setAttribute('title', _t(__CLASS__ . '.Delete', "Delete")) - ->setDescription(_t(__CLASS__ . '.DELETE_DESCRIPTION', 'Delete')); + if ($field) { + return $field->Field(); } - return $field->Field(); + + return null; } /** @@ -188,6 +204,54 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio } } + /** + * + * @param GridField $gridField + * @param DataObject $record + * @param string $columnName + * @return GridField_FormAction|null + */ + private function getRemoveAction($gridField, $record, $columnName) + { + if ($this->getRemoveRelation()) { + if (!$record->canEdit()) { + return null; + } + $title = _t(__CLASS__ . '.UnlinkRelation', "Unlink"); + + $field = GridField_FormAction::create( + $gridField, + 'UnlinkRelation' . $record->ID, + false, + "unlinkrelation", + ['RecordID' => $record->ID] + ) + ->addExtraClass('btn btn--no-text btn--icon-md font-icon-link-broken grid-field__icon-action gridfield-button-unlink action-menu--handled') + ->setAttribute('classNames', 'gridfield-button-unlink font-icon-link-broken') + ->setDescription($title) + ->setAttribute('aria-label', $title); + } else { + if (!$record->canDelete()) { + return null; + } + $title = _t(__CLASS__ . '.Delete', "Delete"); + + $field = GridField_FormAction::create( + $gridField, + 'DeleteRecord' . $record->ID, + false, + "deleterecord", + ['RecordID' => $record->ID] + ) + ->addExtraClass('gridfield-button-delete btn--icon-md font-icon-trash-bin btn--no-text grid-field__icon-action action-menu--handled') + ->setAttribute('classNames', 'gridfield-button-delete font-icon-trash') + ->setDescription($title) + ->setAttribute('aria-label', $title); + } + + return $field; + } + /** * Get whether to remove or delete the relation * diff --git a/src/Forms/GridField/GridFieldEditButton.php b/src/Forms/GridField/GridFieldEditButton.php index 10efe1d45..e56ed330f 100644 --- a/src/Forms/GridField/GridFieldEditButton.php +++ b/src/Forms/GridField/GridFieldEditButton.php @@ -17,7 +17,7 @@ use SilverStripe\View\SSViewer; * The default routing applies to the {@link GridFieldDetailForm} component, * which has to be added separately to the {@link GridField} configuration. */ -class GridFieldEditButton implements GridField_ColumnProvider +class GridFieldEditButton implements GridField_ColumnProvider, GridField_ActionProvider, GridField_ActionMenuLink { /** * HTML classes to be added to GridField edit buttons @@ -27,9 +27,44 @@ class GridFieldEditButton implements GridField_ColumnProvider protected $extraClass = [ 'grid-field__icon-action--hidden-on-hover' => true, 'font-icon-edit' => true, - 'btn--icon-large' => true + 'btn--icon-large' => true, + 'action-menu--handled' => true ]; + /** + * @inheritdoc + */ + public function getTitle($gridField, $record, $columnName) + { + return _t(__CLASS__ . '.EDIT', "Edit"); + } + + /** + * @inheritdoc + */ + public function getGroup($gridField, $record, $columnName) + { + return GridField_ActionMenuItem::DEFAULT_GROUP; + } + + /** + * @inheritdoc + */ + public function getExtraData($gridField, $record, $columnName) + { + return [ + "classNames" => "font-icon-edit action-detail edit-link" + ]; + } + + /** + * @inheritdoc + */ + public function getUrl($gridField, $record, $columnName) + { + return Controller::join_links($gridField->Link('item'), $record->ID, 'edit'); + } + /** * Add a column 'Delete' * @@ -53,7 +88,7 @@ class GridFieldEditButton implements GridField_ColumnProvider */ public function getColumnAttributes($gridField, $record, $columnName) { - return array('class' => 'grid-field__col-compact'); + return ['class' => 'grid-field__col-compact']; } /** @@ -66,7 +101,7 @@ class GridFieldEditButton implements GridField_ColumnProvider public function getColumnMetadata($gridField, $columnName) { if ($columnName == 'Actions') { - return array('title' => ''); + return ['title' => '']; } return []; } @@ -79,7 +114,7 @@ class GridFieldEditButton implements GridField_ColumnProvider */ public function getColumnsHandled($gridField) { - return array('Actions'); + return ['Actions']; } /** @@ -90,7 +125,7 @@ class GridFieldEditButton implements GridField_ColumnProvider */ public function getActions($gridField) { - return array(); + return []; } /** @@ -104,10 +139,10 @@ class GridFieldEditButton implements GridField_ColumnProvider // No permission checks, handled through GridFieldDetailForm, // which can make the form readonly if no edit permissions are available. - $data = new ArrayData(array( + $data = new ArrayData([ 'Link' => Controller::join_links($gridField->Link('item'), $record->ID, 'edit'), 'ExtraClass' => $this->getExtraClass() - )); + ]); $template = SSViewer::get_templates_by_class($this, '', __CLASS__); return $data->renderWith($template); diff --git a/src/Forms/GridField/GridFieldViewButton.php b/src/Forms/GridField/GridFieldViewButton.php index 8d91e41fe..cd5c32aff 100644 --- a/src/Forms/GridField/GridFieldViewButton.php +++ b/src/Forms/GridField/GridFieldViewButton.php @@ -14,16 +14,16 @@ use SilverStripe\View\SSViewer; class GridFieldViewButton implements GridField_ColumnProvider { - public function augmentColumns($field, &$cols) + public function augmentColumns($field, &$columns) { - if (!in_array('Actions', $cols)) { - $cols[] = 'Actions'; + if (!in_array('Actions', $columns)) { + $columns[] = 'Actions'; } } public function getColumnsHandled($field) { - return array('Actions'); + return ['Actions']; } public function getColumnContent($field, $record, $col) @@ -31,20 +31,20 @@ class GridFieldViewButton implements GridField_ColumnProvider if (!$record->canView()) { return null; } - $data = new ArrayData(array( + $data = new ArrayData([ 'Link' => Controller::join_links($field->Link('item'), $record->ID, 'view') - )); + ]); $template = SSViewer::get_templates_by_class($this, '', __CLASS__); return $data->renderWith($template); } public function getColumnAttributes($field, $record, $col) { - return array('class' => 'grid-field__col-compact'); + return ['class' => 'grid-field__col-compact']; } public function getColumnMetadata($gridField, $col) { - return array('title' => null); + return ['title' => null]; } } diff --git a/src/Forms/GridField/GridField_ActionMenu.php b/src/Forms/GridField/GridField_ActionMenu.php new file mode 100644 index 000000000..5c3b07b2f --- /dev/null +++ b/src/Forms/GridField/GridField_ActionMenu.php @@ -0,0 +1,101 @@ +getItems($gridField); + + if (!$items) { + return null; + } + + $schema = array_map(function (GridField_ActionMenuItem $item) use ($gridField, $record, $columnName) { + return [ + 'type' => $item instanceof GridField_ActionMenuLink ? 'link' : 'submit', + 'title' => $item->getTitle($gridField, $record, $columnName), + 'url' => $item instanceof GridField_ActionMenuLink ? $item->getUrl($gridField, $record, $columnName) : null, + 'group' => $item->getGroup($gridField, $record, $columnName), + 'data' => $item->getExtraData($gridField, $record, $columnName), + ]; + }, $items); + + $templateData = ArrayData::create([ + 'Schema' => Convert::raw2json($schema), + ]); + + $template = SSViewer::get_templates_by_class($this, '', static::class); + + return $templateData->renderWith($template); + } + + public function getColumnAttributes($gridField, $record, $columnName) + { + return ['class' => 'grid-field__col-compact action-menu']; + } + + public function getColumnMetadata($gridField, $columnName) + { + return ['title' => null]; + } + + public function getActions($gridField) + { + $actions = []; + + foreach ($this->getItems($gridField) as $item) { + if ($item instanceof GridField_ActionProvider) { + $actions = array_merge($actions, $item->getActions($gridField)); + } + } + + return $actions; + } + + public function handleAction(GridField $gridField, $actionName, $arguments, $data) + { + foreach ($this->getItems($gridField) as $item) { + $actions = []; + if ($item instanceof GridField_ActionProvider) { + $actions = $item->getActions($gridField); + } + + if (in_array($actionName, $actions)) { + $item->handleAction($gridField, $actionName, $arguments, $data); + } + } + } + + /** + * Gets the list of items setup + * + * @return array + */ + public function getItems($gridfield) + { + $items = $gridfield->config->getComponentsByType(GridField_ActionMenuItem::class)->items; + + return $items; + } +} diff --git a/src/Forms/GridField/GridField_ActionMenuItem.php b/src/Forms/GridField/GridField_ActionMenuItem.php new file mode 100644 index 000000000..a4eb9fb26 --- /dev/null +++ b/src/Forms/GridField/GridField_ActionMenuItem.php @@ -0,0 +1,53 @@ +getColumnContent()} + * + * @param GridField $gridField + * @param DataObject $record + * + * @return string $title + */ + public function getTitle($gridField, $record, $columnName); + + /** + * Gets any extra data that could go in to the schema that the menu generates + * + * @see {@link GridField_ActionMenu->getColumnContent()} + * + * @param GridField $gridField + * @param DataObject $record + * + * @return array $data + */ + public function getExtraData($gridField, $record, $columnName); + + /** + * Gets the group this menu item will belong to + * + * @see {@link GridField_ActionMenu->getColumnContent()} + * + * @param GridField $gridField + * @param DataObject $record + * + * @return string $group + */ + public function getGroup($gridField, $record, $columnName); +} diff --git a/src/Forms/GridField/GridField_ActionMenuLink.php b/src/Forms/GridField/GridField_ActionMenuLink.php new file mode 100644 index 000000000..6baa05e1a --- /dev/null +++ b/src/Forms/GridField/GridField_ActionMenuLink.php @@ -0,0 +1,21 @@ +getColumnContent()} + * + * @param GridField $gridField + * @param DataObject $record + * + * @return string $url + */ + public function getUrl($gridField, $record, $columnName); +} diff --git a/templates/SilverStripe/Forms/GridField/GridFieldEditButton.ss b/templates/SilverStripe/Forms/GridField/GridFieldEditButton.ss index 31f93b137..42f39d3dd 100644 --- a/templates/SilverStripe/Forms/GridField/GridFieldEditButton.ss +++ b/templates/SilverStripe/Forms/GridField/GridFieldEditButton.ss @@ -1,6 +1,6 @@ - <%t SilverStripe\\Forms\\GridField\\GridFieldEditButton_ss.EDIT 'Edit' %> + <%t SilverStripe\\Forms\\GridField\\GridFieldEditButton.EDIT 'Edit' %> diff --git a/templates/SilverStripe/Forms/GridField/GridFieldViewButton.ss b/templates/SilverStripe/Forms/GridField/GridFieldViewButton.ss index 690f78efb..f0c97e98e 100644 --- a/templates/SilverStripe/Forms/GridField/GridFieldViewButton.ss +++ b/templates/SilverStripe/Forms/GridField/GridFieldViewButton.ss @@ -1 +1,3 @@ -View + + View + diff --git a/templates/SilverStripe/Forms/GridField/GridField_ActionMenu.ss b/templates/SilverStripe/Forms/GridField/GridField_ActionMenu.ss new file mode 100644 index 000000000..48d605696 --- /dev/null +++ b/templates/SilverStripe/Forms/GridField/GridField_ActionMenu.ss @@ -0,0 +1 @@ +
diff --git a/tests/behat/src/CmsFormsContext.php b/tests/behat/src/CmsFormsContext.php index 50fab1a8b..413cbab12 100644 --- a/tests/behat/src/CmsFormsContext.php +++ b/tests/behat/src/CmsFormsContext.php @@ -416,7 +416,11 @@ JS; return null; } - $button = $name->getParent()->find('xpath', sprintf('//*[@aria-label="%s"]', $buttonLabel)); + if ($dropdownButton = $name->getParent()->find('css', '.action-menu__toggle')) { + $dropdownButton->click(); + } + + $button = $name->getParent()->find('named', array('link_or_button', $buttonLabel)); return $button; } diff --git a/tests/php/Forms/GridField/GridFieldActionMenuTest.php b/tests/php/Forms/GridField/GridFieldActionMenuTest.php new file mode 100644 index 000000000..fa3c28048 --- /dev/null +++ b/tests/php/Forms/GridField/GridFieldActionMenuTest.php @@ -0,0 +1,94 @@ +list = new DataList(Team::class); + $config = GridFieldConfig::create() + ->addComponent(new GridFieldEditButton()) + ->addComponent(new GridFieldDeleteAction()) + ->addComponent(new GridField_ActionMenu()); + $this->gridField = new GridField('testfield', 'testfield', $this->list, $config); + $this->form = new Form(null, 'mockform', new FieldList(array($this->gridField)), new FieldList()); + } + + public function testShowActionMenu() + { + if (Security::getCurrentUser()) { + Security::setCurrentUser(null); + } + + $content = new CSSContentParser($this->gridField->FieldHolder()); + // Check that there are content + $this->assertEquals(4, count($content->getBySelector('.ss-gridfield-item'))); + // Make sure that there are edit links, even though the user doesn't have "edit" permissions + // (they can still view the records) + $this->assertEquals( + 3, + count($content->getBySelector('.gridfield-actionmenu__container')), + 'Edit links should show when not logged in.' + ); + } + + public function testShowEditLinksWithAdminPermission() + { + $this->logInWithPermission('ADMIN'); + $content = new CSSContentParser($this->gridField->FieldHolder()); + $editLinks = $content->getBySelector('.gridfield-actionmenu__container'); + $this->assertEquals(3, count($editLinks), 'Edit links should show when logged in.'); + } +}