Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
FieldBuilder
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 7
90
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
 setId
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setLabel
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setIsFieldset
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setFields
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
 build
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * FieldBuilder.php
4 *
5 * This file is part of the Codex design system, the official design system
6 * for Wikimedia projects. It provides the `Field` class, a builder for constructing
7 * form fields with labels, inputs, or controls using the Codex design system.
8 *
9 * A form field includes a label, an input or control, and an optional validation message.
10 *
11 * @category Builder
12 * @package  Codex\Builder
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\Builder;
20
21use InvalidArgumentException;
22use Wikimedia\Codex\Component\Field;
23use Wikimedia\Codex\Component\Label;
24use Wikimedia\Codex\Renderer\FieldRenderer;
25
26/**
27 * FieldBuilder
28 *
29 * This class implements the builder pattern to construct instances of Field.
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 */
40class FieldBuilder {
41
42    /**
43     * The ID for the fieldset.
44     */
45    protected string $id = '';
46
47    /**
48     * The label object for the field.
49     */
50    protected ?Label $label = null;
51
52    /**
53     * Indicates if the fields should be wrapped in a fieldset with a legend.
54     */
55    protected bool $isFieldset = false;
56
57    /**
58     * An array of fields (as HTML strings) included within the fieldset or div.
59     */
60    protected array $fields = [];
61
62    /**
63     * Additional HTML attributes for the fieldset or div element.
64     */
65    protected array $attributes = [];
66
67    /**
68     * The ID of the input or control element that the label is associated with.
69     */
70    protected string $inputId = '';
71
72    /**
73     * The renderer instance used to render the field.
74     */
75    protected FieldRenderer $renderer;
76
77    /**
78     * Constructor for the Field class.
79     *
80     * @param FieldRenderer $renderer The renderer to use for rendering the field.
81     */
82    public function __construct( FieldRenderer $renderer ) {
83        $this->renderer = $renderer;
84    }
85
86    /**
87     * Set the label's HTML ID attribute.
88     *
89     * @since 0.1.0
90     * @param string $id The ID for the field element.
91     * @return $this
92     */
93    public function setId( string $id ): self {
94        $this->id = $id;
95
96        return $this;
97    }
98
99    /**
100     * Set the label for the field.
101     *
102     * This method accepts a Label object which provides a descriptive label for the field.
103     *
104     * @since 0.1.0
105     * @param Label $label The Label object for the field.
106     * @return $this Returns the Checkbox instance for method chaining.
107     */
108    public function setLabel( Label $label ): self {
109        $this->label = $label;
110
111        return $this;
112    }
113
114    /**
115     * Set whether the fields should be wrapped in a fieldset with a legend.
116     *
117     * When set to `true`, this method wraps the fields in a `<fieldset>` element with a `<legend>`.
118     * If set to `false`, the fields are wrapped in a `<div>` with a `<label>` instead.
119     *
120     * @since 0.1.0
121     * @param bool $isFieldset Whether to wrap fields in a fieldset.
122     * @return $this Returns the Field instance for method chaining.
123     */
124    public function setIsFieldset( bool $isFieldset ): self {
125        $this->isFieldset = $isFieldset;
126
127        return $this;
128    }
129
130    /**
131     * Set the fields within the fieldset.
132     *
133     * This method accepts an array of fields (as HTML strings) to be included within the fieldset or a `<div>`.
134     * It allows grouping of related fields together under a common legend or label for better organization.
135     *
136     * @since 0.1.0
137     * @param array $fields The array of fields to include in the fieldset.
138     * @return $this Returns the Field instance for method chaining.
139     */
140    public function setFields( array $fields ): self {
141        $this->fields = $fields;
142
143        return $this;
144    }
145
146    /**
147     * Set additional HTML attributes for the fieldset or div element.
148     *
149     * This method allows custom HTML attributes to be added to the fieldset or div element, such as `id`, `data-*`,
150     * `aria-*`, or any other valid attributes. These attributes can be used to further customize the fieldset or div,
151     * enhance accessibility, or provide additional metadata.
152     *
153     * The values of these attributes are automatically escaped to prevent XSS vulnerabilities.
154     *
155     * Example usage:
156     *
157     *     $field->setAttributes([
158     *         'id' => 'user-info-fieldset',
159     *         'data-category' => 'user-data',
160     *         'aria-labelledby' => 'legend-user-info'
161     *     ]);
162     *
163     * @since 0.1.0
164     * @param array $attributes An associative array of HTML attributes.
165     * @return $this Returns the Field instance for method chaining.
166     */
167    public function setAttributes( array $attributes ): self {
168        foreach ( $attributes as $key => $value ) {
169            $this->attributes[$key] = $value;
170        }
171
172        return $this;
173    }
174
175    /**
176     * Build and return the Field component object.
177     * This method constructs the immutable Field object with all the properties set via the builder.
178     *
179     * @since 0.1.0
180     * @return Field The constructed Field.
181     */
182    public function build(): Field {
183        if ( !$this->label ) {
184            throw new InvalidArgumentException( "The 'label' is required for Field." );
185        }
186
187        return new Field(
188            $this->id,
189            $this->label,
190            $this->isFieldset,
191            $this->fields,
192            $this->attributes,
193            $this->renderer,
194        );
195    }
196}