Skip to content

Commit

Permalink
feat(Select): add examples, add popper interface, fix focus (#8992)
Browse files Browse the repository at this point in the history
* feat(Select): add examples, add popper interface, fix focus

* feedback round 1

* fix component reference in docs

* remove unneeded toggle focus

* remove popperprops extension, add beta to keys

* remove container

* update snap for removal of container div

* update button

* remove unneeded ref, add focus flag & comments

* toggleRef option

* update dropdown examples

* snap

* update desc

* update wording

* use single prop

* move toggle object to interface, add to docs

* prop req and remove extra prop
  • Loading branch information
kmcfaul committed May 18, 2023
1 parent 2da278e commit 4b296dd
Show file tree
Hide file tree
Showing 24 changed files with 1,202 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,39 +114,37 @@ exports[`With popover opened 1`] = `
>
Month
</span>
<div>
<button
aria-expanded="false"
class="pf-v5-c-menu-toggle"
style="width: 140px;"
type="button"
<button
aria-expanded="false"
class="pf-v5-c-menu-toggle"
style="width: 140px;"
type="button"
>
<span
class="pf-v5-c-menu-toggle__text"
>
December
</span>
<span
class="pf-v5-c-menu-toggle__controls"
>
<span
class="pf-v5-c-menu-toggle__text"
>
December
</span>
<span
class="pf-v5-c-menu-toggle__controls"
class="pf-v5-c-menu-toggle__toggle-icon"
>
<span
class="pf-v5-c-menu-toggle__toggle-icon"
<svg
aria-hidden="true"
class="pf-v5-svg"
fill="currentColor"
role="img"
viewBox="0 0 320 512"
>
<svg
aria-hidden="true"
class="pf-v5-svg"
fill="currentColor"
role="img"
viewBox="0 0 320 512"
>
<path
d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z"
/>
</svg>
</span>
<path
d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z"
/>
</svg>
</span>
</button>
</div>
</span>
</button>
</div>
</div>
<div
Expand Down Expand Up @@ -813,4 +811,4 @@ exports[`disabled date picker 1`] = `
</div>
</div>
</DocumentFragment>
`;
`;
36 changes: 25 additions & 11 deletions packages/react-core/src/components/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ export interface DropdownPopperProps {
enableFlip?: boolean;
}

export interface DropdownToggleProps {
/** Dropdown toggle node. */
toggleNode: React.ReactNode;
/** Reference to the toggle. */
toggleRef?: React.RefObject<HTMLButtonElement>;
}

/**
* See the Menu documentation for additional props that may be passed.
*/
Expand All @@ -27,16 +34,14 @@ export interface DropdownProps extends MenuProps, OUIAProps {
children?: React.ReactNode;
/** Classes applied to root element of dropdown. */
className?: string;
/** Renderer for a custom dropdown toggle. Forwards a ref to the toggle. */
toggle: (toggleRef: React.RefObject<any>) => React.ReactNode;
/** Dropdown toggle. The toggle should either be a renderer function which forwards the given toggle ref, or a direct ReactNode that should be passed along with the toggleRef property. */
toggle: DropdownToggleProps | ((toggleRef: React.RefObject<any>) => React.ReactNode);
/** Flag to indicate if menu is opened.*/
isOpen?: boolean;
/** Flag indicating the toggle should be focused after a selection. If this use case is too restrictive, the optional toggleRef property with a node toggle may be used to control focus. */
shouldFocusToggleOnSelect?: boolean;
/** Function callback called when user selects item. */
onSelect?: (
event?: React.MouseEvent<Element, MouseEvent>,
itemId?: string | number,
toggleRef?: React.RefObject<any>
) => void;
onSelect?: (event?: React.MouseEvent<Element, MouseEvent>, itemId?: string | number) => void;
/** Callback to allow the dropdown component to change the open state of the menu.
* Triggered by clicking outside of the menu, or by pressing either tab or escape. */
onOpenChange?: (isOpen: boolean) => void;
Expand All @@ -62,6 +67,7 @@ const DropdownBase: React.FunctionComponent<DropdownProps> = ({
onSelect,
isOpen,
toggle,
shouldFocusToggleOnSelect = false,
onOpenChange,
isPlain,
isScrollable,
Expand All @@ -73,10 +79,15 @@ const DropdownBase: React.FunctionComponent<DropdownProps> = ({
...props
}: DropdownProps) => {
const localMenuRef = React.useRef<HTMLDivElement>();
const toggleRef = React.useRef<HTMLButtonElement>();
const localToggleRef = React.useRef<HTMLButtonElement>();
const ouiaProps = useOUIAProps(Dropdown.displayName, ouiaId, ouiaSafe);

const menuRef = (innerRef as React.RefObject<HTMLDivElement>) || localMenuRef;
const toggleRef =
typeof toggle === 'function' || (typeof toggle !== 'function' && !toggle.toggleRef)
? localToggleRef
: (toggle?.toggleRef as React.RefObject<HTMLButtonElement>);

React.useEffect(() => {
const handleMenuKeys = (event: KeyboardEvent) => {
// Close the menu on tab or escape if onOpenChange is provided
Expand Down Expand Up @@ -119,13 +130,16 @@ const DropdownBase: React.FunctionComponent<DropdownProps> = ({
window.removeEventListener('keydown', handleMenuKeys);
window.removeEventListener('click', handleClick);
};
}, [isOpen, menuRef, onOpenChange]);
}, [isOpen, menuRef, toggleRef, onOpenChange]);

const menu = (
<Menu
className={css(className)}
ref={menuRef}
onSelect={(event, itemId) => onSelect && onSelect(event, itemId, toggleRef)}
onSelect={(event, itemId) => {
onSelect && onSelect(event, itemId);
shouldFocusToggleOnSelect && toggleRef.current.focus();
}}
isPlain={isPlain}
isScrollable={isScrollable}
{...props}
Expand All @@ -136,7 +150,7 @@ const DropdownBase: React.FunctionComponent<DropdownProps> = ({
);
return (
<Popper
trigger={toggle(toggleRef)}
trigger={typeof toggle === 'function' ? toggle(toggleRef) : toggle.toggleNode}
triggerRef={toggleRef}
popper={menu}
popperRef={menuRef}
Expand Down
11 changes: 10 additions & 1 deletion packages/react-core/src/components/Dropdown/examples/Dropdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,16 @@ id: Dropdown
section: components
subsection: menus
cssPrefix: pf-c-menu
propComponents: ['Dropdown', DropdownGroup, 'DropdownItem', 'DropdownList', 'MenuToggle', 'DropdownPopperProps']
propComponents:
[
'Dropdown',
'DropdownGroup',
'DropdownItem',
'DropdownList',
'MenuToggle',
'DropdownToggleProps',
'DropdownPopperProps'
]
---

import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,10 @@ export const DropdownBasic: React.FunctionComponent = () => {
setIsOpen(!isOpen);
};

const onSelect = (
_event: React.MouseEvent<Element, MouseEvent> | undefined,
itemId: string | number | undefined,
toggleRef: React.RefObject<any>
) => {
const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, itemId: string | number | undefined) => {
// eslint-disable-next-line no-console
console.log('selected', itemId);
setIsOpen(false);
toggleRef?.current.focus();
};

return (
Expand All @@ -30,6 +25,7 @@ export const DropdownBasic: React.FunctionComponent = () => {
</MenuToggle>
)}
ouiaId="BasicDropdown"
shouldFocusToggleOnSelect
>
<DropdownList>
<DropdownItem itemId={0} key="action">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,10 @@ export const DropdownWithDescriptions: React.FunctionComponent = () => {
setIsOpen(!isOpen);
};

const onSelect = (
_event: React.MouseEvent<Element, MouseEvent> | undefined,
itemId: string | number | undefined,
toggleRef: React.RefObject<any>
) => {
const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, itemId: string | number | undefined) => {
// eslint-disable-next-line no-console
console.log('selected', itemId);
setIsOpen(false);
toggleRef?.current.focus();
};

return (
Expand All @@ -29,6 +24,7 @@ export const DropdownWithDescriptions: React.FunctionComponent = () => {
Dropdown
</MenuToggle>
)}
shouldFocusToggleOnSelect
>
<DropdownList>
<DropdownItem itemId={0} key="action" description="This is a description">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,10 @@ export const DropdownWithGroups: React.FunctionComponent = () => {
setIsOpen(!isOpen);
};

const onSelect = (
_event: React.MouseEvent<Element, MouseEvent> | undefined,
itemId: string | number | undefined,
toggleRef: React.RefObject<any>
) => {
const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, itemId: string | number | undefined) => {
// eslint-disable-next-line no-console
console.log('selected', itemId);
setIsOpen(false);
toggleRef?.current.focus();
};

return (
Expand All @@ -37,6 +32,7 @@ export const DropdownWithGroups: React.FunctionComponent = () => {
Dropdown
</MenuToggle>
)}
shouldFocusToggleOnSelect
>
<DropdownGroup>
<DropdownList>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,10 @@ export const DropdownWithKebab: React.FunctionComponent = () => {
setIsOpen(!isOpen);
};

const onSelect = (
_event: React.MouseEvent<Element, MouseEvent> | undefined,
itemId: string | number | undefined,
toggleRef: React.RefObject<any>
) => {
const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, itemId: string | number | undefined) => {
// eslint-disable-next-line no-console
console.log('selected', itemId);
setIsOpen(false);
toggleRef?.current.focus();
};

return (
Expand All @@ -36,6 +31,7 @@ export const DropdownWithKebab: React.FunctionComponent = () => {
<EllipsisVIcon />
</MenuToggle>
)}
shouldFocusToggleOnSelect
>
<DropdownList>
<DropdownItem itemId={0} key="action">
Expand Down
Loading

0 comments on commit 4b296dd

Please sign in to comment.