Test Failed
Pull Request — master (#45)
by Wilmer
03:13 queued 22s
created

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