Skip to content

Commit

Permalink
Implement Tabs in editor settings (#55360)
Browse files Browse the repository at this point in the history
* implement Tabs in editor settings sidebar

* remove duplicated styles from editor sidebar

* incorporate initial feedback

* pass props to Tabs directly

* add `closeGeneralSidebar` to `onSelect` callback

* set TabPanels to `focuasable={false}`

* detect when sidebar is closed. pass `selectedTabId` of `null`

* improve `Tabs` `onSelect` callback

* remove `aria-label` and `data-label` props

* add note explaining null selected tab when sidebar is closed

* update e2e test

* style updates

* update internal component structure to avoid rerenders

* remove fragment

* prevent infinite loop when opening third party sidebar

* update e2e tests for `Tabs` compatibility

* fix double top margin on tabpanels

* update to use new tabId prop

* remove import that is no longer needed after rebase

* fix keyboard navigable blocks test

* fix visibility and tab order tests

* fix footnotes tests

* fix change detection test
  • Loading branch information
chad1008 authored Dec 12, 2023
1 parent 482ac0c commit c90bb03
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 243 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,11 @@ describe( 'Change detection', () => {
it( 'consecutive edits to the same attribute should mark the post as dirty after a save', async () => {
// Open the sidebar block settings.
await openDocumentSettingsSidebar();
await page.click( '.edit-post-sidebar__panel-tab[data-label="Block"]' );

const blockInspectorTab = await page.waitForXPath(
'//button[@role="tab"][contains(text(), "Block")]'
);
await blockInspectorTab.click();

// Insert a paragraph.
await clickBlockAppender();
Expand Down
15 changes: 9 additions & 6 deletions packages/e2e-tests/specs/editor/various/editor-modes.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,21 +102,24 @@ describe( 'Editing modes (visual/HTML)', () => {
expect( title ).toBe( 'Paragraph' );

// The Block inspector should be active.
let blockInspectorTab = await page.$(
'.edit-post-sidebar__panel-tab.is-active[data-label="Block"]'
let [ blockInspectorTab ] = await page.$x(
'//button[@role="tab"][@aria-selected="true"][contains(text(), "Block")]'
);
expect( blockInspectorTab ).not.toBeNull();

await switchEditorModeTo( 'Code' );

// The Block inspector should not be active anymore.
blockInspectorTab = await page.$(
'.edit-post-sidebar__panel-tab.is-active[data-label="Block"]'
[ blockInspectorTab ] = await page.$x(
'//button[@role="tab"][@aria-selected="true"][contains(text(), "Block")]'
);
expect( blockInspectorTab ).toBeNull();
expect( blockInspectorTab ).toBeUndefined();

// No block is selected.
await page.click( '.edit-post-sidebar__panel-tab[data-label="Block"]' );
const inactiveBlockInspectorTab = await page.waitForXPath(
'//button[@role="tab"][contains(text(), "Block")]'
);
inactiveBlockInspectorTab.click();
const noBlocksElement = await page.$(
'.block-editor-block-inspector__no-blocks'
);
Expand Down
10 changes: 7 additions & 3 deletions packages/e2e-tests/specs/editor/various/preferences.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe( 'preferences', () => {
async function getActiveSidebarTabText() {
try {
return await page.$eval(
'.edit-post-sidebar__panel-tab.is-active',
'div[aria-label="Editor settings"] [role="tab"][aria-selected="true"]',
( node ) => node.textContent
);
} catch ( error ) {
Expand All @@ -29,11 +29,15 @@ describe( 'preferences', () => {
}

it( 'remembers sidebar dismissal between sessions', async () => {
const blockTab = await page.waitForXPath(
`//button[@role="tab"][contains(text(), 'Block')]`
);

// Open by default.
expect( await getActiveSidebarTabText() ).toBe( 'Post' );

// Change to "Block" tab.
await page.click( '.edit-post-sidebar__panel-tab[aria-label="Block"]' );
await blockTab.click();
expect( await getActiveSidebarTabText() ).toBe( 'Block' );

// Regression test: Reload resets to document tab.
Expand All @@ -46,7 +50,7 @@ describe( 'preferences', () => {

// Dismiss.
await page.click(
'.edit-post-sidebar__panel-tabs [aria-label="Close Settings"]'
'div[aria-label="Editor settings"] div[role="tablist"] + button[aria-label="Close Settings"]'
);
expect( await getActiveSidebarTabText() ).toBe( null );

Expand Down
29 changes: 16 additions & 13 deletions packages/e2e-tests/specs/editor/various/sidebar.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import {
} from '@wordpress/e2e-test-utils';

const SIDEBAR_SELECTOR = '.edit-post-sidebar';
const ACTIVE_SIDEBAR_TAB_SELECTOR = '.edit-post-sidebar__panel-tab.is-active';
const ACTIVE_SIDEBAR_TAB_SELECTOR =
'div[aria-label="Editor settings"] [role="tab"][aria-selected="true"]';
const ACTIVE_SIDEBAR_BUTTON_TEXT = 'Post';

describe( 'Sidebar', () => {
Expand Down Expand Up @@ -99,22 +100,24 @@ describe( 'Sidebar', () => {

// Tab lands at first (presumed selected) option "Post".
await page.keyboard.press( 'Tab' );
const isActiveDocumentTab = await page.evaluate(
() =>
document.activeElement.textContent === 'Post' &&
document.activeElement.classList.contains( 'is-active' )

// The Post tab should be focused and selected.
const [ documentInspectorTab ] = await page.$x(
'//button[@role="tab"][@aria-selected="true"][contains(text(), "Post")]'
);
expect( isActiveDocumentTab ).toBe( true );
expect( documentInspectorTab ).toBeDefined();
expect( documentInspectorTab ).toHaveFocus();

// Tab into and activate "Block".
await page.keyboard.press( 'Tab' );
// Arrow key into and activate "Block".
await page.keyboard.press( 'ArrowRight' );
await page.keyboard.press( 'Space' );
const isActiveBlockTab = await page.evaluate(
() =>
document.activeElement.textContent === 'Block' &&
document.activeElement.classList.contains( 'is-active' )

// The Block tab should be focused and selected.
const [ blockInspectorTab ] = await page.$x(
'//button[@role="tab"][@aria-selected="true"][contains(text(), "Block")]'
);
expect( isActiveBlockTab ).toBe( true );
expect( blockInspectorTab ).toBeDefined();
expect( blockInspectorTab ).toHaveFocus();
} );

it( 'should be possible to programmatically remove Document Settings panels', async () => {
Expand Down
84 changes: 16 additions & 68 deletions packages/edit-post/src/components/sidebar/settings-header/index.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
/**
* WordPress dependencies
*/
import { Button } from '@wordpress/components';
import { __, _x, sprintf } from '@wordpress/i18n';
import { useDispatch, useSelect } from '@wordpress/data';
import { privateApis as componentsPrivateApis } from '@wordpress/components';
import { __, _x } from '@wordpress/i18n';
import { useSelect } from '@wordpress/data';
import { store as editorStore } from '@wordpress/editor';

/**
* Internal dependencies
*/
import { store as editPostStore } from '../../../store';
import { unlock } from '../../../lock-unlock';
import { sidebars } from '../settings-sidebar';

const SettingsHeader = ( { sidebarName } ) => {
const { openGeneralSidebar } = useDispatch( editPostStore );
const openDocumentSettings = () =>
openGeneralSidebar( 'edit-post/document' );
const openBlockSettings = () => openGeneralSidebar( 'edit-post/block' );
const { Tabs } = unlock( componentsPrivateApis );

const SettingsHeader = () => {
const { documentLabel, isTemplateMode } = useSelect( ( select ) => {
const { getPostTypeLabel, getRenderingMode } = select( editorStore );

Expand All @@ -27,66 +25,16 @@ const SettingsHeader = ( { sidebarName } ) => {
};
}, [] );

const [ documentAriaLabel, documentActiveClass ] =
sidebarName === 'edit-post/document'
? // translators: ARIA label for the Document sidebar tab, selected. %s: Document label.
[ sprintf( __( '%s (selected)' ), documentLabel ), 'is-active' ]
: [ documentLabel, '' ];

const [ blockAriaLabel, blockActiveClass ] =
sidebarName === 'edit-post/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' ), '' ];

const [ templateAriaLabel, templateActiveClass ] =
sidebarName === 'edit-post/document'
? [ __( 'Template (selected)' ), 'is-active' ]
: [ __( 'Template' ), '' ];

/* Use a list so screen readers will announce how many tabs there are. */
return (
<ul>
{ ! isTemplateMode && (
<li>
<Button
onClick={ openDocumentSettings }
className={ `edit-post-sidebar__panel-tab ${ documentActiveClass }` }
aria-label={ documentAriaLabel }
data-label={ documentLabel }
>
{ documentLabel }
</Button>
</li>
) }
{ isTemplateMode && (
<li>
<Button
onClick={ openDocumentSettings }
className={ `edit-post-sidebar__panel-tab ${ templateActiveClass }` }
aria-label={ templateAriaLabel }
data-label={ __( 'Template' ) }
>
{ __( 'Template' ) }
</Button>
</li>
) }
<li>
<Button
onClick={ openBlockSettings }
className={ `edit-post-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>
<Tabs.TabList>
<Tabs.Tab tabId={ sidebars.document }>
{ isTemplateMode ? __( 'Template' ) : documentLabel }
</Tabs.Tab>
<Tabs.Tab tabId={ sidebars.block }>
{ /* translators: Text label for the Block Settings Sidebar tab. */ }
{ __( 'Block' ) }
</Tabs.Tab>
</Tabs.TabList>
);
};

Expand Down

This file was deleted.

Loading

1 comment on commit c90bb03

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests detected in c90bb03.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/7186299998
📝 Reported issues:

Please sign in to comment.