Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
SelectBuilder
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 8
306
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
 setOptions
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
30
 setOptGroups
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
30
 setAttributes
0.00% covered (danger)
0.00%
0 / 3
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
 setSelectedOption
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 build
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * SelectBuilder.php
4 *
5 * This file is part of the Codex design system, the official design system
6 * for Wikimedia projects. It provides the `Select` class, a builder for constructing
7 * select elements using the Codex design system.
8 *
9 * A Select is an input with a dropdown menu of predefined, selectable items.
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\Select;
23use Wikimedia\Codex\Renderer\SelectRenderer;
24
25/**
26 * SelectBuilder
27 *
28 * This class implements the builder pattern to construct instances of Select.
29 * It provides a fluent interface for setting various properties and building the
30 * final immutable object with predefined configurations and immutability.
31 *
32 * @category Builder
33 * @package  Codex\Builder
34 * @since    0.1.0
35 * @author   Doğu Abaris <abaris@null.net>
36 * @license  https://www.gnu.org/copyleft/gpl.html GPL-2.0-or-later
37 * @link     https://doc.wikimedia.org/codex/main/ Codex Documentation
38 */
39class SelectBuilder {
40
41    /**
42     * The ID for the select.
43     */
44    protected string $id = '';
45
46    /**
47     * The options available in the select dropdown.
48     */
49    protected array $options = [];
50
51    /**
52     * The optGroups that group options under labels in the select dropdown.
53     */
54    protected array $optGroups = [];
55
56    /**
57     * The selected option value.
58     */
59    protected ?string $selectedOption = null;
60
61    /**
62     * Additional HTML attributes for the `<select>` element.
63     */
64    protected array $attributes = [];
65
66    /**
67     * Indicates if the select element is disabled.
68     */
69    protected bool $disabled = false;
70
71    /**
72     * The renderer instance used to render the select.
73     */
74    protected SelectRenderer $renderer;
75
76    /**
77     * Constructor for the SelectBuilder class.
78     *
79     * @param SelectRenderer $renderer The renderer to use for rendering the select.
80     */
81    public function __construct( SelectRenderer $renderer ) {
82        $this->renderer = $renderer;
83    }
84
85    /**
86     * Set the Selects HTML ID attribute.
87     *
88     * @since 0.1.0
89     * @param string $id The ID for the Select element.
90     * @return $this
91     */
92    public function setId( string $id ): self {
93        $this->id = $id;
94
95        return $this;
96    }
97
98    /**
99     * Set one or more options for the select element.
100     *
101     * This method allows one or more options to be added to the select dropdown.
102     * Each option can be provided as a simple key-value pair, or as an array with `value`, `text`,
103     * and `selected` keys for more complex options.
104     *
105     * Example usage:
106     *
107     *     // Using key-value pairs:
108     *     $select->setOptions([
109     *         'value1' => 'Label 1',
110     *         'value2' => 'Label 2'
111     *     ]);
112     *
113     *     // Using an array for more complex options:
114     *     $select->setOptions([
115     *         ['value' => 'value1', 'text' => 'Label 1', 'selected' => true],
116     *         ['value' => 'value2', 'text' => 'Label 2']
117     *     ]);
118     *
119     * @since 0.1.0
120     * @param array $options An array of options, either as key-value pairs or
121     *                       arrays with `value`, `text`, and `selected` keys.
122     * @return $this Returns the Select instance for method chaining.
123     */
124    public function setOptions( array $options ): self {
125        if ( !$options ) {
126            throw new InvalidArgumentException( 'At least one option is required for the select element.' );
127        }
128
129        foreach ( $options as $key => $option ) {
130            if ( is_string( $key ) ) {
131                // Handle key-value pairs for simple options
132                $this->options[] = ( new OptionBuilder )
133                    ->setValue( $key )
134                    ->setText( $option )->build();
135            } elseif ( is_array( $option ) ) {
136                // Handle more complex array structure for options
137                $this->options[] = ( new OptionBuilder )
138                    ->setValue( $option['value'] )
139                    ->setText( $option['text'] )
140                    ->setSelected( $option['selected'] ?? false )->build();
141            }
142        }
143
144        return $this;
145    }
146
147    /**
148     * Set the optGroups for the select element.
149     *
150     * This method allows options to be grouped under labels in the select dropdown.
151     * Each optGroup can contain options that are either key-value pairs or arrays with `value`,
152     * `text`, and `selected` keys for more complex options.
153     *
154     * Example usage:
155     *
156     *     $select->setOptGroups([
157     *         'Group 1' => [
158     *             'value1' => 'Option 1',
159     *             ['value' => 'value2', 'text' => 'Option 2', 'selected' => true]
160     *         ],
161     *         'Group 2' => [
162     *             'value3' => 'Option 3',
163     *             'value4' => 'Option 4'
164     *         ]
165     *     ]);
166     *
167     * @since 0.1.0
168     * @param array $optGroups An associative array of optGroups where keys are labels and values are arrays of options.
169     * @return $this Returns the Select instance for method chaining.
170     */
171    public function setOptGroups( array $optGroups ): self {
172        foreach ( $optGroups as $label => $groupOptions ) {
173            $group = [];
174            foreach ( $groupOptions as $key => $option ) {
175                if ( is_string( $key ) ) {
176                    // Handle key-value pairs for options in the group
177                    $group[] = ( new OptionBuilder )
178                        ->setValue( $key )
179                        ->setText( $option )->build();
180                } elseif ( is_array( $option ) ) {
181                    // Handle more complex array structure for group options
182                    $group[] = ( new OptionBuilder )
183                        ->setValue( $option['value'] )
184                        ->setText( $option['text'] )
185                        ->setSelected( $option['selected'] ?? false )->build();
186                }
187            }
188            $this->optGroups[$label] = $group;
189        }
190
191        return $this;
192    }
193
194    /**
195     * Set additional HTML attributes for the `<select>` element.
196     *
197     * This method allows custom HTML attributes to be added to the `<select>` element,
198     * such as `id`, `data-*`, `aria-*`, or any other valid attributes. These attributes can be used
199     * to enhance accessibility or integrate with JavaScript.
200     *
201     * Example usage:
202     *
203     *     $select->setAttributes([
204     *         'id' => 'select-example',
205     *         'data-category' => 'selection',
206     *     ]);
207     *
208     * @since 0.1.0
209     * @param array $attributes An associative array of HTML attributes.
210     * @return $this Returns the Select instance for method chaining.
211     */
212    public function setAttributes( array $attributes ): self {
213        foreach ( $attributes as $key => $value ) {
214            $this->attributes[$key] = $value;
215        }
216        return $this;
217    }
218
219    /**
220     * Set whether the select element should be disabled.
221     *
222     * This method disables the select element, preventing user interaction.
223     * When called with `true`, the `disabled` attribute is added to the `<select>` element.
224     *
225     * Example usage:
226     *
227     *     $select->setDisabled(true);
228     *
229     * @since 0.1.0
230     * @param bool $disabled Indicates whether the select element should be disabled.
231     * @return $this Returns the Select instance for method chaining.
232     */
233    public function setDisabled( bool $disabled ): self {
234        $this->disabled = $disabled;
235
236        return $this;
237    }
238
239    /**
240     * Set the selected option for the select element.
241     *
242     * This method specifies which option should be selected by default when the select element is rendered.
243     *
244     * @since 0.1.0
245     * @param string|null $value The value of the option to be selected, or null to unset the selection.
246     * @return $this Returns the Select instance for method chaining.
247     */
248    public function setSelectedOption( ?string $value ): self {
249        $this->selectedOption = $value;
250
251        return $this;
252    }
253
254    /**
255     * Build and return the Select component object.
256     * This method constructs the immutable Select object with all the properties set via the builder.
257     *
258     * @since 0.1.0
259     * @return Select The constructed Select.
260     */
261    public function build(): Select {
262        return new Select(
263            $this->id,
264            $this->options,
265            $this->optGroups,
266            $this->selectedOption,
267            $this->attributes,
268            $this->disabled,
269            $this->renderer
270        );
271    }
272}