mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
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
This commit is contained in:
parent
a0d0564369
commit
385e9e105c
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
```
|
||||
|
@ -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'
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
* </code>
|
||||
*/
|
||||
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
|
||||
*
|
||||
|
@ -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);
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
101
src/Forms/GridField/GridField_ActionMenu.php
Normal file
101
src/Forms/GridField/GridField_ActionMenu.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Forms\GridField;
|
||||
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\View\ArrayData;
|
||||
use SilverStripe\View\SSViewer;
|
||||
|
||||
/**
|
||||
* Groups exiting actions in the Actions column in to a menu
|
||||
*/
|
||||
class GridField_ActionMenu implements GridField_ColumnProvider, GridField_ActionProvider
|
||||
{
|
||||
public function augmentColumns($gridField, &$columns)
|
||||
{
|
||||
if (!in_array('Actions', $columns)) {
|
||||
$columns[] = 'Actions';
|
||||
}
|
||||
}
|
||||
|
||||
public function getColumnsHandled($gridField)
|
||||
{
|
||||
return ['Actions'];
|
||||
}
|
||||
|
||||
public function getColumnContent($gridField, $record, $columnName)
|
||||
{
|
||||
$items = $this->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;
|
||||
}
|
||||
}
|
53
src/Forms/GridField/GridField_ActionMenuItem.php
Normal file
53
src/Forms/GridField/GridField_ActionMenuItem.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Forms\GridField;
|
||||
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
/**
|
||||
* GridField action menu item interface, this provides data so the action
|
||||
* will be included if there is a {@see GridField_ActionMenu}
|
||||
*/
|
||||
interface GridField_ActionMenuItem extends GridFieldComponent
|
||||
{
|
||||
/**
|
||||
* Default group name
|
||||
*/
|
||||
const DEFAULT_GROUP = 'Default';
|
||||
|
||||
/**
|
||||
* Gets the title for this menu item
|
||||
*
|
||||
* @see {@link GridField_ActionMenu->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);
|
||||
}
|
21
src/Forms/GridField/GridField_ActionMenuLink.php
Normal file
21
src/Forms/GridField/GridField_ActionMenuLink.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Forms\GridField;
|
||||
|
||||
/**
|
||||
* Allows GridField_ActionMenuItem to act as a link
|
||||
*/
|
||||
interface GridField_ActionMenuLink extends GridField_ActionMenuItem
|
||||
{
|
||||
/**
|
||||
* Gets the action url for this menu item
|
||||
*
|
||||
* @see {@link GridField_ActionMenu->getColumnContent()}
|
||||
*
|
||||
* @param GridField $gridField
|
||||
* @param DataObject $record
|
||||
*
|
||||
* @return string $url
|
||||
*/
|
||||
public function getUrl($gridField, $record, $columnName);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<a
|
||||
class="grid-field__icon-action {$ExtraClass} action action-detail edit-link"
|
||||
href="$Link" title="<%t SilverStripe\\Forms\\GridField\\GridFieldEditButton_ss.EDIT 'Edit' %>"
|
||||
href="$Link" title="<%t SilverStripe\\Forms\\GridField\\GridFieldEditButton.EDIT 'Edit' %>"
|
||||
>
|
||||
<span class="sr-only"><%t SilverStripe\\Forms\\GridField\\GridFieldEditButton_ss.EDIT 'Edit' %></span>
|
||||
<span class="sr-only"><%t SilverStripe\\Forms\\GridField\\GridFieldEditButton.EDIT 'Edit' %></span>
|
||||
</a>
|
||||
|
@ -1 +1,3 @@
|
||||
<a class="grid-field__icon-action font-icon-right-open btn--icon-large action action-detail view-link" href="$Link"><span class="sr-only">View</span></a>
|
||||
<a class="grid-field__icon-action font-icon-right-open btn--icon-large action action-detail view-link" href="$Link">
|
||||
<span class="sr-only">View</span>
|
||||
</a>
|
||||
|
@ -0,0 +1 @@
|
||||
<div class="gridfield-actionmenu__container" data-schema="$Schema"></div>
|
@ -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;
|
||||
}
|
||||
|
94
tests/php/Forms/GridField/GridFieldActionMenuTest.php
Normal file
94
tests/php/Forms/GridField/GridFieldActionMenuTest.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Forms\Tests\GridField;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Dev\CSSContentParser;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
use SilverStripe\Forms\GridField\GridField_ActionMenu;
|
||||
use SilverStripe\Forms\GridField\GridFieldConfig;
|
||||
use SilverStripe\Forms\GridField\GridFieldEditButton;
|
||||
use SilverStripe\Forms\GridField\GridFieldDeleteAction;
|
||||
use SilverStripe\Forms\Tests\GridField\GridFieldTest\Cheerleader;
|
||||
use SilverStripe\Forms\Tests\GridField\GridFieldTest\Permissions;
|
||||
use SilverStripe\Forms\Tests\GridField\GridFieldTest\Player;
|
||||
use SilverStripe\Forms\Tests\GridField\GridFieldTest\Team;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\DataList;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\Security;
|
||||
|
||||
class GridFieldActionMenuTest extends SapphireTest
|
||||
{
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
protected $list;
|
||||
|
||||
/**
|
||||
* @var GridField
|
||||
*/
|
||||
protected $gridField;
|
||||
|
||||
/**
|
||||
* @var Form
|
||||
*/
|
||||
protected $form;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected static $fixture_file = 'GridFieldActionTest.yml';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $extra_dataobjects = array(
|
||||
Team::class,
|
||||
Cheerleader::class,
|
||||
Player::class,
|
||||
Permissions::class,
|
||||
);
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->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.');
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user