Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
88.89% |
40 / 45 |
|
58.33% |
7 / 12 |
CRAP | |
0.00% |
0 / 1 |
ButtonBuilder | |
88.89% |
40 / 45 |
|
58.33% |
7 / 12 |
19.50 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setId | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setLabel | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
3.14 | |||
setAction | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
setWeight | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
setSize | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
setType | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
setIconClass | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setIconOnly | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setDisabled | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setAttributes | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
build | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * ButtonBuilder.php |
4 | * |
5 | * This file is part of the Codex design system, the official design system |
6 | * for Wikimedia projects. It provides the `Button` class, a builder for constructing |
7 | * button elements using the Codex design system. |
8 | * |
9 | * A Button triggers an action when the user clicks or taps on it. |
10 | * It can be styled in various ways to reflect its importance, function, and state. |
11 | * |
12 | * @category Builder |
13 | * @package Codex\Builder |
14 | * @since 0.1.0 |
15 | * @author Doğu Abaris <abaris@null.net> |
16 | * @license https://www.gnu.org/copyleft/gpl.html GPL-2.0-or-later |
17 | * @link https://doc.wikimedia.org/codex/main/ Codex Documentation |
18 | */ |
19 | |
20 | namespace Wikimedia\Codex\Builder; |
21 | |
22 | use InvalidArgumentException; |
23 | use Wikimedia\Codex\Component\Button; |
24 | use Wikimedia\Codex\Renderer\ButtonRenderer; |
25 | |
26 | /** |
27 | * ButtonBuilder |
28 | * |
29 | * This class implements the builder pattern to construct instances of Button. |
30 | * It provides a fluent interface for setting various properties and building the |
31 | * final immutable object with predefined configurations and immutability. |
32 | * |
33 | * @category Builder |
34 | * @package Codex\Builder |
35 | * @since 0.1.0 |
36 | * @author Doğu Abaris <abaris@null.net> |
37 | * @license https://www.gnu.org/copyleft/gpl.html GPL-2.0-or-later |
38 | * @link https://doc.wikimedia.org/codex/main/ Codex Documentation |
39 | */ |
40 | class ButtonBuilder { |
41 | |
42 | /** |
43 | * Allowed action styles for the button. |
44 | */ |
45 | private const ALLOWED_ACTIONS = [ |
46 | 'default', |
47 | 'progressive', |
48 | 'destructive', |
49 | ]; |
50 | |
51 | /** |
52 | * Allowed sizes for the button. |
53 | */ |
54 | private const ALLOWED_SIZES = [ |
55 | 'medium', |
56 | 'large', |
57 | ]; |
58 | |
59 | /** |
60 | * Allowed button types. |
61 | */ |
62 | private const ALLOWED_TYPES = [ |
63 | 'button', |
64 | 'submit', |
65 | 'reset', |
66 | ]; |
67 | |
68 | /** |
69 | * Allowed weight styles for the button. |
70 | */ |
71 | private const ALLOWED_WEIGHTS = [ |
72 | 'normal', |
73 | 'primary', |
74 | 'quiet', |
75 | ]; |
76 | |
77 | /** |
78 | * The ID for the button. |
79 | */ |
80 | protected string $id = ''; |
81 | |
82 | /** |
83 | * The text label displayed on the button. |
84 | */ |
85 | protected string $label = ''; |
86 | |
87 | /** |
88 | * The visual action style of the button (e.g., default, progressive, destructive). |
89 | */ |
90 | protected string $action = 'default'; |
91 | |
92 | /** |
93 | * The size of the button (e.g., medium, large). |
94 | */ |
95 | protected string $size = 'medium'; |
96 | |
97 | /** |
98 | * The type of the button (e.g., button, submit, reset). |
99 | */ |
100 | protected string $type = 'button'; |
101 | |
102 | /** |
103 | * The visual prominence of the button (e.g., normal, primary, quiet). |
104 | */ |
105 | protected string $weight = 'normal'; |
106 | |
107 | /** |
108 | * The CSS class for an icon, if the button includes one. |
109 | */ |
110 | protected ?string $iconClass = null; |
111 | |
112 | /** |
113 | * Indicates if the button is icon-only (no text). |
114 | */ |
115 | protected bool $iconOnly = false; |
116 | |
117 | /** |
118 | * Indicates if the button is disabled. |
119 | */ |
120 | protected bool $disabled = false; |
121 | |
122 | /** |
123 | * Additional HTML attributes for the button element. |
124 | */ |
125 | protected array $attributes = []; |
126 | |
127 | /** |
128 | * The renderer instance used to render the button. |
129 | */ |
130 | protected ButtonRenderer $renderer; |
131 | |
132 | /** |
133 | * Constructor for the ButtonBuilder class. |
134 | * |
135 | * @param ButtonRenderer $renderer The renderer to use for rendering the button. |
136 | */ |
137 | public function __construct( ButtonRenderer $renderer ) { |
138 | $this->renderer = $renderer; |
139 | } |
140 | |
141 | /** |
142 | * Set the button's HTML ID attribute. |
143 | * |
144 | * @since 0.1.0 |
145 | * @param string $id The ID for the button element. |
146 | * @return $this |
147 | */ |
148 | public function setId( string $id ): self { |
149 | $this->id = $id; |
150 | |
151 | return $this; |
152 | } |
153 | |
154 | /** |
155 | * Set the label for the button. |
156 | * |
157 | * This method defines the text that will be displayed on the button. The label is crucial for providing |
158 | * users with context about the button's action. In cases where the button is not icon-only, the label |
159 | * will be wrapped in a `<span>` element within the button. |
160 | * |
161 | * It's important to use concise and descriptive text for the label to ensure usability. |
162 | * |
163 | * @since 0.1.0 |
164 | * @param string $label The text label displayed on the button. |
165 | * @return $this Returns the Button instance for method chaining. |
166 | */ |
167 | public function setLabel( string $label ): self { |
168 | if ( trim( $label ) === '' && !$this->iconOnly ) { |
169 | throw new InvalidArgumentException( 'Button label cannot be empty unless the button is icon-only.' ); |
170 | } |
171 | $this->label = $label; |
172 | |
173 | return $this; |
174 | } |
175 | |
176 | /** |
177 | * Set the action style for the button. |
178 | * |
179 | * This method determines the visual style of the button, which reflects the nature of the action |
180 | * it represents. The action can be one of the following: |
181 | * - 'default': A standard action button with no special emphasis. |
182 | * - 'progressive': Indicates a positive or confirmatory action, often styled with a green or blue background. |
183 | * - 'destructive': Used for actions that have a significant or irreversible impact, typically styled in red. |
184 | * |
185 | * The action style is applied as a CSS class (`cdx-button--action-{action}`) to the button element. |
186 | * |
187 | * @since 0.1.0 |
188 | * @param string $action The action style for the button. |
189 | * @return $this Returns the Button instance for method chaining. |
190 | */ |
191 | public function setAction( string $action ): self { |
192 | if ( !in_array( $action, self::ALLOWED_ACTIONS, true ) ) { |
193 | throw new InvalidArgumentException( "Invalid action: $action" ); |
194 | } |
195 | $this->action = $action; |
196 | |
197 | return $this; |
198 | } |
199 | |
200 | /** |
201 | * Set the weight style for the button. |
202 | * |
203 | * This method sets the visual prominence of the button, which can be: |
204 | * - 'normal': A standard button with default emphasis. |
205 | * - 'primary': A high-importance button that stands out, often used for primary actions. |
206 | * - 'quiet': A subtle, low-emphasis button, typically used for secondary or tertiary actions. |
207 | * |
208 | * The weight style is applied as a CSS class (`cdx-button--weight-{weight}`) to the button element. |
209 | * |
210 | * @since 0.1.0 |
211 | * @param string $weight The weight style for the button. |
212 | * @return $this Returns the Button instance for method chaining. |
213 | */ |
214 | public function setWeight( string $weight ): self { |
215 | if ( !in_array( $weight, self::ALLOWED_WEIGHTS, true ) ) { |
216 | throw new InvalidArgumentException( "Invalid weight: $weight" ); |
217 | } |
218 | $this->weight = $weight; |
219 | |
220 | return $this; |
221 | } |
222 | |
223 | /** |
224 | * Set the size of the button. |
225 | * |
226 | * This method defines the size of the button, which can be either: |
227 | * - 'medium': The default size, suitable for most use cases. |
228 | * - 'large': A larger button, often used to improve accessibility or to emphasize an action. |
229 | * |
230 | * The size is applied as a CSS class (`cdx-button--size-{size}`) to the button element. |
231 | * |
232 | * @since 0.1.0 |
233 | * @param string $size The size of the button. |
234 | * @return $this Returns the Button instance for method chaining. |
235 | */ |
236 | public function setSize( string $size ): self { |
237 | if ( !in_array( $size, self::ALLOWED_SIZES, true ) ) { |
238 | throw new InvalidArgumentException( "Invalid size: $size" ); |
239 | } |
240 | $this->size = $size; |
241 | |
242 | return $this; |
243 | } |
244 | |
245 | /** |
246 | * Set the type of the button. |
247 | * |
248 | * This method sets the button's type attribute, which can be one of the following: |
249 | * - 'button': A standard clickable button. |
250 | * - 'submit': A button used to submit a form. |
251 | * - 'reset': A button used to reset form fields to their initial values. |
252 | * |
253 | * The type attribute is applied directly to the `<button>` element. |
254 | * |
255 | * @since 0.1.0 |
256 | * @param string $type The type for the button. |
257 | * @return $this Returns the Button instance for method chaining. |
258 | */ |
259 | public function setType( string $type ): self { |
260 | if ( !in_array( $type, self::ALLOWED_TYPES, true ) ) { |
261 | throw new InvalidArgumentException( "Invalid button type: $type" ); |
262 | } |
263 | $this->type = $type; |
264 | |
265 | return $this; |
266 | } |
267 | |
268 | /** |
269 | * Set the icon class for the button. |
270 | * |
271 | * This method specifies a CSS class for an icon to be displayed inside the button. The icon is rendered |
272 | * within a `<span>` element with the class `cdx-button__icon`, and should be defined using a suitable |
273 | * icon font or SVG sprite. |
274 | * |
275 | * The icon enhances the button's usability by providing a visual cue regarding the button's action. |
276 | * |
277 | * @since 0.1.0 |
278 | * @param string $iconClass The CSS class for the icon. |
279 | * @return $this Returns the Button instance for method chaining. |
280 | */ |
281 | public function setIconClass( string $iconClass ): self { |
282 | $this->iconClass = $iconClass; |
283 | |
284 | return $this; |
285 | } |
286 | |
287 | /** |
288 | * Set whether the button should be icon-only. |
289 | * |
290 | * This method determines whether the button should display only an icon, without any text. |
291 | * When set to `true`, the button will only render the icon, making it useful for scenarios where |
292 | * space is limited, such as in toolbars or mobile interfaces. |
293 | * |
294 | * Icon-only buttons should always include an `aria-label` attribute for accessibility, ensuring that |
295 | * the button's purpose is clear to screen reader users. |
296 | * |
297 | * @since 0.1.0 |
298 | * @param bool $iconOnly Whether the button is icon-only. |
299 | * @return $this Returns the Button instance for method chaining. |
300 | */ |
301 | public function setIconOnly( bool $iconOnly ): self { |
302 | $this->iconOnly = $iconOnly; |
303 | |
304 | return $this; |
305 | } |
306 | |
307 | /** |
308 | * Set whether the button is disabled. |
309 | * |
310 | * This method disables the button, preventing any interaction. |
311 | * A disabled button appears inactive and cannot be clicked. |
312 | * |
313 | * Example usage: |
314 | * |
315 | * $button->setDisabled(true); |
316 | * |
317 | * @since 0.1.0 |
318 | * @param bool $disabled Indicates whether the button is disabled. |
319 | * @return $this Returns the Button instance for method chaining. |
320 | */ |
321 | public function setDisabled( bool $disabled ): self { |
322 | $this->disabled = $disabled; |
323 | |
324 | return $this; |
325 | } |
326 | |
327 | /** |
328 | * Set additional HTML attributes for the button element. |
329 | * |
330 | * This method allows custom HTML attributes to be added to the button element, such as `id`, `data-*`, `aria-*`, |
331 | * or any other valid attributes. These attributes can be used to integrate the button with JavaScript, enhance |
332 | * accessibility, or provide additional metadata. |
333 | * |
334 | * The values of these attributes are automatically escaped to prevent XSS vulnerabilities. |
335 | * |
336 | * Example usage: |
337 | * |
338 | * $button->setAttributes([ |
339 | * 'id' => 'submit-button', |
340 | * 'data-toggle' => 'modal', |
341 | * 'aria-label' => 'Submit Form' |
342 | * ]); |
343 | * |
344 | * @since 0.1.0 |
345 | * @param array $attributes An associative array of HTML attributes. |
346 | * @return $this Returns the Button instance for method chaining. |
347 | */ |
348 | public function setAttributes( array $attributes ): self { |
349 | foreach ( $attributes as $key => $value ) { |
350 | $this->attributes[$key] = $value; |
351 | } |
352 | return $this; |
353 | } |
354 | |
355 | /** |
356 | * Build and return the Button component object. |
357 | * This method constructs the immutable Button object with all the properties set via the builder. |
358 | * |
359 | * @since 0.1.0 |
360 | * @return Button The constructed Button. |
361 | */ |
362 | public function build(): Button { |
363 | return new Button( |
364 | $this->id, |
365 | $this->label, |
366 | $this->action, |
367 | $this->size, |
368 | $this->type, |
369 | $this->weight, |
370 | $this->iconClass, |
371 | $this->iconOnly, |
372 | $this->disabled, |
373 | $this->attributes, |
374 | $this->renderer |
375 | ); |
376 | } |
377 | } |