Skip to content

Commit

Permalink
PLANET-6530 Add new Secondary Navigation Block
Browse files Browse the repository at this point in the history
- Added all necessary files for the new block
- Added tests for the new block
  • Loading branch information
Osong-Michael committed Jan 8, 2025
1 parent de496cc commit ca5f84a
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 2 deletions.
39 changes: 39 additions & 0 deletions assets/src/blocks/SecondaryNavigation/SecondaryNavigationBlock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {SecondaryNavigationEditor} from './SecondaryNavigationEditor';
import {example} from './example';

const {__} = wp.i18n;

const BLOCK_NAME = 'planet4-blocks/secondary-navigation';

export const registerSecondaryNavigationBlock = () => {
const {registerBlockType} = wp.blocks;

registerBlockType(BLOCK_NAME, {
title: 'Secondary Navigation Menu',
description: __('Inserts a secondary navigation menu to the page that leads to different sections of the same page.', 'planet4-blocks-backend'),
icon: 'welcome-widgets-menus',
category: 'planet4-blocks',
attributes: {
levels: {
type: 'array',
default: [{heading: 2, link: true}],
},
exampleMenuItems: { // Used for the block's preview, which can't extract items from anything.
type: 'array',
},
},
isExample: {
type: 'boolean',
default: false,
},
supports: {
multiple: false, // Use the block just once per post.
html: false,
},
edit: SecondaryNavigationEditor,
save() {
return null;
},
example,
});
};
69 changes: 69 additions & 0 deletions assets/src/blocks/SecondaryNavigation/SecondaryNavigationEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import {getHeadingsFromBlocks} from './generateHeadingsForBlock';

const {useSelect} = wp.data;
const {InspectorControls} = wp.blockEditor;
const {PanelBody} = wp.components;
const {__} = wp.i18n;

const renderEdit = () => {
return (
<InspectorControls>
<PanelBody title={__('Learn more about this block', 'planet4-blocks-backend')} initialOpen={false}>
<p className="components-base-control__help">
<a target="_blank" href="https://planet4.greenpeace.org/content/blocks/table-of-contents/" rel="noreferrer">
P4 Handbook P4 Secondary Navigation Menu
</a>
{' '} &#128203;
</p>
</PanelBody>
</InspectorControls>
);
};

const renderView = attributes => {
const {
levels,
isExample,
exampleMenuItems,
} = attributes;

const blocks = useSelect(select => select('core/block-editor').getBlocks(), []);

const flatHeadings = getHeadingsFromBlocks(blocks, levels);

const menuItems = isExample ? exampleMenuItems : flatHeadings;

return (
<section className="block secondary-navigation-block">
{menuItems.length > 0 ?
<div className="secondary-navigation-menu">
<ul className="secondary-navigation-item">
{menuItems.map(({anchor, text, shouldLink}) => (
<li key={anchor} className={`list-style-none ${shouldLink ? 'list-link' : 'list-heading'}`}>
{shouldLink ?
<a
className="icon-link secondary-navigation-link"
href={`#${anchor}`}
>
{text}
</a> :
<span className="secondary-navigation-heading">{text}</span>
}
</li>
))}
</ul>
</div> :
<div className="EmptyMessage">
{__('There are not any pre-established headings that this block can display in the form of a secondary navigation menu. Please add headings to your page.', 'planet4-blocks-backend')}
</div>
}
</section>
);
};

