Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PLANET-6530 Add new Secondary Navigation Block #2494

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
});
};
66 changes: 66 additions & 0 deletions assets/src/blocks/SecondaryNavigation/SecondaryNavigationEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
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, content}) => (
<li key={anchor}>
<a
className="secondary-navigation-link"
href={`#${anchor}`}
>
{content}
</a>
</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,32 @@
import {getHeadingsFromDom} from '../TableOfContents/getHeadingsFromDom';

export const SecondaryNavigationFrontend = ({levels}) => {
const headings = getHeadingsFromDom(levels);
const setActive = event => {
const allLinks = document.querySelectorAll('.secondary-navigation-link');
allLinks.forEach(link => link.classList.remove('active'));
event.target.classList.add('active');
};

return (
<section className="block secondary-navigation-block">
<div className="secondary-navigation-menu">
<ul className="secondary-navigation-item">
{headings.map(({anchor, content}) => (
<li
key={anchor}
>
<a
className="secondary-navigation-link"
href={`#${anchor}`}
onClick={setActive}
>
{content}
</a>
</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: [
{
content: 'Title 1',
anchor: 'title-1',
level: 2,
shouldLink: true,
},
{
content: 'Title 2',
anchor: 'title-2',
level: 2,
shouldLink: true,
},
{
content: '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();
});
});
1 change: 1 addition & 0 deletions assets/src/scss/blocks.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Beta blocks
@import "blocks/ActionsList/ActionsListStyle";
@import "blocks/PostsList/PostsListStyle";
@import "blocks/SecondaryNavigation/SecondaryNavigationStyle";

// P4 Blocks
@import "blocks/Accordion/AccordionStyle";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.secondary-navigation-block {
margin-left: calc(-50vw - -50%);
width: 100vw;
height: 55px;
padding: 10px;
background: var(--color-background-navigation_bar);
border-bottom: 0 transparent;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
}

.secondary-navigation-item {
list-style: none;
display: flex;
justify-content: space-around;

li {
padding: 5px;

&:has(a.active) {
border-bottom: 4px solid var(--grey-900);

a {
color: var(--grey-900) !important;
}
}
}
}

.secondary-navigation-link {
color: var(--grey-600) !important;
font-weight: var(--font-weight-bold);
font-family: var(--font-family-primary);

&:hover {
text-decoration: none;
color: var(--grey-900) !important
}
}
2 changes: 2 additions & 0 deletions src/BlockSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class BlockSettings
self::P4_BLOCKS_PREFIX . '/take-action-boxout',
self::P4_BLOCKS_PREFIX . '/timeline',
self::P4_BLOCKS_PREFIX . '/guestbook',
self::P4_BLOCKS_PREFIX . '/secondary-navigation',
self::HUBSPOT_FORMS_BLOCK,
self::GRAVITY_FORMS_BLOCK,
];
Expand Down Expand Up @@ -92,6 +93,7 @@ class BlockSettings
self::P4_BLOCKS_PREFIX . '/take-action-boxout',
self::P4_BLOCKS_PREFIX . '/timeline',
self::P4_BLOCKS_PREFIX . '/guestbook',
self::P4_BLOCKS_PREFIX . '/secondary-navigation',
self::HUBSPOT_FORMS_BLOCK,
self::GRAVITY_FORMS_BLOCK,
];
Expand Down
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' => true,
],
],
],
],
]
);

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