diff --git a/docs/docs/8.x/datatables/button.md b/docs/docs/8.x/datatables/button.md index f32d769f..2423aa22 100644 --- a/docs/docs/8.x/datatables/button.md +++ b/docs/docs/8.x/datatables/button.md @@ -20,6 +20,7 @@ Button::add() | [class](#class) | Sets additional class | | [route](#route) | Sets the button link href by using a route | | [link](#link) | Sets the button link href | +| [tooltip](#tooltip) | Sets a tooltip for the button | | [attributes](#attributes) | Sets HTML attributes to the button | | [make](#make) | Renders the button | @@ -93,6 +94,27 @@ Sets the button link href. ->link(route('boilerplate.users.edit', $user->id)) ``` +## tooltip + +Sets a tooltip for the button using the HTML `title` attribute. + +```php +->tooltip('Edit this user') +``` + +The tooltip is only rendered when a non-empty string is provided, avoiding unnecessary HTML attributes. + +**Example with tooltip:** + +```php +Button::add('Edit') + ->route('boilerplate.users.edit', $user->id) + ->icon('pencil-alt') + ->color('primary') + ->tooltip('Edit this user') + ->make(); +``` + ## attributes Sets HTML attributes. @@ -113,6 +135,8 @@ Renders the button. ## Button aliases +### Predefined buttons + ```php Button::show('route.to.resource.show', $resource); ``` @@ -126,3 +150,33 @@ Button::delete('route.to.resource.destroy', $resource); ``` > `Button::delete` will show a modal to confirm the deletion. You can set another confirmation message by using the [`Datatable::locale()` method](options#locale). + +### Custom button helper + +The `custom()` method provides a convenient way to create custom buttons with all parameters in a single call: + +```php +Button::custom( + 'route.name', // Route name (required) + $args, // Route arguments (optional, default: []) + 'icon-name', // FontAwesome icon (optional, default: '') + 'Tooltip text', // Tooltip text (optional, default: '') + 'primary', // Button color (optional, default: 'default') + ['data-action' => 'x'] // HTML attributes (optional, default: []) +); +``` + +**Example:** + +```php +Button::custom( + 'users.export', + ['id' => $user->id], + 'download', + 'Export user data', + 'success', + ['data-confirm' => 'Export this user?'] +); +``` + +**Note:** The icon parameter comes before the tooltip for better ergonomics. If no icon is provided, no empty HTML markup is generated. diff --git a/src/Datatables/Button.php b/src/Datatables/Button.php index fca00da6..60d3c765 100644 --- a/src/Datatables/Button.php +++ b/src/Datatables/Button.php @@ -10,6 +10,7 @@ class Button protected string $icon = ''; protected string $label = ''; protected array $attributes = []; + protected string $tooltip = ''; /** * Instanciate a new button. @@ -32,6 +33,28 @@ public static function add(string $label = ''): self return new static($label); } + /** + * Returns a custom button. + * + * @param string $route + * @param mixed $args + * @param string $icon + * @param string $tooltip + * @param string $color + * @param array $attributes + * @return string + */ + public static function custom(string $route, mixed $args = [], string $icon = '', string $tooltip = '', string $color = 'default', array $attributes = []): string + { + $button = self::add()->route($route, $args)->tooltip($tooltip); + + if (! empty($icon)) { + $button->icon($icon); + } + + return $button->color($color)->attributes($attributes)->make(); + } + /** * Returns an edit button. * @@ -41,7 +64,7 @@ public static function add(string $label = ''): self */ public static function show(string $route, $args = []): string { - return self::add()->attributes(['data-action' => 'dt-show-element'])->route($route, $args)->icon('eye')->make(); + return self::add()->attributes(['data-action' => 'dt-show-element'])->route($route, $args)->tooltip(__('boilerplate::datatable.show'))->icon('eye')->make(); } /** @@ -53,7 +76,7 @@ public static function show(string $route, $args = []): string */ public static function edit(string $route, $args = []): string { - return self::add()->attributes(['data-action' => 'dt-edit-element'])->route($route, $args)->color('primary')->icon('pencil-alt')->make(); + return self::add()->attributes(['data-action' => 'dt-edit-element'])->route($route, $args)->tooltip(__('boilerplate::datatable.edit'))->color('primary')->icon('pencil-alt')->make(); } /** @@ -68,6 +91,7 @@ public static function delete(string $route, $args = []): string return self::add() ->route($route, $args) ->attributes(['data-action' => 'dt-delete-element']) + ->tooltip(__('boilerplate::datatable.delete')) ->color('danger') ->icon('trash') ->make(); @@ -161,6 +185,19 @@ public function link(string $href): self return $this; } + /** + * Sets tooltip of button. + * + * @param string $tooltip + * @return $this + */ + public function tooltip(string $tooltip): self + { + $this->tooltip = $tooltip; + + return $this; + } + /** * Renders the button. * @@ -168,8 +205,6 @@ public function link(string $href): self */ public function make(): string { - $str = '%s%s'; - if (! empty($this->label) && ! empty($this->icon)) { $this->label = $this->label.' '; } @@ -182,6 +217,9 @@ public function make(): string return sprintf('%s="%s"', $k, $this->attributes[$k]); }, array_keys($this->attributes))); - return sprintf($str, $this->href, $this->color, $this->class, $attributes, $this->label, $this->icon); + $tooltip = ! empty($this->tooltip) ? sprintf(' data-toggle="tooltip" title="%s"', e($this->tooltip)) : ''; + $str = '%s%s'; + + return sprintf($str, $this->href, $tooltip, $this->color, $this->class, $attributes, $this->label, $this->icon); } } diff --git a/src/resources/lang/bg/datatable.php b/src/resources/lang/bg/datatable.php index 8d4435a9..a318f7db 100644 --- a/src/resources/lang/bg/datatable.php +++ b/src/resources/lang/bg/datatable.php @@ -3,4 +3,7 @@ return [ 'deleteConfirm' => 'Да изтриете този елемент?', 'deleteSuccess' => 'Позицията е успешно изтрита', + 'show' => 'Преглед', + 'edit' => 'Редактиране', + 'delete' => 'Изтриване', ]; diff --git a/src/resources/lang/en/datatable.php b/src/resources/lang/en/datatable.php index 95ef7f52..e2716d1a 100644 --- a/src/resources/lang/en/datatable.php +++ b/src/resources/lang/en/datatable.php @@ -3,4 +3,7 @@ return [ 'deleteConfirm' => 'Delete this item?', 'deleteSuccess' => 'Item successfully deleted', + 'show' => 'Show', + 'edit' => 'Edit', + 'delete' => 'Delete', ]; diff --git a/src/resources/lang/es/datatable.php b/src/resources/lang/es/datatable.php index cbf34f5d..23a36425 100644 --- a/src/resources/lang/es/datatable.php +++ b/src/resources/lang/es/datatable.php @@ -3,4 +3,7 @@ return [ 'deleteConfirm' => '¿Borrar este elemento?', 'deleteSuccess' => 'Elemento eliminado con éxito', + 'show' => 'Ver', + 'edit' => 'Editar', + 'delete' => 'Eliminar', ]; diff --git a/src/resources/lang/fa/datatable.php b/src/resources/lang/fa/datatable.php index c4eaaf93..36e80f72 100644 --- a/src/resources/lang/fa/datatable.php +++ b/src/resources/lang/fa/datatable.php @@ -3,4 +3,7 @@ return [ 'deleteConfirm' => 'این مورد حذف شود؟', 'deleteSuccess' => 'مورد با موفقیت حذف شد', + 'show' => 'نمایش', + 'edit' => 'ویرایش', + 'delete' => 'حذف', ]; diff --git a/src/resources/lang/fr/datatable.php b/src/resources/lang/fr/datatable.php index 203fc72d..4aa34750 100644 --- a/src/resources/lang/fr/datatable.php +++ b/src/resources/lang/fr/datatable.php @@ -3,4 +3,7 @@ return [ 'deleteConfirm' => 'Supprimer cet élément ?', 'deleteSuccess' => "L'élément a été supprimé", + 'show' => 'Voir', + 'edit' => 'Éditer', + 'delete' => 'Supprimer', ]; diff --git a/src/resources/lang/it/datatable.php b/src/resources/lang/it/datatable.php index 859c7153..f55b1b48 100644 --- a/src/resources/lang/it/datatable.php +++ b/src/resources/lang/it/datatable.php @@ -3,4 +3,7 @@ return [ 'deleteConfirm' => 'Cancellare questo elemento?', 'deleteSuccess' => 'Elemento eliminato con successo', + 'show' => 'Mostra', + 'edit' => 'Modifica', + 'delete' => 'Elimina', ]; diff --git a/src/resources/lang/tr/datatable.php b/src/resources/lang/tr/datatable.php index 3052c5ed..c21ed3e0 100644 --- a/src/resources/lang/tr/datatable.php +++ b/src/resources/lang/tr/datatable.php @@ -3,4 +3,7 @@ return [ 'deleteConfirm' => 'Bu öğe silinsin mi?', 'deleteSuccess' => 'Öğe başarıyla silindi', + 'show' => 'Göster', + 'edit' => 'Düzenle', + 'delete' => 'Sil', ]; diff --git a/tests/Controllers/DatatablesTest.php b/tests/Controllers/DatatablesTest.php index 4d1cd95e..8e7382a4 100644 --- a/tests/Controllers/DatatablesTest.php +++ b/tests/Controllers/DatatablesTest.php @@ -48,6 +48,6 @@ public function testUsersDatatables() $data = $resource->getOriginalContent()['data'][0]; $this->assertEquals($user->email, $data['email']); - $this->assertEquals('', $data['dt-actions']); + $this->assertEquals('', $data['dt-actions']); } } diff --git a/tests/Datatables/ButtonTest.php b/tests/Datatables/ButtonTest.php new file mode 100644 index 00000000..408257b2 --- /dev/null +++ b/tests/Datatables/ButtonTest.php @@ -0,0 +1,128 @@ +link('/test') + ->tooltip('This is a tooltip') + ->make(); + + $this->assertStringContainsString('title="This is a tooltip"', $button); + } + + public function testTooltipNotAddedWhenEmpty() + { + $button = Button::add('Test Button') + ->link('/test') + ->make(); + + $this->assertStringNotContainsString('title=', $button); + } + + public function testTooltipMethodChaining() + { + $button = Button::add('Test') + ->link('/test') + ->tooltip('Tooltip text') + ->color('primary') + ->icon('star') + ->make(); + + $this->assertStringContainsString('title="Tooltip text"', $button); + $this->assertStringContainsString('btn-primary', $button); + $this->assertStringContainsString('fa-star', $button); + } + + public function testButtonHtmlStructure() + { + $button = Button::add('Click me') + ->link('/action') + ->tooltip('Button tooltip') + ->color('success') + ->make(); + + $this->assertMatchesRegularExpression('/assertStringContainsString('btn-success', $button); + $this->assertStringContainsString('fa-star', $button); + $this->assertStringContainsString('/test/1', $button); + } + + public function testCustomButtonWithAttributes() + { + $this->app['router']->get('/test', function () { + return 'test'; + })->name('test.route2'); + + $button = Button::custom( + 'test.route2', + [], + 'heart', + 'Custom tooltip', + 'primary', + ['data-action' => 'custom-action', 'data-id' => '123'] + ); + + $this->assertStringContainsString('data-action="custom-action"', $button); + $this->assertStringContainsString('data-id="123"', $button); + $this->assertStringContainsString('btn-primary', $button); + $this->assertStringContainsString('fa-heart', $button); + } + + public function testCustomButtonWithDefaults() + { + $this->app['router']->get('/default', function () { + return 'test'; + })->name('test.default'); + + $button = Button::custom('test.default'); + + $this->assertStringNotContainsString('title=', $button); + $this->assertStringContainsString('btn-default', $button); + $this->assertStringNotContainsString('fa-', $button); // No icon by default + } + + public function testCustomButtonArgumentOrder() + { + // Test that $icon comes before $tooltip in the method signature + $this->app['router']->get('/order', function () { + return 'test'; + })->name('test.order'); + + $button = Button::custom( + 'test.order', + [], + 'cog', // $icon (3rd parameter) + 'Tooltip', // $tooltip (4th parameter) + 'warning' // $color (5th parameter) + ); + + $this->assertStringContainsString('fa-cog', $button); + $this->assertStringContainsString('title="Tooltip"', $button); + $this->assertStringContainsString('btn-warning', $button); + } +}