export const SecondaryNavigationEditor = ({attributes, isSelected}) => (
<>
{isSelected && renderEdit()}
{renderView(attributes)}
</>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {getHeadingsFromDom} from '../TableOfContents/getHeadingsFromDom';

export const SecondaryNavigationFrontend = ({levels}) => {
const headings = getHeadingsFromDom(levels);

return (
<section className="block secondary-navigation-block">
<div className="secondary-navigation-menu">
<ul className="secondary-navigation-item">
{headings.map(({anchor, text, shouldLink}) => (
<li key={anchor} className={`list-style-none ${shouldLink ? 'list-link' : 'list-heading'}`}>
{shouldLink ?
<a
className="icon-link secondary-navigation-link"
href={`#${anchor}`}
>
{text}
</a> :
<span className="secondary-navigation-heading">{text}</span>
}
</li>
))}
</ul>
</div>
</section>
);
};
27 changes: 27 additions & 0 deletions assets/src/blocks/SecondaryNavigation/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export const example = {
viewportWidth: 992,
attributes: {
isExample: true,
exampleMenuItems: [
{
text: 'Title 1',
anchor: 'Title 1',
level: 2,
shouldLink: true,
},
{
text: 'Title 2',
anchor: 'Title 2',
level: 2,
shouldLink: true,
},
{
text: 'Title 3',
anchor: 'Title 3',
level: 2,
shouldLink: true,
},
],
},
};

30 changes: 30 additions & 0 deletions assets/src/blocks/SecondaryNavigation/generateHeadingsForBlock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {generateAnchor} from '../TableOfContents/generateAnchor';
import {unescape} from '../../functions/unescape';

const stripTags = str => str.replace(/(<([^>]+)>)/ig, ''); //NOSONAR

export const getHeadingsFromBlocks = (blocks, selectedLevels) => {
const headings = [];
blocks.forEach(block => {
if (block.name === 'core/heading') {
const blockLevel = block.attributes.level;

const levelConfig = selectedLevels.find(selected => selected.heading === blockLevel);

if (!levelConfig) {
return;
}

const anchor = block.attributes.anchor || generateAnchor(block.attributes.content, headings.map(h => h.anchor));

headings.push({
level: blockLevel,
content: unescape(stripTags(block.attributes.content)),
anchor,
shouldLink: levelConfig.link,
});
}
});

return headings;
};
4 changes: 3 additions & 1 deletion assets/src/blocks/TableOfContents/getHeadingsFromDom.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ export const getHeadingsFromDom = selectedLevels => {
}

// Get all heading tags that we need to query
const headingsSelector = selectedLevels.map(level => `:not(.table-of-contents-block) > h${level.heading}`);
const headingsSelector = selectedLevels.map(
level => `:not(.table-of-contents-block):not(.secondary-navigation-block) > h${level.heading}`
);

const usedAnchors = [];

Expand Down
2 changes: 2 additions & 0 deletions assets/src/editorIndex.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {registerColumnsBlock} from './blocks/Columns/ColumnsBlock';
import {registerBlockStyles} from './block-styles';
import {registerBlockVariations} from './block-variations';
import {registerActionButtonTextBlock} from './blocks/ActionCustomButtonText';
import {registerSecondaryNavigationBlock} from './blocks/SecondaryNavigation/SecondaryNavigationBlock';

wp.domReady(() => {
// Blocks
Expand All @@ -21,6 +22,7 @@ wp.domReady(() => {
registerHappyPointBlock();
registerSocialMediaBlock();
registerTimelineBlock();
registerSecondaryNavigationBlock();

// Block Templates
registerBlockTemplates();
Expand Down
4 changes: 3 additions & 1 deletion assets/src/frontendIndex.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import {TableOfContentsFrontend} from './blocks/TableOfContents/TableOfContentsF
import {HappyPointFrontend} from './blocks/HappyPoint/HappyPointFrontend';
import {ColumnsFrontend} from './blocks/Columns/ColumnsFrontend';
import {setupLightboxForImages} from './blocks/components/Lightbox/setupLightboxForImages';
import {SecondaryNavigationFrontend} from './blocks/SecondaryNavigation/SecondaryNavigationFrontend';

// Render React components
const COMPONENTS = {
'planet4-blocks/submenu': TableOfContentsFrontend,
'planet4-blocks/happypoint': HappyPointFrontend,
'planet4-blocks/columns': ColumnsFrontend,
'planet4-blocks/secondary-navigation': SecondaryNavigationFrontend,
};

document.addEventListener('DOMContentLoaded', () => {
Expand All @@ -35,4 +37,4 @@ document.addEventListener('DOMContentLoaded', () => {
);

setupLightboxForImages();
});
});
73 changes: 73 additions & 0 deletions src/Blocks/SecondaryNavigation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

/**
* SecondaryNavigation block class.
*
* @package P4\MasterTheme
* @since 0.1
*/

namespace P4\MasterTheme\Blocks;

/**
* Class SecondaryNavigation
*
* @package P4\MasterTheme\Blocks
*/
class SecondaryNavigation extends BaseBlock
{
/**
* Block name.
*
* @const string BLOCK_NAME.
*/
public const BLOCK_NAME = 'secondary-navigation';

/**
* SecondaryNavigation constructor.
*/
public function __construct()
{

$this->register_secondary_navigation_block();
}

/**
* Register SecondaryNavigation block.
*/
public function register_secondary_navigation_block(): void
{
register_block_type(
self::get_full_block_name(),
[
'render_callback' => [ self::class, 'render_frontend' ],
'attributes' => [
'levels' => [
'type' => 'array',
'default' => [
[
'heading' => 2,
'link' => false,
],
],
],
],
]
);

add_action('enqueue_block_editor_assets', [ self::class, 'enqueue_editor_assets' ]);
add_action('wp_enqueue_scripts', [ self::class, 'enqueue_frontend_assets' ]);
}

/**
* Required by the `Base_Block` class.
*
* @param array $fields Unused, required by the abstract function.
* @phpcs:disable SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter
*/
public function prepare_data(array $fields): array
{
return [];
}
// @phpcs:enable SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter
}
1 change: 1 addition & 0 deletions src/Loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ public static function add_blocks(): void
new Blocks\TableOfContents();//NOSONAR
new Blocks\TakeActionBoxout();//NOSONAR
new Blocks\Timeline();//NOSONAR
new Blocks\SecondaryNavigation();//NOSONAR

register_block_pattern_category(
'page-headers',
Expand Down

0 comments on commit ca5f84a

Please sign in to comment.