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

Tools Panel: only show header ➕ icon when all items are optional #38262

Merged
merged 5 commits into from
Jan 27, 2022
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Enhancements

- Refine `ExternalLink` to be same size as the text, to appear more as a glyph than an icon. ([#37859](https://github.com/WordPress/gutenberg/pull/37859))
- Updated `ToolsPanel` header icon to only show "plus" icon when all items are optional and all are currently hidden ([#38262](https://github.com/WordPress/gutenberg/pull/38262))

### Bug Fix

Expand Down
21 changes: 21 additions & 0 deletions packages/components/src/tools-panel/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* External dependencies
*/
import styled from '@emotion/styled';
import { boolean } from '@storybook/addon-knobs';

/**
* WordPress dependencies
Expand All @@ -23,6 +24,9 @@ import { createSlotFill, Provider as SlotFillProvider } from '../../slot-fill';
export default {
title: 'Components (Experimental)/ToolsPanel',
component: ToolsPanel,
parameters: {
knobs: { disable: false },
},
};

export const _default = () => {
Expand Down Expand Up @@ -138,19 +142,36 @@ export const WithNonToolsPanelItems = () => {
export const WithOptionalItemsPlusIcon = () => {
const [ height, setHeight ] = useState();
const [ width, setWidth ] = useState();
const [ minWidth, setMinWidth ] = useState();

const resetAll = () => {
setHeight( undefined );
setWidth( undefined );
setMinWidth( undefined );
};

const includeDefaultControls = boolean( 'includeDefaultControls', false );

return (
<PanelWrapperView>
<Panel>
<ToolsPanel
label="Tools Panel (optional items only)"
resetAll={ resetAll }
key={ includeDefaultControls }
Copy link
Contributor

Choose a reason for hiding this comment

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

Is includeDefaultControls used as a key to force ToolsPanel to re-render?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. Unfortunately without it, the panel doesn't refresh properly. At first I thought it pointed to a bug in the source code, but the storybook and the Gutenberg app are behaving differently here. 🤷

>
<SingleColumnItem
hasValue={ () => !! minWidth }
label="Minimum width"
onDeselect={ () => setMinWidth( undefined ) }
isShownByDefault={ includeDefaultControls }
>
<UnitControl
label="Minimum width"
value={ minWidth }
onChange={ ( next ) => setMinWidth( next ) }
/>
</SingleColumnItem>
<SingleColumnItem
hasValue={ () => !! width }
label="Width"
Expand Down
32 changes: 31 additions & 1 deletion packages/components/src/tools-panel/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,17 @@ describe( 'ToolsPanel', () => {
} );

describe( 'panel header icon toggle', () => {
const defaultControls = {
attributes: { value: false },
hasValue: jest.fn().mockImplementation( () => {
return !! defaultControls.attributes.value;
} ),
label: 'Default',
onDeselect: jest.fn(),
onSelect: jest.fn(),
isShownByDefault: true,
};

const optionalControls = {
attributes: { value: false },
hasValue: jest.fn().mockImplementation( () => {
Expand All @@ -953,7 +964,26 @@ describe( 'ToolsPanel', () => {
isShownByDefault: false,
};

it( 'should render appropriate icons for the dropdown menu', async () => {
it( 'should render appropriate icon for the dropdown menu where there are default controls', async () => {
render(
<ToolsPanel { ...defaultProps }>
<ToolsPanelItem { ...defaultControls }>
<div>Default control</div>
</ToolsPanelItem>
<ToolsPanelItem { ...optionalControls }>
<div>Optional control</div>
</ToolsPanelItem>
</ToolsPanel>
);

const optionsDisplayedIcon = screen.getByRole( 'button', {
name: 'View options',
} );

expect( optionsDisplayedIcon ).toBeInTheDocument();
} );

it( 'should render appropriate icons for the dropdown menu where there are no default controls', async () => {
render(
<ToolsPanel { ...defaultProps }>
<ToolsPanelItem { ...optionalControls }>
Expand Down
22 changes: 13 additions & 9 deletions packages/components/src/tools-panel/tools-panel/hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ const generateMenuItems = ( {
return menuItems;
};

const isMenuItemTypeEmpty = (
obj: ToolsPanelMenuItems[ ToolsPanelMenuItemKey ]
ramonjd marked this conversation as resolved.
Show resolved Hide resolved
) => obj && Object.keys( obj ).length === 0;

export function useToolsPanel(
props: WordPressComponentProps< ToolsPanelProps, 'div' >
) {
Expand Down Expand Up @@ -167,24 +171,24 @@ export function useToolsPanel(
] = useState( false );

useEffect( () => {
if ( menuItems.optional ) {
const optionalItems = Object.entries( menuItems.optional );
const allControlsHidden =
optionalItems.length > 0 &&
! optionalItems.some( ( [ , isSelected ] ) => isSelected );
if (
isMenuItemTypeEmpty( menuItems?.default ) &&
ramonjd marked this conversation as resolved.
Show resolved Hide resolved
! isMenuItemTypeEmpty( menuItems?.optional )
) {
const allControlsHidden = ! Object.entries(
menuItems.optional
).some( ( [ , isSelected ] ) => isSelected );
setAreAllOptionalControlsHidden( allControlsHidden );
}
}, [ menuItems.optional, setAreAllOptionalControlsHidden ] );
}, [ menuItems, setAreAllOptionalControlsHidden ] );

const cx = useCx();
const classes = useMemo( () => {
const hasDefaultMenuItems =
menuItems?.default && !! Object.keys( menuItems?.default ).length;
const wrapperStyle =
hasInnerWrapper &&
styles.ToolsPanelWithInnerWrapper( DEFAULT_COLUMNS );
const emptyStyle =
! hasDefaultMenuItems &&
isMenuItemTypeEmpty( menuItems?.default ) &&
areAllOptionalControlsHidden &&
styles.ToolsPanelHiddenInnerWrapper;

Expand Down