Skip to content

Commit 5a42b5c

Browse files
sebastienheydclaude
andcommitted
feat(datatables): add tooltip support and custom button helper
Add tooltip functionality to datatable buttons with conditional rendering to avoid empty title attributes. Introduce custom() static helper method for simplified button creation with icon, tooltip, color and attributes support. The method intelligently handles empty icons to prevent unnecessary HTML generation. Include comprehensive test suite with 8 tests covering tooltip rendering, method chaining, custom button creation with various parameter combinations, and argument order validation. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 5c21945 commit 5a42b5c

File tree

2 files changed

+168
-3
lines changed

2 files changed

+168
-3
lines changed

src/Datatables/Button.php

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class Button
1010
protected string $icon = '';
1111
protected string $label = '';
1212
protected array $attributes = [];
13+
protected string $tooltip = '';
1314

1415
/**
1516
* Instanciate a new button.
@@ -32,6 +33,28 @@ public static function add(string $label = ''): self
3233
return new static($label);
3334
}
3435

36+
/**
37+
* Returns a custom button.
38+
*
39+
* @param string $route
40+
* @param array|string $args
41+
* @param string $icon
42+
* @param string $tooltip
43+
* @param string $color
44+
* @param array $attributes
45+
* @return string
46+
*/
47+
public static function custom(string $route, array|string $args = [], string $icon = '', string $tooltip = '', string $color = 'default', array $attributes = []): string
48+
{
49+
$button = self::add()->route($route, $args)->tooltip($tooltip);
50+
51+
if (! empty($icon)) {
52+
$button->icon($icon);
53+
}
54+
55+
return $button->color($color)->attributes($attributes)->make();
56+
}
57+
3558
/**
3659
* Returns an edit button.
3760
*
@@ -168,8 +191,6 @@ public function link(string $href): self
168191
*/
169192
public function make(): string
170193
{
171-
$str = '<a href="%s" class="btn btn-sm btn-%s ml-1%s" %s>%s%s</a>';
172-
173194
if (! empty($this->label) && ! empty($this->icon)) {
174195
$this->label = $this->label.' ';
175196
}
@@ -182,6 +203,22 @@ public function make(): string
182203
return sprintf('%s="%s"', $k, $this->attributes[$k]);
183204
}, array_keys($this->attributes)));
184205

185-
return sprintf($str, $this->href, $this->color, $this->class, $attributes, $this->label, $this->icon);
206+
$tooltip = ! empty($this->tooltip) ? sprintf(' title="%s"', $this->tooltip) : '';
207+
$str = '<a href="%s"%s class="btn btn-sm btn-%s ml-1%s" %s>%s%s</a>';
208+
209+
return sprintf($str, $this->href, $tooltip, $this->color, $this->class, $attributes, $this->label, $this->icon);
210+
}
211+
212+
/**
213+
* Sets tooltip of button.
214+
*
215+
* @param string $tooltip
216+
* @return $this
217+
*/
218+
public function tooltip(string $tooltip): self
219+
{
220+
$this->tooltip = $tooltip;
221+
222+
return $this;
186223
}
187224
}

tests/Datatables/ButtonTest.php

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<?php
2+
3+
namespace Sebastienheyd\Boilerplate\Tests\Datatables;
4+
5+
use Sebastienheyd\Boilerplate\Datatables\Button;
6+
use Sebastienheyd\Boilerplate\Tests\TestCase;
7+
8+
class ButtonTest extends TestCase
9+
{
10+
public function testTooltipMethod()
11+
{
12+
$button = Button::add('Test Button')
13+
->link('/test')
14+
->tooltip('This is a tooltip')
15+
->make();
16+
17+
$this->assertStringContainsString('title="This is a tooltip"', $button);
18+
}
19+
20+
public function testTooltipNotAddedWhenEmpty()
21+
{
22+
$button = Button::add('Test Button')
23+
->link('/test')
24+
->make();
25+
26+
$this->assertStringNotContainsString('title=', $button);
27+
}
28+
29+
public function testTooltipMethodChaining()
30+
{
31+
$button = Button::add('Test')
32+
->link('/test')
33+
->tooltip('Tooltip text')
34+
->color('primary')
35+
->icon('star')
36+
->make();
37+
38+
$this->assertStringContainsString('title="Tooltip text"', $button);
39+
$this->assertStringContainsString('btn-primary', $button);
40+
$this->assertStringContainsString('fa-star', $button);
41+
}
42+
43+
public function testButtonHtmlStructure()
44+
{
45+
$button = Button::add('Click me')
46+
->link('/action')
47+
->tooltip('Button tooltip')
48+
->color('success')
49+
->make();
50+
51+
$this->assertMatchesRegularExpression('/<a href="\/action" title="Button tooltip" class="btn btn-sm btn-success/', $button);
52+
}
53+
54+
public function testCustomButtonWithRoute()
55+
{
56+
// Define a test route
57+
$this->app['router']->get('/test/{id}', function () {
58+
return 'test';
59+
})->name('test.route');
60+
61+
$button = Button::custom(
62+
'test.route',
63+
['id' => 1],
64+
'star',
65+
'Custom tooltip',
66+
'success'
67+
);
68+
69+
$this->assertStringContainsString('title="Custom tooltip"', $button);
70+
$this->assertStringContainsString('btn-success', $button);
71+
$this->assertStringContainsString('fa-star', $button);
72+
$this->assertStringContainsString('/test/1', $button);
73+
}
74+
75+
public function testCustomButtonWithAttributes()
76+
{
77+
$this->app['router']->get('/test', function () {
78+
return 'test';
79+
})->name('test.route2');
80+
81+
$button = Button::custom(
82+
'test.route2',
83+
[],
84+
'heart',
85+
'Custom tooltip',
86+
'primary',
87+
['data-action' => 'custom-action', 'data-id' => '123']
88+
);
89+
90+
$this->assertStringContainsString('data-action="custom-action"', $button);
91+
$this->assertStringContainsString('data-id="123"', $button);
92+
$this->assertStringContainsString('btn-primary', $button);
93+
$this->assertStringContainsString('fa-heart', $button);
94+
}
95+
96+
public function testCustomButtonWithDefaults()
97+
{
98+
$this->app['router']->get('/default', function () {
99+
return 'test';
100+
})->name('test.default');
101+
102+
$button = Button::custom('test.default');
103+
104+
$this->assertStringNotContainsString('title=', $button);
105+
$this->assertStringContainsString('btn-default', $button);
106+
$this->assertStringNotContainsString('fa-', $button); // No icon by default
107+
}
108+
109+
public function testCustomButtonArgumentOrder()
110+
{
111+
// Test that $icon comes before $tooltip in the method signature
112+
$this->app['router']->get('/order', function () {
113+
return 'test';
114+
})->name('test.order');
115+
116+
$button = Button::custom(
117+
'test.order',
118+
[],
119+
'cog', // $icon (3rd parameter)
120+
'Tooltip', // $tooltip (4th parameter)
121+
'warning' // $color (5th parameter)
122+
);
123+
124+
$this->assertStringContainsString('fa-cog', $button);
125+
$this->assertStringContainsString('title="Tooltip"', $button);
126+
$this->assertStringContainsString('btn-warning', $button);
127+
}
128+
}

0 commit comments

Comments
 (0)