Test Failed
Pull Request — master (#35)
by Wilmer
02:16
created

ButtonDropdown::encodeLabels()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 5
ccs 0
cts 3
cp 0
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Bootstrap5;
6
7
use Yiisoft\Arrays\ArrayHelper;
8
use Yiisoft\Factory\Exceptions\InvalidConfigException;
9
use Yiisoft\Html\Html;
10
11
/**
12
 * ButtonDropdown renders a group or split button dropdown bootstrap component.
13
 *
14
 * For example,
15
 *
16
 * ```php
17
 * // a button group using Dropdown widget
18
 * echo ButtonDropdown::widget()
19
 *     ->label('Action')
20
 *     ->dropdown'([
21
 *         'items' => [
22
 *             ['label' => 'DropdownA', 'url' => '/'],
23
 *             ['label' => 'DropdownB', 'url' => '#'],
24
 *         ],
25
 *     ]);
26
 * ```
27
 */
28
final class ButtonDropdown extends Widget
29
{
30
    /**
31
     * The css class part of dropdown
32
     */
33
    public const DIRECTION_DOWN = 'down';
34
35
    /**
36
     * The css class part of dropleft
37
     */
38
    public const DIRECTION_LEFT = 'left';
39
40
    /**
41
     * The css class part of dropright
42
     */
43
    public const DIRECTION_RIGHT = 'right';
44
45
    /**
46
     * The css class part of dropup
47
     */
48
    public const DIRECTION_UP = 'up';
49
50
    private string $label = 'Button';
51
    private array $options = [];
52
    private array $buttonOptions = [];
53
    private array $dropdown = [];
54
    private string $direction = self::DIRECTION_DOWN;
55
    private bool $split = false;
56
    private string $tagName = 'button';
57
    private bool $encodeLabels = true;
58
    private bool $encodeTags = false;
59
    private string $dropdownClass = Dropdown::class;
60
    private bool $renderContainer = true;
61 3
62
    public function run(): string
63
    {
64 3
        if (!isset($this->dropdown['items'])) {
65 3
            return '';
66
        }
67 3
68 3
        /** Set options id to button options id to ensure correct css selector in plugin initialisation */
69
        if (empty($this->options['id'])) {
70
            $id = $this->getId();
71 3
72
            $this->options['id'] = "{$id}-button-dropdown";
73 3
            $this->buttonOptions['id'] = "{$id}-button";
74
        }
75 3
76
        if ($this->encodeTags === false) {
77 3
            $this->options = array_merge($this->options, ['encode' => false]);
78 3
        }
79 3
80
        $html = $this->renderButton() . "\n" . $this->renderDropdown();
81
82 3
        if ($this->renderContainer) {
83
            /** @psalm-suppress InvalidArgument */
84 3
            Html::addCssClass($this->options, ['widget' => 'drop' . $this->direction, 'btn-group']);
85
86
            $options = $this->options;
87
            $tag = ArrayHelper::remove($options, 'tag', 'div');
88
            $html = Html::tag($tag, $html, $options);
89
        }
90
91
        $this->registerPlugin('dropdown', $this->options);
92
93
        return $html;
94 3
    }
95
96 3
    /**
97
     * The HTML attributes of the button.
98 3
     *
99
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
100 3
     *
101 3
     * @param array $value
102
     *
103
     * @return $this
104 3
     */
105 1
    public function withButtonOptions(array $value): self
106
    {
107 1
        $new = clone $this;
108 1
        $new->buttonOptions = $value;
109 1
110
        return $new;
111 1
    }
112
113 1
    /**
114
     * The drop-direction of the widget.
115 1
     *
116 1
     * Possible values are 'left', 'right', 'up', or 'down' (default)
117 1
     *
118 1
     * @param string $value
119 1
     *
120
     * @return $this
121 2
     */
122
    public function withDirection(string $value): self
123 2
    {
124
        $new = clone $this;
125 2
        $new->direction = $value;
126 2
127 2
        return $new;
128 2
    }
129
130
    /**
131 3
     * The configuration array for example:
132
     *
133
     * ```php
134
     *    [
135
     *        'items' => [
136 3
     *            ['label' => 'DropdownA', 'url' => '/'],
137 3
     *            ['label' => 'DropdownB', 'url' => '#'],
138 3
     *        ],
139 3
     *    ]
140 3
     * ```
141 3
     *
142 3
     * {@see Dropdown}
143
     *
144
     * @param array $value
145
     *
146
     * @return $this
147
     */
148
    public function withDropdown(array $value): self
149
    {
150 3
        $new = clone $this;
151
        $new->dropdown = $value;
152 3
153
        return $new;
154 3
    }
155 3
156 3
    /**
157
     * Name of a class to use for rendering dropdowns withing this widget. Defaults to {@see Dropdown}.
158
     *
159
     * @param string $value
160
     *
161
     * @return $this
162
     */
163
    public function withDropdownClass(string $value): self
164
    {
165
        $new = clone $this;
166
        $new->dropdownClass = $value;
167
168
        return $new;
169
    }
170
171
    /**
172
     * Whether the label should be HTML-encoded.
173
     *
174
     * @param bool $value
175
     *
176
     * @return $this
177
     */
178
    public function withoutEncodeLabels(bool $value = false): self
179
    {
180
        $new = clone $this;
181
        $new->encodeLabels = $value;
182
183
        return $new;
184 3
    }
185
186 3
    /**
187
     * The button label.
188 3
     *
189
     * @param string $value
190
     *
191
     * @return $this
192
     */
193
    public function withLabel(string $value): self
194
    {
195
        $new = clone $this;
196
        $new->label = $value;
197
198
        return $new;
199
    }
200
201
    /**
202
     * The HTML attributes for the container tag. The following special options are recognized.
203
     *
204
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
205
     *
206
     * @param array $value
207
     *
208
     * @return $this
209 3
     */
210
    public function withOptions(array $value): self
211 3
    {
212
        $new = clone $this;
213 3
        $new->options = $value;
214
215
        return $new;
216
    }
217
218
    /**
219
     * Whether to render the container using the {@see options} as HTML attributes. If set to `false`, the container
220
     * element enclosing the button and dropdown will NOT be rendered.
221
     *
222
     * @param bool $value
223
     *
224
     * @return $this
225
     */
226
    public function withoutRenderContainer(bool $value = false): self
227
    {
228
        $new = clone $this;
229
        $new->renderContainer = $value;
230
231
        return $new;
232
    }
233
234
    /**
235
     * Whether to display a group of split-styled button group.
236
     *
237
     * @param bool $value
238
     *
239
     * @return $this
240
     */
241
    public function withSplit(bool $value = true): self
242
    {
243
        $new = clone $this;
244
        $new->split = $value;
245
246
        return $new;
247
    }
248
249
    /**
250
     * The tag to use to render the button.
251 3
     *
252
     * @param string $value
253 3
     *
254
     * @return $this
255 3
     */
256
    public function withTagName(string $value): self
257
    {
258
        $new = clone $this;
259
        $new->tagName = $value;
260
261
        return $new;
262
    }
263
264
    /**
265
     * Allows you to enable or disable the encoding tags html.
266
     *
267 1
     * @param bool $value
268
     *
269 1
     * @return self
270
     */
271 1
    public function withEncodeTags(bool $value = true): self
272
    {
273
        $new = clone $this;
274
        $new->encodeTags = $value;
275
276
        return $new;
277
    }
278
279
    /**
280
     * Generates the button dropdown.
281
     *
282
     * @throws InvalidConfigException
283
     *
284
     * @return string the rendering result.
285
     */
286
    private function renderButton(): string
287
    {
288
        Html::addCssClass($this->buttonOptions, 'btn');
289
290
        $label = $this->label;
291
292
        if ($this->encodeLabels !== false) {
293
            $label = Html::encode($label);
294
        }
295
296 1
        if ($this->split) {
297
            $buttonOptions = $this->buttonOptions;
298 1
299
            $this->buttonOptions['data-bs-toggle'] = 'dropdown';
300 1
            $this->buttonOptions['aria-haspopup'] = 'true';
301
            $this->buttonOptions['aria-expanded'] = 'false';
302
303
            Html::addCssClass($this->buttonOptions, ['toggle' => 'dropdown-toggle dropdown-toggle-split']);
304
305
            unset($buttonOptions['id']);
306
307
            $splitButton = Button::widget()
308
                ->withLabel('<span class="sr-only">Toggle Dropdown</span>')
0 ignored issues
show
Bug introduced by
The method withLabel() does not exist on Yiisoft\Widget\Widget. It seems like you code against a sub-type of Yiisoft\Widget\Widget such as Yiisoft\Yii\Bootstrap5\Button or Yiisoft\Yii\Bootstrap5\ButtonDropdown. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

308
                ->/** @scrutinizer ignore-call */ withLabel('<span class="sr-only">Toggle Dropdown</span>')
Loading history...
309
                ->withoutEncodeLabels()
310
                ->withOptions($this->buttonOptions)
311
                ->render();
312
        } else {
313
            $buttonOptions = $this->buttonOptions;
314
315
            Html::addCssClass($buttonOptions, ['toggle' => 'dropdown-toggle']);
316
317
            $buttonOptions['data-bs-toggle'] = 'dropdown';
318
            $buttonOptions['aria-haspopup'] = 'true';
319
            $buttonOptions['aria-expanded'] = 'false';
320
            $splitButton = '';
321
        }
322
323
        if (!isset($buttonOptions['href']) && ($this->tagName === 'a')) {
324
            $buttonOptions['href'] = '#';
325
            $buttonOptions['role'] = 'button';
326
        }
327
328
        return Button::widget()
329
            ->withTagName($this->tagName)
0 ignored issues
show
Bug introduced by
The method withTagName() does not exist on Yiisoft\Widget\Widget. It seems like you code against a sub-type of Yiisoft\Widget\Widget such as Yiisoft\Yii\Bootstrap5\Button or Yiisoft\Yii\Bootstrap5\ButtonDropdown. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

329
            ->/** @scrutinizer ignore-call */ withTagName($this->tagName)
Loading history...
330
            ->withLabel($label)
331
            ->withOptions($buttonOptions)
332
            ->withoutEncodeLabels()
333
            ->render()
334
            . "\n" . $splitButton;
335
    }
336
337
    /**
338
     * Generates the dropdown menu.
339
     *
340
     * @return string the rendering result.
341
     */
342
    private function renderDropdown(): string
343
    {
344
        $dropdownClass = $this->dropdownClass;
345
346
        return $dropdownClass::widget()
347
            ->withItems($this->dropdown['items'])
348
            ->withEncodeLabels($this->encodeLabels)
349
            ->render();
350
    }
351
}
352