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 e38c8322..19f2f734 100644 --- a/src/Datatables/Button.php +++ b/src/Datatables/Button.php @@ -4,12 +4,13 @@ class Button { - protected $class = ''; - protected $color = 'default'; - protected $href = '#'; - protected $icon = ''; - protected $label = ''; - protected $attributes = []; + protected string $class = ''; + protected string $color = 'default'; + protected string $href = '#'; + 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 array|string $args + * @param string $icon + * @param string $tooltip + * @param string $color + * @param array $attributes + * @return string + */ + public static function custom(string $route, array|string $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. * @@ -168,8 +191,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 +203,22 @@ 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(' title="%s"', $this->tooltip) : ''; + $str = '%s%s'; + + return sprintf($str, $this->href, $tooltip, $this->color, $this->class, $attributes, $this->label, $this->icon); + } + + /** + * Sets tooltip of button. + * + * @param string $tooltip + * @return $this + */ + public function tooltip(string $tooltip): self + { + $this->tooltip = $tooltip; + + return $this; } } diff --git a/tests/Datatables/ButtonTest.php b/tests/Datatables/ButtonTest.php new file mode 100644 index 00000000..bcc8374a --- /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); + } +}