refactor(core): ♻️ convert main menu to use Components

This commit is contained in:
alistair3149 2024-05-09 17:46:19 -04:00
parent b873871e50
commit 85b8fe34e4
No known key found for this signature in database
10 changed files with 278 additions and 58 deletions

View file

@ -0,0 +1,14 @@
<?php
namespace MediaWiki\Skins\Citizen\Components;
/**
* Component interface for managing Citizen-modified components
*
* @internal
*/
interface CitizenComponent {
/**
* @return array of Mustache compatible data
*/
public function getTemplateData(): array;
}

View file

@ -0,0 +1,41 @@
<?php
namespace MediaWiki\Skins\Citizen\Components;
/**
* CitizenComponentMainMenu component
*/
class CitizenComponentMainMenu implements CitizenComponent {
/** @var array */
private $sidebarData;
/**
* @param array $sidebarData
*/
public function __construct( array $sidebarData ) {
$this->sidebarData = $sidebarData;
}
/**
* @inheritDoc
*/
public function getTemplateData(): array {
$portletsRest = [];
foreach ( $this->sidebarData[ 'array-portlets-rest' ] as $data ) {
/**
* Remove toolbox from main menu as we moved it to article tools
* TODO: Move handling to SkinCitizen.php after we convert pagetools to component
*/
if ( $data['id'] === 'p-tb' ) {
continue;
}
$portletsRest[] = ( new CitizenComponentMenu( $data ) )->getTemplateData();
}
$firstPortlet = new CitizenComponentMenu( $this->sidebarData['data-portlets-first'] );
return [
'data-portlets-first' => $firstPortlet->getTemplateData(),
'array-portlets-rest' => $portletsRest
];
}
}

View file

@ -0,0 +1,49 @@
<?php
namespace MediaWiki\Skins\Citizen\Components;
use Countable;
/**
* CitizenComponentMenu component
*/
class CitizenComponentMenu implements CitizenComponent, Countable {
/** @var array */
private $data;
/**
* @param array $data
*/
public function __construct( array $data ) {
$this->data = $data;
}
/**
* Counts how many items the menu has.
*
* @return int
*/
public function count(): int {
$items = $this->data['array-list-items'] ?? null;
if ( $items ) {
return count( $items );
}
$htmlItems = $this->data['html-items'] ?? '';
return substr_count( $htmlItems, '<li' );
}
/**
* @inheritDoc
*/
public function getTemplateData(): array {
return $this->data + [
'class' => '',
'label' => '',
'html-tooltip' => '',
'label-class' => '',
'html-before-portal' => '',
'html-items' => '',
'html-after-portal' => '',
'array-list-items' => null,
];
}
}

View file

