Skip to content

Commit

Permalink
Site Editor: Add template tab to sidebar (#28633)
Browse files Browse the repository at this point in the history
  • Loading branch information
david-szabo97 authored Feb 11, 2021
1 parent 5401bf2 commit 5dcaad4
Show file tree
Hide file tree
Showing 9 changed files with 371 additions and 5 deletions.
102 changes: 102 additions & 0 deletions packages/e2e-tests/specs/experiments/settings-sidebar.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* WordPress dependencies
*/
import {
trashAllPosts,
activateTheme,
getAllBlocks,
selectBlockByClientId,
} from '@wordpress/e2e-test-utils';

/**
* Internal dependencies
*/
import { navigationPanel, siteEditor } from '../../experimental-features';

async function toggleSidebar() {
await page.click(
'.edit-site-header__actions button[aria-label="Settings"]'
);
}

async function getActiveTabLabel() {
return await page.$eval(
'.edit-site-sidebar__panel-tab.is-active',
( element ) => element.getAttribute( 'aria-label' )
);
}

async function getTemplateCard() {
return {
title: await page.$eval(
'.edit-site-template-card__title',
( element ) => element.innerText
),
description: await page.$eval(
'.edit-site-template-card__description',
( element ) => element.innerText
),
};
}

describe( 'Settings sidebar', () => {
beforeAll( async () => {
await activateTheme( 'tt1-blocks' );
await trashAllPosts( 'wp_template' );
await trashAllPosts( 'wp_template_part' );
} );
afterAll( async () => {
await trashAllPosts( 'wp_template' );
await trashAllPosts( 'wp_template_part' );
await activateTheme( 'twentytwentyone' );
} );
beforeEach( async () => {
await siteEditor.visit();
} );

describe( 'Template tab', () => {
it( 'should open template tab by default if no block is selected', async () => {
await toggleSidebar();

expect( await getActiveTabLabel() ).toEqual(
'Template (selected)'
);
} );

it( "should show the currently selected template's title and description", async () => {
await toggleSidebar();

const templateCardBeforeNavigation = await getTemplateCard();
await navigationPanel.open();
await navigationPanel.backToRoot();
await navigationPanel.navigate( 'Templates' );
await navigationPanel.clickItemByText( '404' );
await navigationPanel.close();
const templateCardAfterNavigation = await getTemplateCard();

expect( templateCardBeforeNavigation ).toMatchObject( {
title: 'Index',
description:
'The default template which is used when no other template can be found',
} );
expect( templateCardAfterNavigation ).toMatchObject( {
title: '404',
description: 'Used when the queried content cannot be found',
} );
} );
} );

describe( 'Block tab', () => {
it( 'should open block tab by default if a block is selected', async () => {
const allBlocks = await getAllBlocks();
await selectBlockByClientId( allBlocks[ 0 ].clientId );

await toggleSidebar();
// TODO: Remove when toolbar supports text fields
expect( console ).toHaveWarnedWith(
'Using custom components as toolbar controls is deprecated. Please use ToolbarItem or ToolbarButton components instead. See: https://developer.wordpress.org/block-editor/components/toolbar-button/#inside-blockcontrols'
);
expect( await getActiveTabLabel() ).toEqual( 'Block (selected)' );
} );
} );
} );
2 changes: 2 additions & 0 deletions packages/edit-site/src/components/sidebar/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const SIDEBAR_TEMPLATE = 'edit-site/template';
export const SIDEBAR_BLOCK = 'edit-site/block-inspector';
2 changes: 2 additions & 0 deletions packages/edit-site/src/components/sidebar/default-sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default function DefaultSidebar( {
children,
closeLabel,
header,
headerClassName,
} ) {
return (
<>
Expand All @@ -25,6 +26,7 @@ export default function DefaultSidebar( {
icon={ icon }
closeLabel={ closeLabel }
header={ header }
headerClassName={ headerClassName }
>
{ children }
</ComplementaryArea>
Expand Down
43 changes: 38 additions & 5 deletions packages/edit-site/src/components/sidebar/index.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,64 @@
/**
* WordPress dependencies
*/
import { createSlotFill } from '@wordpress/components';
import { createSlotFill, PanelBody } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { cog, typography } from '@wordpress/icons';
import { useSelect } from '@wordpress/data';
import { store as interfaceStore } from '@wordpress/interface';
import { store as blockEditorStore } from '@wordpress/block-editor';

/**
* Internal dependencies
*/
import DefaultSidebar from './default-sidebar';
import GlobalStylesSidebar from './global-styles-sidebar';
import { STORE_NAME } from '../../store/constants';
import SettingsHeader from './settings-header';
import TemplateCard from './template-card';
import { SIDEBAR_BLOCK, SIDEBAR_TEMPLATE } from './constants';

const { Slot: InspectorSlot, Fill: InspectorFill } = createSlotFill(
'EditSiteSidebarInspector'
);
export const SidebarInspectorFill = InspectorFill;

export function SidebarComplementaryAreaFills() {
const { sidebarName } = useSelect( ( select ) => {
let sidebar = select( interfaceStore ).getActiveComplementaryArea(
STORE_NAME
);

if ( ! [ SIDEBAR_BLOCK, SIDEBAR_TEMPLATE ].includes( sidebar ) ) {
sidebar = SIDEBAR_TEMPLATE;
if ( select( blockEditorStore ).getBlockSelectionStart() ) {
sidebar = SIDEBAR_BLOCK;
}
}

return {
sidebarName: sidebar,
};
} );

return (
<>
<DefaultSidebar
identifier="edit-site/block-inspector"
title={ __( 'Block Inspector' ) }
identifier={ sidebarName }
title={ __( 'Settings' ) }
icon={ cog }
closeLabel={ __( 'Close block inspector sidebar' ) }
closeLabel={ __( 'Close settings sidebar' ) }
header={ <SettingsHeader sidebarName={ sidebarName } /> }
headerClassName="edit-site-sidebar__panel-tabs"
>
<InspectorSlot bubblesVirtually />
{ sidebarName === SIDEBAR_TEMPLATE && (
<PanelBody>
<TemplateCard />
</PanelBody>
) }
{ sidebarName === SIDEBAR_BLOCK && (
<InspectorSlot bubblesVirtually />
) }
</DefaultSidebar>
<GlobalStylesSidebar
identifier="edit-site/global-styles"
Expand Down
71 changes: 71 additions & 0 deletions packages/edit-site/src/components/sidebar/settings-header/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* WordPress dependencies
*/
import { Button } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useDispatch } from '@wordpress/data';
import { store as interfaceStore } from '@wordpress/interface';

/**
* Internal dependencies
*/
import { STORE_NAME } from '../../../store/constants';
import { SIDEBAR_BLOCK, SIDEBAR_TEMPLATE } from '../constants';

const SettingsHeader = ( { sidebarName } ) => {
const { enableComplementaryArea } = useDispatch( interfaceStore );
const openTemplateSettings = () =>
enableComplementaryArea( STORE_NAME, SIDEBAR_TEMPLATE );
const openBlockSettings = () =>
enableComplementaryArea( STORE_NAME, SIDEBAR_BLOCK );

const [ templateAriaLabel, templateActiveClass ] =
sidebarName === SIDEBAR_TEMPLATE
? // translators: ARIA label for the Template sidebar tab, selected.
[ __( 'Template (selected)' ), 'is-active' ]
: // translators: ARIA label for the Template Settings Sidebar tab, not selected.
[ __( 'Template' ), '' ];

const [ blockAriaLabel, blockActiveClass ] =
sidebarName === SIDEBAR_BLOCK
? // translators: ARIA label for the Block Settings Sidebar tab, selected.
[ __( 'Block (selected)' ), 'is-active' ]
: // translators: ARIA label for the Block Settings Sidebar tab, not selected.
[ __( 'Block' ), '' ];

/* Use a list so screen readers will announce how many tabs there are. */
return (
<ul>
<li>
<Button
onClick={ openTemplateSettings }
className={ `edit-site-sidebar__panel-tab ${ templateActiveClass }` }
aria-label={ templateAriaLabel }
// translators: Data label for the Template Settings Sidebar tab.
data-label={ __( 'Template' ) }
>
{
// translators: Text label for the Template Settings Sidebar tab.
__( 'Template' )
}
</Button>
</li>
<li>
<Button
onClick={ openBlockSettings }
className={ `edit-site-sidebar__panel-tab ${ blockActiveClass }` }
aria-label={ blockAriaLabel }
// translators: Data label for the Block Settings Sidebar tab.
data-label={ __( 'Block' ) }
>
{
// translators: Text label for the Block Settings Sidebar tab.
__( 'Block' )
}
</Button>
</li>
</ul>
);
};

export default SettingsHeader;
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
.components-panel__header.edit-site-sidebar__panel-tabs {
justify-content: flex-start;
padding-left: 0;
padding-right: $grid-unit-20;
border-top: 0;
margin-top: 0;

ul {
display: flex;
}
li {
margin: 0;
}

.components-button.has-icon {
display: none;
margin: 0 0 0 auto;
padding: 0;
min-width: $icon-size;
height: $icon-size;

@include break-medium() {
display: flex;
}
}
}

.components-button.edit-site-sidebar__panel-tab {
border-radius: 0;
height: $grid-unit-60;
background: transparent;
border: none;
box-shadow: none;
cursor: pointer;
display: inline-block;
padding: 3px 15px; // Use padding to offset the is-active border, this benefits Windows High Contrast mode
margin-left: 0;
font-weight: 500;

// This pseudo-element "duplicates" the tab label and sets the text to bold.
// This ensures that the tab doesn't change width when selected.
// See: https://github.com/WordPress/gutenberg/pull/9793
&::after {
content: attr(data-label);
display: block;
font-weight: 600;
height: 0;
overflow: hidden;
speak: none;
visibility: hidden;
}

&.is-active {
// The transparent shadow ensures no jumpiness when focus animates on an active tab.
box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) transparent, inset 0 0 -$border-width-tab 0 0 var(--wp-admin-theme-color);
position: relative;
z-index: z-index(".edit-post-sidebar__panel-tab.is-active");

// This border appears in Windows High Contrast mode instead of the box-shadow.
&::before {
content: "";
position: absolute;
top: 0;
bottom: 1px;
right: 0;
left: 0;
border-bottom: $border-width-tab solid transparent;
}
}

&:focus {
box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
position: relative;
z-index: z-index(".edit-post-sidebar__panel-tab.is-active");
}

&.is-active:focus {
box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color), inset 0 0 -$border-width-tab 0 0 var(--wp-admin-theme-color);
}
}
46 changes: 46 additions & 0 deletions packages/edit-site/src/components/sidebar/template-card/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { Icon } from '@wordpress/components';
import { layout } from '@wordpress/icons';
import { store as editorStore } from '@wordpress/editor';
import { store as coreStore } from '@wordpress/core-data';

/**
* Internal dependencies
*/
import { store as editSiteStore } from '../../../store';

export default function TemplateCard() {
const { title, description } = useSelect( ( select ) => {
const { getEditedPostType, getEditedPostId } = select( editSiteStore );
const { getEntityRecord } = select( coreStore );
const { __experimentalGetTemplateInfo: getTemplateInfo } = select(
editorStore
);

const postType = getEditedPostType();
const postId = getEditedPostId();
const record = getEntityRecord( 'postType', postType, postId );
const info = record ? getTemplateInfo( record ) : {};

return info;
}, [] );

if ( ! title && ! description ) {
return null;
}

return (
<div className="edit-site-template-card">
<Icon className="edit-site-template-card__icon" icon={ layout } />
<div className="edit-site-template-card__content">
<h2 className="edit-site-template-card__title">{ title }</h2>
<span className="edit-site-template-card__description">
{ description }
</span>
</div>
</div>
);
}
Loading

0 comments on commit 5dcaad4

Please sign in to comment.