From 01ef91ebf038c155e7c4afe82e195fe8029e3c8a Mon Sep 17 00:00:00 2001 From: mhuser Date: Mon, 4 Sep 2023 12:44:46 +0200 Subject: [PATCH] Add Grid bulk action methods (#1920) --- demos/collection/grid.php | 37 +++++++++++++++--- src/Grid.php | 62 ++++++++++++++++++++++++++++-- src/Table/Column/ActionButtons.php | 2 +- tests-behat/grid.feature | 23 +++++++++++ 4 files changed, 113 insertions(+), 11 deletions(-) diff --git a/demos/collection/grid.php b/demos/collection/grid.php index ba99097a5a..874d96df3a 100644 --- a/demos/collection/grid.php +++ b/demos/collection/grid.php @@ -6,8 +6,10 @@ use Atk4\Data\Model; use Atk4\Ui\Button; +use Atk4\Ui\Form; use Atk4\Ui\Grid; use Atk4\Ui\Js\Jquery; +use Atk4\Ui\Js\JsBlock; use Atk4\Ui\Js\JsExpression; use Atk4\Ui\Js\JsReload; use Atk4\Ui\Js\JsToast; @@ -71,12 +73,35 @@ // TODO button is added not only to the table rows, but also below the table! // $grid->addExecutorButton($deleteExecutor, new Button(['icon' => 'times circle outline'])); -$sel = $grid->addSelection(); -$grid->menu->addItem('show selection') - ->on('click', new JsExpression( - 'alert(\'Selected: \' + [])', - [$sel->jsChecked()] - )); +$grid->addSelection(); + +$grid->addBulkAction(['Show selected', 'icon' => 'binoculars'], static function (Jquery $j, array $ids) { + return new JsToast('Selected: ' . implode(', ', $ids) . '#'); +}); + +// Executing a modal on a bulk selection +$grid->addModalBulkAction(['Delete selected', 'icon' => 'trash'], '', static function (View $modal, array $ids) use ($grid) { + Message::addTo($modal, [ + 'The selected records will be permanently deleted: ' . implode(', ', $ids) . '#', + 'type' => 'warning', + 'icon' => 'warning', + ]); + $form = Form::addTo($modal); + $form->buttonSave->set('Delete'); + $form->buttonSave->icon = 'trash'; + $form->onSubmit(static function (Form $form) use ($grid, $ids) { + $grid->model->atomic(static function () use ($grid, $ids) { + foreach ($ids as $id) { + $grid->model->delete($id); + } + }); + + return new JsBlock([ + $grid->jsReload(), + $form->jsSuccess(), + ]); + }); +}); // Setting ipp with an array will add an ItemPerPageSelector to paginator. $grid->setIpp([10, 100, 1000]); diff --git a/src/Grid.php b/src/Grid.php index 7338cba0de..a4704c6399 100644 --- a/src/Grid.php +++ b/src/Grid.php @@ -52,7 +52,7 @@ class Grid extends View public $actionButtons; /** - * Calling addAction will add a new column inside $table with dropdown menu, + * Calling addActionMenuItem will add a new column inside $table with dropdown menu, * and will be re-used for next addActionMenuItem(). * * @var Table\Column|null @@ -386,7 +386,7 @@ private function getActionButtons(): Table\Column\ActionButtons } /** - * Similar to addAction. Will add Button that when click will display + * Similar to addActionButton. Will add Button that when click will display * a Dropdown menu. * * @param View|string $view @@ -498,8 +498,8 @@ public function addPopup($columnName, $popup = null, $icon = 'caret square down' } /** - * Similar to addAction but when button is clicked, modal is displayed - * with the $title and $callback is executed through VirtualPage. + * Similar to addActionButton but when button is clicked, modal is displayed + * with the $title and $callback is executed. * * @param string|array|View $button * @param string $title @@ -513,6 +513,60 @@ public function addModalAction($button, $title, \Closure $callback, $args = []) return $this->getActionButtons()->addModal($button, $title, $callback, $this, $args); } + /** + * @return list + */ + private function explodeSelectionValue(string $value): array + { + return $value === '' ? [] : explode(',', $value); + } + + /** + * Similar to addActionButton but apply to a multiple records selection and display in menu. + * When menu item is clicked, $callback is executed. + * + * @param string|array|MenuItem $item + * @param \Closure(Js\Jquery, list): JsExpressionable $callback + * @param array $args extra URL argument for callback + * + * @return View + */ + public function addBulkAction($item, \Closure $callback, $args = []) + { + $menuItem = $this->menu->addItem($item); + $menuItem->on('click', function (Js\Jquery $j, string $value) use ($callback) { + return $callback($j, $this->explodeSelectionValue($value)); + }, [$this->selection->jsChecked()]); + + return $menuItem; + } + + /** + * Similar to addModalAction but apply to a multiple records selection and display in menu. + * When menu item is clicked, modal is displayed with the $title and $callback is executed. + * + * @param string|array|MenuItem $item + * @param string $title + * @param \Closure(View, list): void $callback + * @param array $args extra URL argument for callback + * + * @return View + */ + public function addModalBulkAction($item, $title, \Closure $callback, $args = []) + { + $modalDefaults = is_string($title) ? ['title' => $title] : []; // @phpstan-ignore-line + + $modal = Modal::addTo($this->getOwner(), $modalDefaults); + $modal->set(function (View $t) use ($callback) { + $callback($t, $this->explodeSelectionValue($t->stickyGet($this->name) ?? '')); + }); + + $menuItem = $this->menu->addItem($item); + $menuItem->on('click', $modal->jsShow(array_merge([$this->name => $this->selection->jsChecked()], $args))); + + return $menuItem; + } + /** * Get sortBy value from URL parameter. */ diff --git a/src/Table/Column/ActionButtons.php b/src/Table/Column/ActionButtons.php index 9ccf256cae..ed1e7fdc8c 100644 --- a/src/Table/Column/ActionButtons.php +++ b/src/Table/Column/ActionButtons.php @@ -85,7 +85,7 @@ public function addButton($button, $action = null, string $confirmMsg = '', $isD */ public function addModal($button, $defaults, \Closure $callback, $owner = null, $args = []) { - if ($owner === null) { + if ($owner === null) { // TODO explicit owner should not be needed $owner = $this->getOwner()->getOwner(); } diff --git a/tests-behat/grid.feature b/tests-behat/grid.feature index fd73c8f667..1e3a1dd508 100644 --- a/tests-behat/grid.feature +++ b/tests-behat/grid.feature @@ -76,3 +76,26 @@ Feature: Grid Then I should see "Andorra" Then I should see "China" Then I should see "Zambia" + + Scenario: Bulk action + Given I am on "collection/grid.php" + Then I press button "Show selected" + Then Toast display should contain text "Selected: #" + When I click using selector "//tr[5]//div.ui.checkbox" + When I click using selector "//tr[8]//div.ui.checkbox" + Then I press button "Show selected" + Then Toast display should contain text "Selected: 5, 8#" + + Scenario: Bulk modal action + Given I am on "collection/grid.php" + Then I press button "Delete selected" + Then Modal is open with text "The selected records will be permanently deleted: #" + Then I press button "Delete" + Then I should see "Success" + Then I click close modal + When I click using selector "//tr[5]//div.ui.checkbox" + When I click using selector "//tr[8]//div.ui.checkbox" + Then I press button "Delete selected" + Then Modal is open with text "The selected records will be permanently deleted: 5, 8#" + Then I press button "Delete" + Then I should see "Success"