Skip to content

Commit

Permalink
Modal: add headerActions prop to render buttons in the header (#53328)
Browse files Browse the repository at this point in the history
* Experiment to add a new prop `allowFullScreenToggle` that exposes a button in the header to toggle fullscreen

* CHANGELOG.md

* Try out passing a ReactNode as a prop

* Added a story

* Renaming `auxiliaryActions` to `headerActions`

* to!

* Simplified storybook variation.
  • Loading branch information
ramonjd authored Aug 9, 2023
1 parent 7177761 commit 0d36d5d
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- `Theme`: Expose via private APIs ([#53262](https://github.com/WordPress/gutenberg/pull/53262)).
- `ProgressBar`: Use the theme system accent for indicator color ([#53347](https://github.com/WordPress/gutenberg/pull/53347)).
- `ProgressBar`: Use gray 300 for track color ([#53349](https://github.com/WordPress/gutenberg/pull/53349)).
- `Modal`: add `headerActions` prop to render buttons in the header. ([#53328](https://github.com/WordPress/gutenberg/pull/53328)).

### Bug Fix

Expand Down
7 changes: 7 additions & 0 deletions packages/components/src/modal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,13 @@ If this property is true, it will focus the first tabbable element rendered in t
- Required: No
- Default: `true`

#### headerActions

An optional React node intended to contain additional actions or other elements related to the modal, for example, buttons. Content is rendered in the top right corner of the modal and to the left of the close button, if visible.

- Required: No
- Default: `null`

#### `isDismissible`: `boolean`

If this property is set to false, the modal will not display a close icon and cannot be dismissed.
Expand Down
2 changes: 2 additions & 0 deletions packages/components/src/modal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ function UnforwardedModal(
contentLabel,
onKeyDown,
isFullScreen = false,
headerActions = null,
__experimentalHideHeader = false,
} = props;

Expand Down Expand Up @@ -257,6 +258,7 @@ function UnforwardedModal(
</h1>
) }
</div>
{ headerActions }
{ isDismissible && (
<Button
onClick={ onRequestClose }
Expand Down
27 changes: 27 additions & 0 deletions packages/components/src/modal/stories/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { ComponentStory, ComponentMeta } from '@storybook/react';
* WordPress dependencies
*/
import { useState } from '@wordpress/element';
import { starEmpty, starFilled } from '@wordpress/icons';

/**
* Internal dependencies
Expand Down Expand Up @@ -35,6 +36,9 @@ const meta: ComponentMeta< typeof Modal > = {
onRequestClose: {
action: 'onRequestClose',
},
isDismissible: {
control: { type: 'boolean' },
},
},
parameters: {
controls: { expanded: true },
Expand Down Expand Up @@ -98,3 +102,26 @@ Default.parameters = {
},
},
};

const LikeButton = () => {
const [ isLiked, setIsLiked ] = useState( false );
return (
<Button
icon={ isLiked ? starFilled : starEmpty }
label="Like"
onClick={ () => setIsLiked( ! isLiked ) }
/>
);
};

export const WithHeaderActions: ComponentStory< typeof Modal > = Template.bind(
{}
);
WithHeaderActions.args = {
...Default.args,
headerActions: <LikeButton />,
isDismissible: false,
};
WithHeaderActions.parameters = {
...Default.parameters,
};
14 changes: 14 additions & 0 deletions packages/components/src/modal/test/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,18 @@ describe( 'Modal', () => {
await user.click( modalFrame.parentElement! );
expect( opener ).toHaveFocus();
} );

it( 'should render `headerActions` React nodes', async () => {
render(
<Modal
headerActions={ <button>A sweet button</button> }
onRequestClose={ noop }
>
<p>Modal content</p>
</Modal>
);
expect(
screen.getByText( 'A sweet button', { selector: 'button' } )
).toBeInTheDocument();
} );
} );
8 changes: 8 additions & 0 deletions packages/components/src/modal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ export type ModalProps = {
* @default true
*/
focusOnMount?: Parameters< typeof useFocusOnMount >[ 0 ];
/**
* Elements that are injected into the modal header to the left of the close button (if rendered).
* Hidden if `__experimentalHideHeader` is `true`.
*
* @default null
*/
headerActions?: ReactNode;

/**
* If this property is added, an icon will be added before the title.
*/
Expand Down

0 comments on commit 0d36d5d

Please sign in to comment.