Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 23
CRAP
0.00% covered (danger)
0.00%
0 / 1
Label
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 23
650
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLabelText
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getInputId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isOptional
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isVisuallyHidden
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isLegend
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDescription
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDescriptionId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isDisabled
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getIconClass
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAttributes
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setId
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setLabelText
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setInputId
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setOptional
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setVisuallyHidden
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setIsLegend
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setDescription
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setDescriptionId
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 setDisabled
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setIconClass
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setAttributes
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2declare( strict_types = 1 );
3
4/**
5 * Label.php
6 *
7 * This file is part of the Codex design system, the official design system for Wikimedia projects.
8 * It contains the definition and implementation of the `Label` class, responsible for managing
9 * the behavior and properties of the corresponding component.
10 *
11 * @category Component
12 * @package  Codex\Component
13 * @since    0.1.0
14 * @author   Doğu Abaris <abaris@null.net>
15 * @license  https://www.gnu.org/copyleft/gpl.html GPL-2.0-or-later
16 * @link     https://doc.wikimedia.org/codex/main/ Codex Documentation
17 */
18
19namespace Wikimedia\Codex\Component;
20
21use Wikimedia\Codex\Contract\Component;
22use Wikimedia\Codex\Renderer\LabelRenderer;
23
24/**
25 * Label
26 *
27 * @category Component
28 * @package  Codex\Component
29 * @since    0.1.0
30 * @author   Doğu Abaris <abaris@null.net>
31 * @license  https://www.gnu.org/copyleft/gpl.html GPL-2.0-or-later
32 * @link     https://doc.wikimedia.org/codex/main/ Codex Documentation
33 */
34class Label extends Component {
35    private ?string $id = null;
36
37    public function __construct(
38        LabelRenderer $renderer,
39        private string|HtmlSnippet $labelText,
40        private string $inputId,
41        private bool $optional,
42        private bool $visuallyHidden,
43        private bool $isLegend,
44        private string|HtmlSnippet $description,
45        private ?string $descriptionId,
46        private bool $disabled,
47        private ?string $iconClass,
48        private array $attributes
49    ) {
50        parent::__construct( $renderer );
51    }
52
53    /**
54     * Get the ID of the label element.
55     *
56     * @since 0.1.0
57     * @return string|null The ID of the label, or null if not set.
58     */
59    public function getId(): ?string {
60        return $this->id;
61    }
62
63    /**
64     * Get the text displayed inside the label.
65     *
66     * This method returns the text displayed inside the label. The label text provides
67     * a descriptive title for the associated input field.
68     *
69     * @since 0.1.0
70     * @return string|HtmlSnippet The text of the label.
71     */
72    public function getLabelText(): string|HtmlSnippet {
73        return $this->labelText;
74    }
75
76    /**
77     * Get the ID of the input/control this label is associated with.
78     *
79     * This method returns the ID of the input element that this label is associated with. The ID
80     * is crucial for linking the label to its corresponding input, ensuring accessibility.
81     *
82     * @since 0.1.0
83     * @return string The ID of the input element.
84     */
85    public function getInputId(): string {
86        return $this->inputId;
87    }
88
89    /**
90     * Check if the associated input field is optional.
91     *
92     * This method returns a boolean indicating whether the associated input field is optional.
93     * If true, an "(optional)" flag is typically displayed next to the label text.
94     *
95     * @since 0.1.0
96     * @return bool True if the input field is optional, false otherwise.
97     */
98    public function isOptional(): bool {
99        return $this->optional;
100    }
101
102    /**
103     * Check if the label is visually hidden but accessible to screen readers.
104     *
105     * This method returns a boolean indicating whether the label is visually hidden
106     * while still being accessible to screen readers. This is useful for forms where
107     * labels need to be accessible but not displayed.
108     *
109     * @since 0.1.0
110     * @return bool True if the label is visually hidden, false otherwise.
111     */
112    public function isVisuallyHidden(): bool {
113        return $this->visuallyHidden;
114    }
115
116    /**
117     * Check if the label is rendered as a `<legend>` element.
118     *
119     * This method returns a boolean indicating whether the label is rendered as a `<legend>`
120     * element, typically used within a `<fieldset>` for grouping related inputs.
121     *
122     * @since 0.1.0
123     * @return bool True if the label is rendered as a `<legend>`, false otherwise.
124     */
125    public function isLegend(): bool {
126        return $this->isLegend;
127    }
128
129    /**
130     * Get the description text associated with the label.
131     *
132     * This method returns the description text that provides additional information about the
133     * input field. The description is linked to the input via the `aria-describedby` attribute.
134     *
135     * @since 0.1.0
136     * @return string|HtmlSnippet The description text for the label.
137     */
138    public function getDescription(): string|HtmlSnippet {
139        return $this->description;
140    }
141
142    /**
143     * Get the ID of the description element.
144     *
145     * This method returns the ID of the description element, which is useful for associating
146     * the description with an input via the `aria-describedby` attribute.
147     *
148     * @since 0.1.0
149     * @return string|null The ID for the description element, or null if not set.
150     */
151    public function getDescriptionId(): ?string {
152        return $this->descriptionId;
153    }
154
155    /**
156     * Check if the label is for a disabled field or input.
157     *
158     * This method returns a boolean indicating whether the label is associated with a disabled
159     * input field, applying the appropriate styles.
160     *
161     * @since 0.1.0
162     * @return bool True if the label is for a disabled input, false otherwise.
163     */
164    public function isDisabled(): bool {
165        return $this->disabled;
166    }
167
168    /**
169     * Get the icon class used before the label text.
170     *
171     * This method returns the CSS class for the icon displayed before the label text, if applicable.
172     * The icon enhances the visual appearance of the label.
173     *
174     * @since 0.1.0
175     * @return string|null The CSS class for the icon, or null if no icon is set.
176     */
177    public function getIconClass(): ?string {
178        return $this->iconClass;
179    }
180
181    /**
182     * Get the additional HTML attributes for the label element.
183     *
184     * This method returns an associative array of custom HTML attributes that are applied
185     * to the label element. These attributes can be used for customization or accessibility.
186     *
187     * @since 0.1.0
188     * @return array The additional attributes as an array.
189     */
190    public function getAttributes(): array {
191        return $this->attributes;
192    }
193
194    /**
195     * Set the label's HTML ID attribute.
196     *
197     * @deprecated Use setAttributes() to set the ID
198     * @since 0.1.0
199     * @param string $id The ID for the label element.
200     * @return $this
201     */
202    public function setId( string $id ): self {
203        $this->id = $id;
204
205        return $this;
206    }
207
208    /**
209     * Set the label text.
210     *
211     * This method specifies the text that will be displayed inside the label.
212     * The label text provides a descriptive title for the associated input field.
213     *
214     * @since 0.1.0
215     * @param string|HtmlSnippet $labelText The text of the label.
216     * @return $this Returns the Label instance for method chaining.
217     */
218    public function setLabelText( string|HtmlSnippet $labelText ): self {
219        $this->labelText = $labelText;
220
221        return $this;
222    }
223
224    /**
225     * Set the ID of the input/control this label is associated with.
226     *
227     * This method sets the 'for' attribute of the label, linking it to an input element.
228     * This connection is important for accessibility and ensures that clicking the label focuses the input.
229     *
230     * Example usage:
231     *
232     *     $label->setInputId('username');
233     *
234     * @since 0.1.0
235     * @param string $inputId The ID of the input element.
236     * @return $this Returns the Label instance for method chaining.
237     */
238    public function setInputId( string $inputId ): self {
239        $this->inputId = $inputId;
240
241        return $this;
242    }
243
244    /**
245     * Set the optional flag.
246     *
247     * This method indicates whether the associated input field is optional.
248     * If true, an "(optional)" flag will be displayed next to the label text.
249     *
250     * Example usage:
251     *
252     *     $label->setOptionalFlag(true);
253     *
254     * @since 0.1.0
255     * @param bool $optional Whether the label is for an optional input.
256     * @return $this Returns the Label instance for method chaining.
257     */
258    public function setOptional( bool $optional ): self {
259        $this->optional = $optional;
260
261        return $this;
262    }
263
264    /**
265     * Set whether the label should be visually hidden.
266     *
267     * This method determines whether the label should be visually hidden while still being accessible to screen
268     * readers. Useful for forms where labels need to be read by assistive technologies but not displayed.
269     *
270     * Example usage:
271     *
272     *     $label->setVisuallyHidden(true);
273     *
274     * @since 0.1.0
275     * @param bool $visuallyHidden Whether the label should be visually hidden.
276     * @return $this Returns the Label instance for method chaining.
277     */
278    public function setVisuallyHidden( bool $visuallyHidden ): self {
279        $this->visuallyHidden = $visuallyHidden;
280
281        return $this;
282    }
283
284    /**
285     * Set whether this component should output a `<legend>` element.
286     *
287     * This method determines whether the label should be rendered as a `<legend>` element,
288     * typically used within a `<fieldset>` for grouping related inputs.
289     *
290     * Example usage:
291     *
292     *     $label->setIsLegend(true);
293     *
294     * @since 0.1.0
295     * @param bool $isLegend Whether to render the label as a `<legend>`.
296     * @return $this Returns the Label instance for method chaining.
297     */
298    public function setIsLegend( bool $isLegend ): self {
299        $this->isLegend = $isLegend;
300
301        return $this;
302    }
303
304    /**
305     * Set the description text for the label.
306     *
307     * This method adds a short description below the label, providing additional information about the input field.
308     * The description is linked to the input via the `aria-describedby` attribute for accessibility.
309     *
310     * Example usage:
311     *
312     *     $label->setDescriptionText('Please enter a valid email.');
313     *
314     * @since 0.1.0
315     * @param string|HtmlSnippet $description The description text for the label.
316     * @return $this Returns the Label instance for method chaining.
317     */
318    public function setDescription( string|HtmlSnippet $description ): self {
319        $this->description = $description;
320
321        return $this;
322    }
323
324    /**
325     * Set the ID of the description element.
326     *
327     * This method sets the ID attribute for the description element, which is useful for associating
328     * the description with an input via the `aria-describedby` attribute.
329     *
330     * Example usage:
331     *
332     *     $label->setDescriptionId('username-desc');
333     *
334     * @since 0.1.0
335     * @param string|null $descriptionId The ID for the description element.
336     * @return $this Returns the Label instance for method chaining.
337     */
338    public function setDescriptionId( ?string $descriptionId ): self {
339        $this->descriptionId = $descriptionId ?: null;
340
341        return $this;
342    }
343
344    /**
345     * Set whether the label is for a disabled field or input.
346     *
347     * This method marks the label as associated with a disabled input, applying the appropriate styles.
348     *
349     * Example usage:
350     *
351     *     $label->setDisabled(true);
352     *
353     * @since 0.1.0
354     * @param bool $disabled Whether the label is for a disabled input.
355     * @return $this Returns the Label instance for method chaining.
356     */
357    public function setDisabled( bool $disabled ): self {
358        $this->disabled = $disabled;
359
360        return $this;
361    }
362
363    /**
364     * Set an icon before the label text.
365     *
366     * This method allows for an icon to be displayed before the label text, specified by a CSS class.
367     * The icon enhances the visual appearance of the label.
368     *
369     * Example usage:
370     *
371     *     $label->setIcon('icon-class-name');
372     *
373     * @since 0.1.0
374     * @param string|null $iconClass The CSS class for the icon.
375     * @return $this Returns the Label instance for method chaining.
376     */
377    public function setIconClass( ?string $iconClass ): self {
378        $this->iconClass = $iconClass;
379
380        return $this;
381    }
382
383    /**
384     * Set additional HTML attributes for the label element.
385     *
386     * This method allows custom HTML attributes to be added to the label element, such as `id`, `class`, or `data-*`
387     * attributes. These attributes are automatically escaped to prevent XSS vulnerabilities.
388     *
389     * Example usage:
390     *
391     *     $label->setAttributes(['class' => 'custom-label-class', 'data-info' => 'additional-info']);
392     *
393     * @since 0.1.0
394     * @param array $attributes An associative array of HTML attributes.
395     * @return $this Returns the Label instance for method chaining.
396     */
397    public function setAttributes( array $attributes ): self {
398        foreach ( $attributes as $key => $value ) {
399            $this->attributes[$key] = $value;
400        }
401
402        return $this;
403    }
404}