@ -32,31 +32,10 @@ use NumberFormatter;
* Drawer partial of Skin Citizen
* Generates the following partials:
* - Logo
* - Drawer
* + Special Pages Link
* + Upload Link
*/
final class Drawer extends Partial {
/**
* Decorate main menu template data
*
* @return array
*/
public function decorateMainMenuData( $mainMenuData ) {
for ( $i = 0; $i < count( $mainMenuData['array-portlets-rest'] ); $i++ ) {
if ( $mainMenuData['array-portlets-rest'][$i]['id'] === 'p-tb' ) {
// Remove toolbox since it is handled by page tools
unset( $mainMenuData['array-portlets-rest'][$i] );
break;
}
}
// Reset index after unsetting toolbox
$mainMenuData['array-portlets-rest'] = array_values( $mainMenuData['array-portlets-rest'] );
return $mainMenuData;
}
/**
* Get messages used for site stats in the drawer
*

View file

@ -23,6 +23,7 @@
namespace MediaWiki\Skins\Citizen;
use MediaWiki\Skins\Citizen\Components\CitizenComponentMainMenu;
use MediaWiki\Skins\Citizen\Partials\BodyContent;
use MediaWiki\Skins\Citizen\Partials\Drawer;
use MediaWiki\Skins\Citizen\Partials\Footer;
@ -116,13 +117,23 @@ class SkinCitizen extends SkinMustache {
'msg-citizen-footer-tagline' => $this->msg( "citizen-footer-tagline" )->inContentLanguage()->parse(),
// Decorate data provided by core
'data-search-box' => $header->decorateSearchBoxData( $parentData['data-search-box'] ),
'data-main-menu' => $drawer->decorateMainMenuData( $parentData['data-portlets-sidebar'] ),
'data-footer' => $footer->decorateFooterData( $parentData['data-footer'] ),
];
$data += $sidebar->getSidebarData( $parentData );
$data += $tools->getPageToolsData( $parentData );
$components = [
'data-main-menu' => new CitizenComponentMainMenu( $parentData['data-portlets-sidebar'] )
];
foreach ( $components as $key => $component ) {
// Array of components or null values.
if ( $component ) {
$parentData[$key] = $component->getTemplateData();
}
}
return array_merge( $parentData, $data );
}

View file

@ -21,10 +21,7 @@
</div>
</header>
{{#data-main-menu}}
<section class="citizen-drawer__menu">
{{#data-portlets-first}}{{>Menu}}{{/data-portlets-first}}
{{#array-portlets-rest}}{{>Menu}}{{/array-portlets-rest}}
</section>
{{>MainMenu}}
{{/data-main-menu}}
</aside>
</div>

View file

@ -0,0 +1,4 @@
<section id="citizen-main-menu" class="citizen-main-menu citizen-drawer__menu">
{{#data-portlets-first}}{{>Menu}}{{/data-portlets-first}}
{{#array-portlets-rest}}{{>Menu}}{{/array-portlets-rest}}
</section>

View file

@ -11,38 +11,7 @@ use MediaWiki\Skins\Citizen\SkinCitizen;
* @group Citizen
* @group Database
*/
class DrawerTest extends \MediaWikiIntegrationTestCase {
/**
* @covers \MediaWiki\Skins\Citizen\Partials\Drawer::decorateMainMenuData
* @return void
*/
public function testDecorateMainMenuDataEmpty() {
$partial = new Drawer( new SkinCitizen() );
$this->assertEmpty( $partial->decorateMainMenuData( [
'array-portlets-rest' => [],
] )['array-portlets-rest'] );
}
/**
* @covers \MediaWiki\Skins\Citizen\Partials\Drawer::decorateMainMenuData
* @return void
*/
public function testDecorateMainMenuRemovePageTools() {
$partial = new Drawer( new SkinCitizen() );
$mainMenuData = [
'array-portlets-rest' => [
[ 'id' => 'foo' ],
[ 'id' => 'p-tb' ],
],
];
$this->assertNotEmpty( $partial->decorateMainMenuData( $mainMenuData ) );
$this->assertArrayHasKey( 'array-portlets-rest', $partial->decorateMainMenuData( $mainMenuData ) );
$this->assertNotContains( [ 'id' => 'pt-tb' ], $partial->decorateMainMenuData( $mainMenuData )['array-portlets-rest'] );
}
class DrawerTest extends MediaWikiIntegrationTestCase {
/**
* @covers \MediaWiki\Skins\Citizen\Partials\Drawer::getSiteStatsData
* @return void

View file

@ -0,0 +1,58 @@
<?php
declare( strict_types=1 );
namespace MediaWiki\Skins\Citizen\Tests\Unit\Components;
use MediaWiki\Skins\Citizen\Components\CitizenComponent;
use MediaWiki\Skins\Citizen\Components\CitizenComponentMainMenu;
use MediaWikiUnitTestCase;
/**
* @group Citizen
* @group Components
* @coversDefaultClass \MediaWiki\Skins\Citizen\Components\CitizenComponentMainMenu
*/
class CitizenComponentMainMenuTest extends MediaWikiUnitTestCase {
/**
* This test checks if the CitizenComponentMainMenu class can be instantiated
* @covers ::__construct
*/
public function testConstruct() {
// Mock the sidebar data
$sidebarData = [];
// Create a new CitizenComponentMainMenu object
$mainMenu = new CitizenComponentMainMenu(
$sidebarData
);
// Assert that the object is an instance of CitizenComponent
$this->assertInstanceOf( CitizenComponent::class, $mainMenu );
}
/**
* @covers ::getTemplateData
*/
public function testGetTemplateData( array $sidebarData, array $languageData, bool $isPinned ) {
// Create a new CitizenComponentMainMenu object
$mainMenu = new CitizenComponentMainMenu(
$sidebarData
);
// Call the getTemplateData method
$templateData = $mainMenu->getTemplateData();
// Assert main menu id
$this->assertSame( 'citizen-main-menu', $templateData['id'] );
// Assert the structure and types of expected keys
$this->assertIsArray( $templateData['data-portlets-first'] );
$this->assertIsArray( $templateData['array-portlets-rest'] );
// Assert the structure and types of expected keys
$this->assertArrayHasKey( 'data-portlets-first', $templateData );
$this->assertArrayHasKey( 'array-portlets-rest', $templateData );
}
}

View file

@ -0,0 +1,98 @@
<?php
declare( strict_types=1 );
namespace MediaWiki\Skins\Citizen\Tests\Unit\Components;
use MediaWiki\Skins\Citizen\Components\CitizenComponent;
use MediaWiki\Skins\Citizen\Components\CitizenComponentMenu;
use MediaWikiUnitTestCase;
/**
* @group Citizen
* @group Components
* @coversDefaultClass \MediaWiki\Skins\Citizen\Components\CitizenComponentMenu
*/
class CitizenComponentMenuTest extends MediaWikiUnitTestCase {
/**
* @return array[]
*/
public function provideMenuData(): array {
return [
[
[
'class' => 'some-class',
'label' => 'Some label',
'html-tooltip' => 'Some tooltip',
'label-class' => 'some-label-class',
'html-before-portal' => 'Some before portal',
'html-items' => 'Some items',
'html-after-portal' => 'Some after portal',
'array-list-items' => [ 'some-item-one', 'some-item-2', 'some-item-3' ]
]
]
];
}
/**
* @return array[]
*/
public function provideCountData(): array {
return [
[
[
'array-list-items' => [ 'some-item-one', 'some-item-2', 'some-item-3' ]
],
3
],
[
[
'html-items' => '<li>Some item</li><li>Some item</li><li>Some item</li>'
],
3
]
];
}
/**
* This test checks if the CitizenComponentMenu class can be instantiated
* @covers ::__construct
*/
public function testConstruct() {
// Create a new CitizenComponentMenu object
$menu = new CitizenComponentMenu( [] );
// Check if the object is an instance of CitizenComponent
$this->assertInstanceOf( CitizenComponent::class, $menu );
}
/**
* This test checks if the count method returns the correct number of items
* @covers ::count
* @dataProvider provideCountData
*/
public function testCount( array $data, int $expected ) {
// Create a new CitizenComponentMenu object
$menu = new CitizenComponentMenu( $data );
// Check if the count method returns the correct number of items
$this->assertSame( $expected, $menu->count() );
}
/**
* This test checks if the getTemplateData method returns the correct data
* @covers ::getTemplateData
* @dataProvider provideMenuData
*/
public function testGetTemplateData( array $data ) {
// Create a new CitizenComponentMenu object
$menu = new CitizenComponentMenu( $data );
// Call the getTemplateData method
$actualData = $menu->getTemplateData();
// Check if the getTemplateData method returns the correct data
$this->assertSame( $data, $actualData );
}
}