Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 58
0.00% covered (danger)
0.00%
0 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
SingleMenuEntry
0.00% covered (danger)
0.00%
0 / 58
0.00% covered (danger)
0.00%
0 / 12
420
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
12
 overrideIcon
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 overrideText
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 create
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 getName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCSSClasses
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 getComponents
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
12
 trackClicks
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setIcon
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 setTitle
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setNodeID
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setJSOnly
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18namespace MediaWiki\Minerva\Menu\Entries;
19
20use MediaWiki\Message\Message;
21
22/**
23 * Model for a simple menu entries with label and icon
24 */
25class SingleMenuEntry implements IMenuEntry {
26    private string $name;
27    private array $attributes;
28    private bool $isJSOnly = false;
29
30    /**
31     * Create a simple menu element with one component
32     *
33     * @param string $name An unique menu element identifier
34     * @param string $text Text to show on menu element
35     * @param string $url URL menu element points to
36     * @param string|array $className Additional CSS class names.
37     * @param bool $isInterface If true, the menu element is provided with data-mw='interface'
38     * and is treated as a standard part of the interface (ie. MediaWiki Core might bind to
39     * the menu element)
40     */
41    public function __construct( string $name, string $text, string $url, $className = '', bool $isInterface = true ) {
42        $this->name = $name;
43        $menuClass = 'menu__item--' . $name;
44
45        $this->attributes = [
46            'data-icon' => [
47                'icon' => null,
48            ],
49            'data-event-name' => null,
50            'title' => null,
51            'id' => null,
52            'text' => $text,
53            'href' => $url,
54            'role' => '',
55            'class' => is_array( $className ) ?
56                implode( ' ', $className + [ $menuClass ] ) :
57                    ltrim( $className . ' ' . $menuClass ),
58        ];
59        if ( $isInterface ) {
60            // This is needed when Minerva uses a standard MediaWiki button (such as the
61            // watchstar) for a different purpose than MediaWiki usually uses it for. Not setting
62            // data-mw interface will prevent MediaWiki Core from binding to the element and
63            // potentially triggering its own actions. See T344925 for an example bug report.
64            $this->attributes['data-mw'] = 'interface';
65        }
66    }
67
68    /**
69     * Override the icon used in the home menu entry.
70     *
71     * @param string $icon
72     * @return $this
73     */
74    public function overrideIcon( string $icon ): self {
75        $this->setIcon( $icon );
76        return $this;
77    }
78
79    /**
80     * Override the text used in the home menu entry.
81     *
82     * @param string $text
83     * @return $this
84     */
85    public function overrideText( string $text ): self {
86        $this->attributes['text'] = $text;
87        return $this;
88    }
89
90    /**
91     * Create a Single Menu entry with text, icon and active click tracking
92     *
93     * @param string $name Entry identifier
94     * @param string $text Entry label
95     * @param string $url The URL entry points to
96     * @param string $className Optional HTML classes
97     * @param string|null $icon defaults to $name if not specified
98     * @param bool $trackable Whether an entry will track clicks or not. Default is false.
99     * @return static
100     */
101    public static function create( $name, $text, $url, $className = '', $icon = null, $trackable = false ) {
102        $entry = new static( $name, $text, $url, $className );
103        if ( $trackable ) {
104            $entry->trackClicks( $name );
105        }
106        if ( $icon === null ) {
107            $icon = $name;
108        }
109        $entry->setIcon( $icon );
110        return $entry;
111    }
112
113    /**
114     * @inheritDoc
115     */
116    public function getName(): string {
117        return $this->name;
118    }
119
120    /**
121     * @inheritDoc
122     */
123    public function getCSSClasses(): array {
124        return $this->isJSOnly ? [ 'skin-minerva-list-item-jsonly' ] : [];
125    }
126
127    /**
128     * @inheritDoc
129     */
130    public function getComponents(): array {
131        $attrs = [];
132        foreach ( [ 'id', 'href', 'data-event-name', 'data-mw', 'role' ] as $key ) {
133            $value = $this->attributes[$key] ?? null;
134            if ( $value ) {
135                $attrs[] = [
136                    'key' => $key,
137                    'value' => $value,
138                ];
139            }
140        }
141
142        $btn = [
143            'tag-name' => 'a',
144            'label' => $this->attributes['text'],
145            'array-attributes' => $attrs,
146            'classes' => $this->attributes['class'],
147            'data-icon' => $this->attributes['data-icon'],
148        ];
149
150        return [ $btn ];
151    }
152
153    /**
154     * @param string $eventName Should clicks be tracked. To override the tracking code
155     * pass the tracking code as string
156     * @return $this
157     */
158    public function trackClicks( $eventName ): self {
159        $this->attributes['data-event-name'] = 'menu.' . $eventName;
160        return $this;
161    }
162
163    /**
164     * Set the Menu entry icon
165     * @param string|null $iconName
166     * @return $this
167     */
168    public function setIcon( $iconName ): self {
169        if ( $iconName !== null ) {
170            $this->attributes['data-icon']['icon'] = $iconName;
171        } else {
172            $this->attributes['data-icon'] = null;
173        }
174        return $this;
175    }
176
177    /**
178     * Set the menu entry title
179     * @param Message $message Title message
180     * @return $this
181     */
182    public function setTitle( Message $message ): self {
183        $this->attributes['title'] = $message->escaped();
184        return $this;
185    }
186
187    /**
188     * Set the Menu entry ID html attribute
189     * @param string $nodeID
190     * @return $this
191     */
192    public function setNodeID( $nodeID ): self {
193        $this->attributes['id'] = $nodeID;
194        return $this;
195    }
196
197    /**
198     * Mark entry as JS only.
199     */
200    public function setJSOnly() {
201        $this->isJSOnly = true;
202    }
203}