Add popover support to select control dropdown (#34967)
* Add dropdownPlacement option to SelectControl * Make use of parent element of menu for width * Add changelog * Add useEffect for boundingRect so it gets updated accordingly * Fix styling for popover dropdown * Move popup markup down to render return * Move getMenuProps up * Move getMenuProps down to list again and always render the popover menu * Add MenuSlot that adds popover slot to mody with aria-live=off so it is skipped by Modal * Add comment in relation to the eslint-disable comment * Fix flashing of popover and addressed some minor PR suggestions
This commit is contained in:
parent
0769625b72
commit
1f7deb139f
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: update
|
||||
|
||||
Add support in SelectControl for using the popover slot for the popover.
|
|
@ -5,13 +5,25 @@
|
|||
left: 0;
|
||||
margin-top: $gap-smaller;
|
||||
box-sizing: border-box;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.components-popover.woocommerce-experimental-select-control__popover-menu {
|
||||
background: $studio-white;
|
||||
border: 1px solid $studio-gray-5;
|
||||
border-radius: 3px;
|
||||
z-index: 10;
|
||||
|
||||
display: none;
|
||||
&.is-open.has-results {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.woocommerce-experimental-select-control__popover-menu-container {
|
||||
margin: 0;
|
||||
max-height: 300px;
|
||||
overflow-y: scroll;
|
||||
|
||||
> .category-field-dropdown__item:not( :first-child ) {
|
||||
.category-field-dropdown__item-content {
|
||||
border-top: 1px solid $gray-200;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Popover } from '@wordpress/components';
|
||||
import classnames from 'classnames';
|
||||
import { createElement } from '@wordpress/element';
|
||||
import {
|
||||
createElement,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
createPortal,
|
||||
} from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -22,21 +29,63 @@ export const Menu = ( {
|
|||
isOpen,
|
||||
className,
|
||||
}: MenuProps ) => {
|
||||
const [ boundingRect, setBoundingRect ] = useState< DOMRect >();
|
||||
const selectControlMenuRef = useRef< HTMLDivElement >( null );
|
||||
|
||||
useEffect( () => {
|
||||
if ( selectControlMenuRef.current?.parentElement ) {
|
||||
setBoundingRect(
|
||||
selectControlMenuRef.current.parentElement.getBoundingClientRect()
|
||||
);
|
||||
}
|
||||
}, [ selectControlMenuRef.current ] );
|
||||
|
||||
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/click-events-have-key-events */
|
||||
/* Disabled because of the onmouseup on the ul element below. */
|
||||
return (
|
||||
<ul
|
||||
{ ...getMenuProps() }
|
||||
<div
|
||||
ref={ selectControlMenuRef }
|
||||
className={ classnames(
|
||||
'woocommerce-experimental-select-control__menu',
|
||||
className,
|
||||
className
|
||||
) }
|
||||
>
|
||||
<Popover
|
||||
focusOnMount={ false }
|
||||
className={ classnames(
|
||||
'woocommerce-experimental-select-control__popover-menu',
|
||||
{
|
||||
'is-open': isOpen,
|
||||
'has-results': Array.isArray( children )
|
||||
? children.length
|
||||
: Boolean( children ),
|
||||
'has-results':
|
||||
Array.isArray( children ) && children.length > 0,
|
||||
}
|
||||
) }
|
||||
position="bottom center"
|
||||
animate={ false }
|
||||
>
|
||||
<ul
|
||||
{ ...getMenuProps() }
|
||||
className="woocommerce-experimental-select-control__popover-menu-container"
|
||||
style={ {
|
||||
width: boundingRect?.width,
|
||||
} }
|
||||
onMouseUp={ ( e ) =>
|
||||
// Fix to prevent select control dropdown from closing when selecting within the Popover.
|
||||
e.stopPropagation()
|
||||
}
|
||||
>
|
||||
{ isOpen && children }
|
||||
</ul>
|
||||
</Popover>
|
||||
</div>
|
||||
);
|
||||
/* eslint-enable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/click-events-have-key-events */
|
||||
};
|
||||
|
||||
export const MenuSlot: React.FC = () =>
|
||||
createPortal(
|
||||
<div aria-live="off">
|
||||
<Popover.Slot />
|
||||
</div>,
|
||||
document.body
|
||||
);
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { CheckboxControl, Spinner } from '@wordpress/components';
|
||||
import {
|
||||
Button,
|
||||
CheckboxControl,
|
||||
Modal,
|
||||
SlotFillProvider,
|
||||
Spinner,
|
||||
} from '@wordpress/components';
|
||||
import React from 'react';
|
||||
import { createElement, useState } from '@wordpress/element';
|
||||
|
||||
|
@ -11,7 +17,7 @@ import { createElement, useState } from '@wordpress/element';
|
|||
import { SelectedType, DefaultItemType, getItemLabelType } from '../types';
|
||||
import { MenuItem } from '../menu-item';
|
||||
import { SelectControl, selectControlStateChangeTypes } from '../';
|
||||
import { Menu } from '../menu';
|
||||
import { Menu, MenuSlot } from '../menu';
|
||||
|
||||
const sampleItems = [
|
||||
{ value: 'apple', label: 'Apple' },
|
||||
|
@ -365,6 +371,45 @@ export const CustomItemType: React.FC = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export const SingleWithinModalUsingBodyDropdownPlacement: React.FC = () => {
|
||||
const [ isOpen, setOpen ] = useState( true );
|
||||
const [ selected, setSelected ] =
|
||||
useState< SelectedType< DefaultItemType > >();
|
||||
const [ selectedTwo, setSelectedTwo ] =
|
||||
useState< SelectedType< DefaultItemType > >();
|
||||
|
||||
return (
|
||||
<SlotFillProvider>
|
||||
Selected: { JSON.stringify( selected ) }
|
||||
<Button onClick={ () => setOpen( true ) }>
|
||||
Show Dropdown in Modal
|
||||
</Button>
|
||||
{ isOpen && (
|
||||
<Modal
|
||||
title="Dropdown Modal"
|
||||
onRequestClose={ () => setOpen( false ) }
|
||||
>
|
||||
<SelectControl
|
||||
items={ sampleItems }
|
||||
label="Single value"
|
||||
selected={ selected }
|
||||
onSelect={ ( item ) => item && setSelected( item ) }
|
||||
onRemove={ () => setSelected( null ) }
|
||||
/>
|
||||
<SelectControl
|
||||
items={ sampleItems }
|
||||
label="Single value"
|
||||
selected={ selectedTwo }
|
||||
onSelect={ ( item ) => item && setSelectedTwo( item ) }
|
||||
onRemove={ () => setSelectedTwo( null ) }
|
||||
/>
|
||||
</Modal>
|
||||
) }
|
||||
<MenuSlot />
|
||||
</SlotFillProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default {
|
||||
title: 'WooCommerce Admin/experimental/SelectControl',
|
||||
component: SelectControl,
|
||||
|
|
|
@ -49,7 +49,10 @@ export {
|
|||
MenuItem as __experimentalSelectControlMenuItem,
|
||||
MenuItemProps as __experimentalSelectControlMenuItemProps,
|
||||
} from './experimental-select-control/menu-item';
|
||||
export { Menu as __experimentalSelectControlMenu } from './experimental-select-control/menu';
|
||||
export {
|
||||
Menu as __experimentalSelectControlMenu,
|
||||
MenuSlot as __experimentalSelectControlMenuSlot,
|
||||
} from './experimental-select-control/menu';
|
||||
export { default as ScrollTo } from './scroll-to';
|
||||
export { Sortable } from './sortable';
|
||||
export { ListItem } from './list-item';
|
||||
|
|
Loading…
Reference in New Issue