Test Failed
Pull Request — master (#18)
by Wilmer
02:38
created

Modal::footer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
ccs 1
cts 1
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Bootstrap5;
6
7
use JsonException;
8
use Yiisoft\Arrays\ArrayHelper;
9
use Yiisoft\Html\Html;
10
11
use function array_merge;
12
13
/**
14
 * Modal renders a modal window that can be toggled by clicking on a button.
15
 *
16
 * The following example will show the content enclosed between the {@see begin()} and {@see end()} calls within the
17
 * modal window:
18
 *
19
 * ```php
20
 * Modal::widget()
21
 *     ->title('<h2>Hello world</h2>')
22
 *     ->toggleButton(['label' => 'click me'])
23
 *     ->begin();
24
 *
25
 * echo 'Say hello...';
26
 *
27
 * echo Modal::end();
28
 * ```
29
 */
30
class Modal extends Widget
31
{
32
    /**
33
     * The additional css class of large modal
34
     */
35
    public const SIZE_LARGE = 'modal-lg';
36
37
    /**
38
     * The additional css class of small modal
39
     */
40
    public const SIZE_SMALL = 'modal-sm';
41
42
    /**
43
     * The additional css class of default modal
44
     */
45
    public const SIZE_DEFAULT = '';
46
47
    private ?string $title = null;
48
    private array $titleOptions = [];
49
    private array $headerOptions = [];
50
    private array $bodyOptions = [];
51
    private ?string $footer = null;
52
    private array $footerOptions = [];
53
    private ?string $size = null;
54
    private array $closeButton = [];
55
    private bool $closeButtonEnabled = true;
56
    private array $toggleButton = [];
57
    private bool $toggleButtonEnabled = true;
58
    private array $options = [];
59
60 3
    public function begin(): ?string
61
    {
62 3
        parent::begin();
63 3
64
        if (!isset($this->options['id'])) {
65
            $this->options['id'] = "{$this->getId()}-modal";
66 3
        }
67
68 3
        $this->initOptions();
69
70 3
        return
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->renderTogg...renderBodyBegin() . ' ' returns the type string which is incompatible with the return type mandated by Yiisoft\Widget\Widget::begin() of Yiisoft\Widget\Widget.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
71 3
            $this->renderToggleButton() . "\n" .
72 3
            Html::beginTag('div', $this->options) . "\n" .
73 3
            Html::beginTag('div', ['class' => 'modal-dialog ' . $this->size]) . "\n" .
74 3
            Html::beginTag('div', ['class' => 'modal-content']) . "\n" .
75 3
            $this->renderHeader() . "\n" .
76
            $this->renderBodyBegin() . "\n";
77 3
    }
78
79
    protected function run(): string
80 3
    {
81
        $this->registerPlugin('modal', $this->options);
82 3
83
        return
84 3
            "\n" . $this->renderBodyEnd() .
85 3
            "\n" . $this->renderFooter() .
86 3
            "\n" . Html::endTag('div') . // modal-content
87 3
            "\n" . Html::endTag('div') . // modal-dialog
88 3
            "\n" . Html::endTag('div');
89
    }
90 3
91
    /**
92 3
     * Renders the header HTML markup of the modal.
93
     *
94
     * @throws JsonException
95
     *
96
     * @return string the rendering result
97
     */
98
    protected function renderHeader(): string
99
    {
100
        $button = $this->renderCloseButton();
101
102 3
        if ($this->title !== null) {
103
            Html::addCssClass($this->titleOptions, ['widget' => 'modal-title']);
104 3
            $header = Html::tag('h5', $this->title, $this->titleOptions);
105
        } else {
106 3
            $header = '';
107 2
        }
108 2
109
        if ($button !== null) {
110 1
            $header .= "\n" . $button;
111
        } elseif ($header === '') {
112
            return '';
113 3
        }
114 2
115 1
        Html::addCssClass($this->headerOptions, ['widget' => 'modal-header']);
116 1
117
        return Html::tag('div', "\n" . $header . "\n", $this->headerOptions);
118
    }
119 2
120
    /**
121 2
     * Renders the opening tag of the modal body.
122
     *
123
     * @throws JsonException
124
     *
125
     * @return string the rendering result
126
     */
127
    protected function renderBodyBegin(): string
128
    {
129
        Html::addCssClass($this->bodyOptions, ['widget' => 'modal-body']);
130
131 3
        return Html::beginTag('div', $this->bodyOptions);
132
    }
133 3
134
    /**
135 3
     * Renders the closing tag of the modal body.
136
     *
137
     * @return string the rendering result
138
     */
139
    protected function renderBodyEnd(): string
140
    {
141
        return Html::endTag('div');
142
    }
143 3
144
    /**
145 3
     * Renders the HTML markup for the footer of the modal.
146
     *
147
     * @throws JsonException
148
     *
149
     * @return string the rendering result
150
     */
151
    protected function renderFooter(): ?string
152
    {
153
        if ($this->footer === null) {
154
            return null;
155 3
        }
156
157 3
        Html::addCssClass($this->footerOptions, ['widget' => 'modal-footer']);
158 1
        return Html::tag('div', "\n" . $this->footer . "\n", $this->footerOptions);
159
    }
160
161 2
    /**
162 2
     * Renders the toggle button.
163
     *
164
     * @throws JsonException
165
     *
166
     * @return string the rendering result
167
     */
168
    protected function renderToggleButton(): ?string
169
    {
170
        if ($this->toggleButtonEnabled === false) {
171
            return null;
172 3
        }
173
174 3
        $tag = ArrayHelper::remove($this->toggleButton, 'tag', 'button');
175 2
        $label = ArrayHelper::remove($this->toggleButton, 'label', 'Show');
176
177
        return Html::tag($tag, $label, $this->toggleButton);
178 1
    }
179 1
180
    /**
181 1
     * Renders the close button.
182
     *
183
     * @throws JsonException
184
     *
185
     * @return string the rendering result
186
     */
187
    protected function renderCloseButton(): ?string
188
    {
189
        if ($this->closeButtonEnabled === false) {
190
            return null;
191 3
        }
192
193 3
        $tag = ArrayHelper::remove($this->closeButton, 'tag', 'button');
194 1
        $label = ArrayHelper::remove($this->closeButton, 'label', Html::tag('span', '&times;', [
195
            'aria-hidden' => 'true',
196
        ]));
197 2
198 2
        return Html::tag($tag, $label, $this->closeButton);
199 2
    }
200
201
    /**
202 2
     * Initializes the widget options.
203
     *
204
     * This method sets the default values for various options.
205
     */
206
    protected function initOptions(): void
207
    {
208
        $this->options = array_merge([
209
            'class' => 'fade',
210 3
            'role' => 'dialog',
211
            'tabindex' => -1,
212 3
            'aria-hidden' => 'true',
213 3
        ], $this->options);
214
215
        Html::addCssClass($this->options, ['widget' => 'modal']);
216
217 3
        if ($this->getEnableClientOptions() !== false) {
218
            $this->clientOptions(array_merge(['show' => false], $this->getClientOptions()));
219 3
        }
220
221 3
        $this->titleOptions = array_merge([
222
            'id' => $this->options['id'] . '-label',
223
        ], $this->titleOptions);
224
225 3
        if (!isset($this->options['aria-label'], $this->options['aria-labelledby']) && $this->title !== null) {
226 3
            $this->options['aria-labelledby'] = $this->titleOptions['id'];
227 3
        }
228
229 3
        if ($this->closeButtonEnabled !== false) {
230 2
            $this->closeButton = array_merge([
231
                'data-dismiss' => 'modal',
232
                'class' => 'close',
233 3
                'type' => 'button',
234 2
            ], $this->closeButton);
235 2
        }
236
237
        if ($this->toggleButton !== []) {
238 2
            $this->toggleButton = array_merge([
239
                'data-toggle' => 'modal',
240
                'type' => 'button',
241 3
            ], $this->toggleButton);
242 1
            if (!isset($this->toggleButton['data-target']) && !isset($this->toggleButton['href'])) {
243 1
                $this->toggleButton['data-target'] = '#' . $this->options['id'];
244
            }
245 1
        }
246 1
    }
247 1
248
    /**
249
     * Body options.
250 3
     *
251
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
252
     *
253
     * @param array $value
254
     *
255
     * @return $this
256
     */
257
    public function bodyOptions(array $value): self
258
    {
259
        $this->bodyOptions = $value;
260
261 1
        return $this;
262
    }
263 1
264
    /**
265 1
     * The options for rendering the close button tag.
266
     *
267
     * The close button is displayed in the header of the modal window. Clicking on the button will hide the modal
268
     * window. If {@see closeButtonEnabled} is false, no close button will be rendered.
269
     *
270
     * The following special options are supported:
271
     *
272
     * - tag: string, the tag name of the button. Defaults to 'button'.
273
     * - label: string, the label of the button. Defaults to '&times;'.
274
     *
275
     * The rest of the options will be rendered as the HTML attributes of the button tag. Please refer to the
276
     * [Modal plugin help](http://getbootstrap.com/javascript/#modals) for the supported HTML attributes.
277
     *
278
     * @param array $value
279
     *
280
     * @return $this
281
     */
282
    public function closeButton(array $value): self
283
    {
284
        $this->closeButton = $value;
285
286
        return $this;
287
    }
288
289
    /**
290
     * Enable/Disable close button.
291
     *
292
     * @param bool $value
293
     *
294
     * @return $this
295
     */
296
    public function closeButtonEnabled(bool $value): self
297
    {
298
        $this->closeButtonEnabled = $value;
299
300 1
        return $this;
301
    }
302 1
303
    /**
304 1
     * The footer content in the modal window.
305
     *
306
     * @param string|null $value
307
     *
308
     * @return $this
309
     */
310
    public function footer(?string $value): self
311
    {
312
        $this->footer = $value;
313
314 2
        return $this;
315
    }
316 2
317
    /**
318 2
     * Additional footer options.
319
     *
320
     * @param array $value
321
     *
322
     * @return $this
323
     *
324
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
325
     */
326
    public function footerOptions(array $value): self
327
    {
328
        $this->footerOptions = $value;
329
330
        return $this;
331
    }
332
333
    /**
334
     * Additional header options.
335
     *
336
     * @param array $value
337
     *
338
     * @return $this
339
     *
340
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
341
     */
342
    public function headerOptions(array $value): self
343
    {
344
        $this->headerOptions = $value;
345
346
        return $this;
347
    }
348
349
    /**
350
     * @param array $value the HTML attributes for the widget container tag. The following special options are
351
     * recognized.
352
     *
353
     * @return $this
354
     *
355
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
356
     */
357
    public function options(array $value): self
358
    {
359
        $this->options = $value;
360
361
        return $this;
362
    }
363
364
    /**
365
     * The title content in the modal window.
366
     *
367
     * @param string|null $value
368
     *
369
     * @return $this
370
     */
371
    public function title(?string $value): self
372
    {
373
        $this->title = $value;
374
375 2
        return $this;
376
    }
377 2
378
    /**
379 2
     * Additional title options.
380
     *
381
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
382
     *
383
     * @param array $value
384
     *
385
     * @return $this
386
     */
387
    public function titleOptions(array $value): self
388
    {
389
        $this->titleOptions = $value;
390
391
        return $this;
392
    }
393
394
    /**
395
     * The options for rendering the toggle button tag.
396
     *
397
     * The toggle button is used to toggle the visibility of the modal window. If {@see toggleButtonEnabled} is false,
398
     * no toggle button will be rendered.
399
     *
400
     * The following special options are supported:
401
     *
402
     * - tag: string, the tag name of the button. Defaults to 'button'.
403
     * - label: string, the label of the button. Defaults to 'Show'.
404
     *
405
     * The rest of the options will be rendered as the HTML attributes of the button tag. Please refer to the
406
     * [Modal plugin help](http://getbootstrap.com/javascript/#modals) for the supported HTML attributes.
407
     *
408
     * @param array $value
409
     *
410
     * @return $this
411
     */
412
    public function toggleButton(array $value): self
413
    {
414
        $this->toggleButton = $value;
415
416 1
        return $this;
417
    }
418 1
419
    /**
420 1
     * Enable/Disable toggle button.
421
     *
422
     * @param bool $value
423
     *
424
     * @return $this
425
     */
426
    public function toggleButtonEnabled(bool $value): self
427
    {
428
        $this->toggleButtonEnabled = $value;
429
430 2
        return $this;
431
    }
432 2
433
    /**
434 2
     * The modal size. Can be {@see SIZE_LARGE} or {@see SIZE_SMALL}, or null for default.
435
     *
436
     * @param string|null $value
437
     *
438
     * @return $this
439
     */
440
    public function size(?string $value): self
441
    {
442
        $this->size = $value;
443
444
        return $this;
445
    }
446
}
447