Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 71
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
VectorComponentStickyHeader
0.00% covered (danger)
0.00%
0 / 71
0.00% covered (danger)
0.00%
0 / 6
110
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 msg
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getIconButtons
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
20
 getAddSectionButton
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 getSearchButton
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
2
 getTemplateData
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2namespace MediaWiki\Skins\Vector\Components;
3
4use MediaWiki\Message\Message;
5use MessageLocalizer;
6
7/**
8 * VectorComponentStickyHeader component
9 */
10class VectorComponentStickyHeader implements VectorComponent {
11    private const TALK_ICON = [
12        'icon' => 'speechBubbles',
13        'id' => 'ca-talk-sticky-header',
14        'event' => 'talk-sticky-header'
15    ];
16    private const SUBJECT_ICON = [
17        'icon' => 'article',
18        'id' => 'ca-subject-sticky-header',
19        'event' => 'subject-sticky-header'
20    ];
21    private const HISTORY_ICON = [
22        'icon' => 'wikimedia-history',
23        'id' => 'ca-history-sticky-header',
24        'event' => 'history-sticky-header',
25    ];
26    // Event and icon will be updated depending on watchstar state
27    private const WATCHSTAR_ICON = [
28        'id' => 'ca-watchstar-sticky-header',
29        'event' => 'watch-sticky-header',
30        'icon' => 'wikimedia-star',
31        'is-quiet' => true,
32        'tabindex' => '-1',
33        // With the original watchstar, this class is applied to the <li> element
34        // thats the parent of the actual watchlink. In the sticky header we dont use
35        // the same markup, so its directly applied to the watchlink element
36        'class' => 'mw-watchlink'
37    ];
38    private const EDIT_VE_ICON = [
39        'id' => 'ca-ve-edit-sticky-header',
40        'event' => 've-edit-sticky-header',
41        'icon' => 'wikimedia-edit',
42    ];
43    private const EDIT_WIKITEXT_ICON = [
44        'id' => 'ca-edit-sticky-header',
45        'event' => 'wikitext-edit-sticky-header',
46        'icon' => 'wikimedia-wikiText',
47    ];
48    private const EDIT_PROTECTED_ICON = [
49        'href' => '#',
50        'id' => 'ca-viewsource-sticky-header',
51        'event' => 've-edit-protected-sticky-header',
52        'icon' => 'wikimedia-editLock',
53    ];
54
55    /** @var MessageLocalizer */
56    private $localizer;
57    /** @var VectorComponent */
58    private $search;
59    /** @var VectorComponent|null */
60    private $langButton;
61
62    /** @var bool */
63    private $visualEditorTabPositionFirst;
64
65    /**
66     * @param MessageLocalizer $localizer
67     * @param VectorComponent $searchBox
68     * @param VectorComponent|null $langButton
69     * @param bool $visualEditorTabPositionFirst
70     */
71    public function __construct(
72        MessageLocalizer $localizer,
73        VectorComponent $searchBox,
74        $langButton = null,
75        bool $visualEditorTabPositionFirst = false
76    ) {
77        $this->search = $searchBox;
78        $this->langButton = $langButton;
79        $this->localizer = $localizer;
80        $this->visualEditorTabPositionFirst = $visualEditorTabPositionFirst;
81    }
82
83    /**
84     * @param mixed $key
85     * @return Message
86     */
87    private function msg( $key ): Message {
88        return $this->localizer->msg( $key );
89    }
90
91    /**
92     * Creates array of Button components in the sticky header
93     *
94     * @return array
95     */
96    private function getIconButtons() {
97        $icons = [
98            self::TALK_ICON,
99            self::SUBJECT_ICON,
100            self::HISTORY_ICON,
101            self::WATCHSTAR_ICON,
102        ];
103        $icons[] = $this->visualEditorTabPositionFirst ? self::EDIT_VE_ICON : self::EDIT_WIKITEXT_ICON;
104        $icons[] = $this->visualEditorTabPositionFirst ? self::EDIT_WIKITEXT_ICON : self::EDIT_VE_ICON;
105        $icons[] = self::EDIT_PROTECTED_ICON;
106        $iconButtons = [];
107        foreach ( $icons as $icon ) {
108            $iconButtons[] = new VectorComponentButton(
109                // Button labels will be populated in stickyHeader.js
110                "",
111                $icon[ 'icon' ],
112                $icon[ 'id' ],
113                $icon[ 'class' ] ?? '',
114                [
115                    'tabindex' => '-1',
116                    'data-event-name' => $icon[ 'event' ],
117                ],
118                'quiet',
119                'default',
120                true,
121                '#'
122            );
123        }
124        return $iconButtons;
125    }
126
127    /**
128     * Creates button data for the "Add section" button in the sticky header
129     *
130     * @return VectorComponentButton
131     */
132    private function getAddSectionButton() {
133        return new VectorComponentButton(
134            $this->msg( [ 'vector-2022-action-addsection', 'skin-action-addsection' ] )->text(),
135            'speechBubbleAdd-progressive',
136            'ca-addsection-sticky-header',
137            '',
138            [
139                'tabindex' => '-1',
140                'data-event-name' => 'addsection-sticky-header'
141            ],
142            'quiet',
143            'progressive',
144            false,
145            '#'
146        );
147    }
148
149    /**
150     * Creates button data for the "search" button in the sticky header
151     *
152     * @param array $searchBoxData
153     * @return VectorComponentButton
154     */
155    private function getSearchButton( $searchBoxData ) {
156        return new VectorComponentButton(
157            $this->msg( 'search' ),
158            'search',
159            null,
160            'vector-sticky-header-search-toggle',
161            [
162                'tabindex' => '-1',
163                'data-event-name' => 'ui.' . $searchBoxData['form-id'] . '.icon'
164            ],
165            'quiet',
166            'default',
167            true
168        );
169    }
170
171    /**
172     * @inheritDoc
173     */
174    public function getTemplateData(): array {
175        $iconButtonData = array_map( static function ( $btn ) {
176            return $btn->getTemplateData();
177        }, $this->getIconButtons() );
178        $buttonData = $this->langButton ? [ $this->langButton->getTemplateData() ] : [];
179        $buttonData[] = $this->getAddSectionButton()->getTemplateData();
180        $searchBoxData = $this->search->getTemplateData();
181        $searchButtonData = $this->getSearchButton( $searchBoxData )->getTemplateData();
182        return [
183            'array-icon-buttons' => $iconButtonData,
184            'array-buttons' => $buttonData,
185            'data-button-start' => $searchButtonData,
186            'data-search' => $searchBoxData,
187        ];
188    }
189}