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

Modal: add __experimentalHideHeader prop, wrap in forwardref #36831

Merged
merged 10 commits into from
Nov 25, 2021
5 changes: 5 additions & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,16 @@
- Fixed inconsistent padding in `UnitControl` ([#35646](https://github.com/WordPress/gutenberg/pull/35646)).
- Added support for RTL behavior for the `ZStack`'s `offset` prop ([#36769](https://github.com/WordPress/gutenberg/pull/36769))
- Fixed race conditions causing conditionally displayed `ToolsPanelItem` components to be erroneously deregistered ([36588](https://github.com/WordPress/gutenberg/pull/36588)).
- Added `__experimentalHideHeader` prop to `Modal` component ([#36831](https://github.com/WordPress/gutenberg/pull/36831)).
ciampo marked this conversation as resolved.
Show resolved Hide resolved

### Bug Fix

- Fixed spacing between `BaseControl` fields and help text within the `ToolsPanel` ([36334](https://github.com/WordPress/gutenberg/pull/36334))

### Enhancements

- Wrapped `Modal` in a `forwardRef` call ([#36831](https://github.com/WordPress/gutenberg/pull/36831)).

## 19.0.2 (2021-11-15)

- Remove erroneous use of `??=` syntax from `build-module`.
Expand Down
10 changes: 10 additions & 0 deletions packages/components/src/modal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,16 @@ This property when set to `true` will render a full screen modal.
- Required: No
- Default: `false`

#### __experimentalHideHeader

When set to `true`, the Modal's header (including the icon, title and close button) will not be rendered.

*Warning*: This property is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes.

- Type: `boolean`
- Required: No
- Default: `false`

## Related components

- To notify a user with a message of medium importance, use `Notice`.
2 changes: 2 additions & 0 deletions packages/components/src/modal/aria-helper.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//@ts-nocheck

/**
* External dependencies
*/
Expand Down
127 changes: 72 additions & 55 deletions packages/components/src/modal/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//@ts-nocheck

/**
* External dependencies
*/
Expand All @@ -6,7 +8,12 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { createPortal, useEffect, useRef } from '@wordpress/element';
import {
createPortal,
useEffect,
useRef,
forwardRef,
} from '@wordpress/element';
import {
useInstanceId,
useFocusReturn,
Expand All @@ -30,31 +37,34 @@ import StyleProvider from '../style-provider';
// Used to count the number of open modals.
let openModalCount = 0;

export default function Modal( {
bodyOpenClassName = 'modal-open',
role = 'dialog',
title = null,
focusOnMount = true,
shouldCloseOnEsc = true,
shouldCloseOnClickOutside = true,
isDismissable, // Deprecated
isDismissible = isDismissable || true,
/* accessibility */
aria = {
labelledby: null,
describedby: null,
},
onRequestClose,
icon,
closeButtonLabel,
children,
style,
overlayClassName,
className,
contentLabel,
onKeyDown,
isFullScreen = false,
} ) {
function Modal( props, forwardedRef ) {
const {
bodyOpenClassName = 'modal-open',
role = 'dialog',
title = null,
focusOnMount = true,
shouldCloseOnEsc = true,
shouldCloseOnClickOutside = true,
isDismissable, // Deprecated
isDismissible = isDismissable || true,
/* accessibility */
aria = {
labelledby: null,
describedby: null,
},
onRequestClose,
icon,
closeButtonLabel,
children,
style,
overlayClassName,
className,
contentLabel,
onKeyDown,
isFullScreen = false,
__experimentalHideHeader = false,
Copy link
Member

Choose a reason for hiding this comment

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

Oh, interesting! I'll keep this convention in mind for future additions.

} = props;

const ref = useRef();
const instanceId = useInstanceId( Modal );
const headingId = title
Expand Down Expand Up @@ -106,7 +116,7 @@ export default function Modal( {
return createPortal(
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
<div
ref={ ref }
ref={ useMergeRefs( [ ref, forwardedRef ] ) }
className={ classnames(
'components-modal__screen-overlay',
overlayClassName
Expand Down Expand Up @@ -139,38 +149,43 @@ export default function Modal( {
onKeyDown={ onKeyDown }
>
<div
className={ 'components-modal__content' }
className={ classnames( 'components-modal__content', {
'hide-header': __experimentalHideHeader,
} ) }
role="document"
>
<div className="components-modal__header">
<div className="components-modal__header-heading-container">
{ icon && (
<span
className="components-modal__icon-container"
aria-hidden
>
{ icon }
</span>
) }
{ title && (
<h1
id={ headingId }
className="components-modal__header-heading"
>
{ title }
</h1>
{ ! __experimentalHideHeader && (
<div className="components-modal__header">
<div className="components-modal__header-heading-container">
{ icon && (
<span
className="components-modal__icon-container"
aria-hidden
>
{ icon }
</span>
) }
{ title && (
<h1
id={ headingId }
className="components-modal__header-heading"
>
{ title }
</h1>
) }
</div>
{ isDismissible && (
<Button
onClick={ onRequestClose }
icon={ closeSmall }
label={
closeButtonLabel ||
__( 'Close dialog' )
}
/>
) }
</div>
{ isDismissible && (
<Button
onClick={ onRequestClose }
icon={ closeSmall }
label={
closeButtonLabel || __( 'Close dialog' )
}
/>
) }
</div>
) }
{ children }
</div>
</div>
Expand All @@ -179,3 +194,5 @@ export default function Modal( {
document.body
);
}

export default forwardRef( Modal );
5 changes: 5 additions & 0 deletions packages/components/src/modal/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ export const _default = () => {
'shouldCloseOnClickOutside',
true
);
const __experimentalHideHeader = boolean(
'__experimentalHideHeader',
false
);

const iconComponent = showIcon ? <Icon icon={ wordpress } /> : null;

Expand All @@ -65,6 +69,7 @@ export const _default = () => {
shouldCloseOnEsc,
shouldCloseOnClickOutside,
title,
__experimentalHideHeader,
};

return <ModalExample { ...modalProps } />;
Expand Down
9 changes: 9 additions & 0 deletions packages/components/src/modal/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,13 @@
display: block;
margin-bottom: $grid-unit-30;
}

&.hide-header {
margin-top: 0;
padding-top: $grid-unit-30;

&::before {
content: none;
}
}
}
11 changes: 11 additions & 0 deletions packages/components/src/modal/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,15 @@ describe( 'Modal', () => {
const titleId = within( dialog ).getByText( 'Test Title' ).id;
expect( dialog ).toHaveAttribute( 'aria-labelledby', titleId );
} );

it( 'hides the header when the `__experimentalHideHeader` prop is used', () => {
render(
<Modal title="Test Title" __experimentalHideHeader={ true }>
ciampo marked this conversation as resolved.
Show resolved Hide resolved
<p>Modal content</p>
</Modal>
);
const dialog = screen.getByRole( 'dialog' );
const title = within( dialog ).queryByText( 'Test Title' );
expect( title ).not.toBeInTheDocument();
} );
} );
2 changes: 2 additions & 0 deletions packages/components/src/style-provider/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//@ts-nocheck

/**
* External dependencies
*/
Expand Down
2 changes: 2 additions & 0 deletions packages/components/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"src/icon/**/*",
"src/menu-item/**/*",
"src/menu-group/**/*",
"src/modal/**/*",
"src/navigable-container/**/*",
"src/navigator/**/*",
"src/number-control/**/*",
Expand All @@ -57,6 +58,7 @@
"src/select-control/**/*",
"src/shortcut/**/*",
"src/slot-fill/**/*",
"src/style-provider/**/*",
"src/spacer/**/*",
"src/spinner/**/*",
"src/surface/**/*",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`KeyboardShortcutHelpModal should match snapshot when the modal is active 1`] = `
<Modal
<ForwardRef(Modal)
className="edit-post-keyboard-shortcut-help-modal"
closeLabel="Close"
onRequestClose={[Function]}
Expand Down Expand Up @@ -80,7 +80,7 @@ exports[`KeyboardShortcutHelpModal should match snapshot when the modal is activ
}
title="Text formatting"
/>
</Modal>
</ForwardRef(Modal)>
`;

exports[`KeyboardShortcutHelpModal should match snapshot when the modal is not active 1`] = `""`;
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`PreferencesModal should match snapshot when the modal is active large viewports 1`] = `
<Modal
<ForwardRef(Modal)
className="edit-post-preferences-modal"
closeLabel="Close"
onRequestClose={[Function]}
Expand Down Expand Up @@ -30,11 +30,11 @@ exports[`PreferencesModal should match snapshot when the modal is active large v
>
<Component />
</TabPanel>
</Modal>
</ForwardRef(Modal)>
`;

exports[`PreferencesModal should match snapshot when the modal is active small viewports 1`] = `
<Modal
<ForwardRef(Modal)
className="edit-post-preferences-modal"
closeLabel="Close"
onRequestClose={[Function]}
Expand Down Expand Up @@ -386,5 +386,5 @@ exports[`PreferencesModal should match snapshot when the modal is active small v
</Card>
</NavigatorScreen>
</NavigatorProvider>
</Modal>
</ForwardRef(Modal)>
`;