From 5b4cf8a7edc272ce7c1953f96878074d732168ab Mon Sep 17 00:00:00 2001 From: kyletsang <6854874+kyletsang@users.noreply.github.com> Date: Thu, 22 Jul 2021 12:21:13 -0700 Subject: [PATCH] feat: clean up dropdown props --- src/Dropdown.tsx | 111 +++++++++++++++++++---------------------- src/DropdownMenu.tsx | 98 +++++++++++++++++++----------------- src/DropdownToggle.tsx | 15 ++---- 3 files changed, 109 insertions(+), 115 deletions(-) diff --git a/src/Dropdown.tsx b/src/Dropdown.tsx index 709e0fb..9278cbb 100644 --- a/src/Dropdown.tsx +++ b/src/Dropdown.tsx @@ -2,7 +2,6 @@ import qsa from 'dom-helpers/querySelectorAll'; import addEventListener from 'dom-helpers/addEventListener'; import { useCallback, useRef, useEffect, useMemo, useContext } from 'react'; import * as React from 'react'; -import PropTypes from 'prop-types'; import { useUncontrolledProp } from 'uncontrollable'; import usePrevious from '@restart/hooks/usePrevious'; import useForceUpdate from '@restart/hooks/useForceUpdate'; @@ -25,7 +24,6 @@ import SelectableContext from './SelectableContext'; import { SelectCallback } from './types'; import { dataAttr } from './DataKey'; import { Placement } from './usePopper'; -import { placements } from './popper'; export type { DropdownMenuProps, @@ -36,91 +34,88 @@ export type { DropdownItemProps, }; -const propTypes = { - /** - * A render prop that returns the root dropdown element. The `props` - * argument should spread through to an element containing _both_ the - * menu and toggle in order to handle keyboard events for focus management. - * - * @type {Function ({ - * props: { - * onKeyDown: (SyntheticEvent) => void, - * }, - * }) => React.Element} - */ - children: PropTypes.node, +export interface DropdownInjectedProps { + onKeyDown: React.KeyboardEventHandler; +} - /** - * The PopperJS placement for positioning the Dropdown menu in relation to it's Toggle. - * - * @default 'bottom-start' - */ - placement: PropTypes.oneOf(placements), +export type ToggleEvent = React.SyntheticEvent | KeyboardEvent | MouseEvent; +export interface ToggleMetadata { + source: string | undefined; + originalEvent: ToggleEvent | undefined; +} + +export interface DropdownProps { /** - * Controls the focus behavior for when the Dropdown is opened. Set to - * `true` to always focus the first menu item, `keyboard` to focus only when - * navigating via the keyboard, or `false` to disable completely + * The PopperJS placement for positioning the Dropdown menu in relation to + * its Toggle. * - * The Default behavior is `false` **unless** the Menu has a `role="menu"` - * where it will default to `keyboard` to match the recommended [ARIA Authoring practices](https://www.w3.org/TR/wai-aria-practices-1.1/#menubutton). + * @default 'bottom-start' */ - focusFirstItemOnShow: PropTypes.oneOf([false, true, 'keyboard']), + placement?: Placement; /** - * A css slector string that will return __focusable__ menu items. - * Selectors should be relative to the menu component: - * e.g. ` > li:not('.disabled')` + * Sets the initial visibility of the Dropdown. */ - itemSelector: PropTypes.string, + defaultShow?: boolean; /** * Whether or not the Dropdown is visible. * * @controllable onToggle */ - show: PropTypes.bool, + show?: boolean; /** - * Sets the initial show position of the Dropdown. + * A callback fired when a DropdownItem has been selected. */ - defaultShow: PropTypes.bool, + onSelect?: SelectCallback; /** - * A callback fired when the Dropdown wishes to change visibility. Called with the requested - * `show` value, the DOM event, and the source that fired it: `'click'`,`'keydown'`,`'rootClose'`, or `'select'`. + * A callback fired when the Dropdown wishes to change visibility. Called with + * the requested `show` value, the DOM event, and the source that fired it: + * `'click'`,`'keydown'`,`'rootClose'`, or `'select'`. * * ```ts static * function( - * isOpen: boolean, - * event: SyntheticEvent, + * nextShow: boolean, + * meta: ToggleMetadata, * ): void * ``` * * @controllable show */ - onToggle: PropTypes.func, -}; - -export interface DropdownInjectedProps { - onKeyDown: React.KeyboardEventHandler; -} + onToggle?: (nextShow: boolean, meta: ToggleMetadata) => void; -export type ToggleEvent = React.SyntheticEvent | KeyboardEvent | MouseEvent; + /** + * A css selector string that will return __focusable__ menu items. + * Selectors should be relative to the menu component: + * e.g. ` > li:not('.disabled')` + */ + itemSelector?: string; -export interface ToggleMetadata { - source: string | undefined; - originalEvent: ToggleEvent | undefined; -} + /** + * Controls the focus behavior for when the Dropdown is opened. Set to + * `true` to always focus the first menu item, `keyboard` to focus only when + * navigating via the keyboard, or `false` to disable completely + * + * The Default behavior is `false` **unless** the Menu has a `role="menu"` + * where it will default to `keyboard` to match the recommended [ARIA Authoring + * practices](https://www.w3.org/TR/wai-aria-practices-1.1/#menubutton). + */ + focusFirstItemOnShow?: boolean | 'keyboard'; -export interface DropdownProps { - placement?: Placement; - defaultShow?: boolean; - show?: boolean; - onSelect?: SelectCallback; - onToggle?: (nextShow: boolean, meta: ToggleMetadata) => void; - itemSelector?: string; - focusFirstItemOnShow?: false | true | 'keyboard'; + /** + * A render prop that returns the root dropdown element. The `props` + * argument should spread through to an element containing _both_ the + * menu and toggle in order to handle keyboard events for focus management. + * + * @type {Function ({ + * props: { + * onKeyDown: (SyntheticEvent) => void, + * }, + * }) => React.Element} + */ children: React.ReactNode; } @@ -344,8 +339,6 @@ function Dropdown({ Dropdown.displayName = 'Dropdown'; -Dropdown.propTypes = propTypes; - Dropdown.Menu = DropdownMenu; Dropdown.Toggle = DropdownToggle; Dropdown.Item = DropdownItem; diff --git a/src/DropdownMenu.tsx b/src/DropdownMenu.tsx index f438ded..e122b12 100644 --- a/src/DropdownMenu.tsx +++ b/src/DropdownMenu.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import { useContext, useRef } from 'react'; import * as React from 'react'; import useCallbackRef from '@restart/hooks/useCallbackRef'; @@ -11,17 +10,62 @@ import usePopper, { } from './usePopper'; import useRootClose, { RootCloseOptions } from './useRootClose'; import mergeOptionsWithPopperConfig from './mergeOptionsWithPopperConfig'; -import { placements } from './popper'; export interface UseDropdownMenuOptions { + /** + * Enables the Popper.js `flip` modifier, allowing the Dropdown to + * automatically adjust it's placement in case of overlap with the viewport or + * toggle. See the [flip docs](https://popper.js.org/docs/v2/modifiers/flip/) + * for more info. + */ flip?: boolean; + + /** + * Controls the visible state of the menu, generally this is provided by the + * parent `Dropdown` component, but may also be specified as a prop directly. + */ show?: boolean; + + /** + * Use the `fixed` positioning strategy in Popper. This is used if the + * dropdown toggle is in a fixed container. + */ fixed?: boolean; + + /** + * The PopperJS placement for positioning the Dropdown menu in relation to it's Toggle. + * Generally this is provided by the parent `Dropdown` component, + * but may also be specified as a prop directly. + */ placement?: Placement; + + /** + * Whether or not to use Popper for positioning the menu. + */ usePopper?: boolean; + + /** + * Whether or not to add scroll and resize listeners to update menu position. + * + * See the [event listeners docs](https://popper.js.org/docs/v2/modifiers/event-listeners/) + * for more info. + */ enableEventListeners?: boolean; + + /** + * Offset of the dropdown menu from the dropdown toggle. See the + * [offset docs](https://popper.js.org/docs/v2/modifiers/offset/) for more info. + */ offset?: Offset; + + /** + * Override the default event used by RootCloseWrapper. + */ rootCloseEvent?: RootCloseOptions['clickTrigger']; + + /** + * A set of popper options and props passed directly to react-popper's Popper component. + */ popperConfig?: Omit; } @@ -132,10 +176,14 @@ export function useDropdownMenu(options: UseDropdownMenuOptions = {}) { return [menuProps, metadata] as const; } -const propTypes = { +const defaultProps = { + usePopper: true, +}; + +export interface DropdownMenuProps extends UseDropdownMenuOptions { /** * A render prop that returns a Menu element. The `props` - * argument should spread through to **a component that can accept a ref**. + * argument should be spread through to **a component that can accept a ref**. * * @type {Function ({ * show: boolean, @@ -154,47 +202,6 @@ const propTypes = { * }, * }) => React.Element} */ - children: PropTypes.func.isRequired, - - /** - * Controls the visible state of the menu, generally this is - * provided by the parent `Dropdown` component, - * but may also be specified as a prop directly. - */ - show: PropTypes.bool, - - /** - * The PopperJS placement for positioning the Dropdown menu in relation to it's Toggle. - * Generally this is provided by the parent `Dropdown` component, - * but may also be specified as a prop directly. - */ - placement: PropTypes.oneOf(placements), - - /** - * Enables the Popper.js `flip` modifier, allowing the Dropdown to - * automatically adjust it's placement in case of overlap with the viewport or toggle. - * Refer to the [flip docs](https://popper.js.org/popper-documentation.html#modifiers..flip.enabled) for more info - */ - flip: PropTypes.bool, - - usePopper: PropTypes.oneOf([true, false]), - - /** - * A set of popper options and props passed directly to react-popper's Popper component. - */ - popperConfig: PropTypes.object, - - /** - * Override the default event used by RootCloseWrapper. - */ - rootCloseEvent: PropTypes.string, -}; - -const defaultProps = { - usePopper: true, -}; - -export interface DropdownMenuProps extends UseDropdownMenuOptions { children: ( props: UserDropdownMenuProps, meta: UseDropdownMenuMetadata, @@ -215,7 +222,6 @@ function DropdownMenu({ children, ...options }: DropdownMenuProps) { DropdownMenu.displayName = 'DropdownMenu'; -DropdownMenu.propTypes = propTypes; DropdownMenu.defaultProps = defaultProps; /** @component */ diff --git a/src/DropdownToggle.tsx b/src/DropdownToggle.tsx index 3474968..436b5aa 100644 --- a/src/DropdownToggle.tsx +++ b/src/DropdownToggle.tsx @@ -1,5 +1,3 @@ -import PropTypes from 'prop-types'; - import { useContext, useCallback } from 'react'; import * as React from 'react'; import { useSSRSafeId } from './ssr'; @@ -60,26 +58,24 @@ export function useDropdownToggle(): [ return [props, { show, toggle }]; } -const propTypes = { +export interface DropdownToggleProps { /** * A render prop that returns a Toggle element. The `props` * argument should spread through to **a component that can accept a ref**. Use * the `onToggle` argument to toggle the menu open or closed * * @type {Function ({ - * show: boolean, - * toggle: (show: boolean) => void, * props: { * ref: (?HTMLElement) => void, * aria-haspopup: true * aria-expanded: boolean * }, + * meta: { + * show: boolean, + * toggle: (show: boolean) => void, + * } * }) => React.Element} */ - children: PropTypes.func.isRequired, -}; - -export interface DropdownToggleProps { children: ( props: UseDropdownToggleProps, meta: UseDropdownToggleMetadata, @@ -99,7 +95,6 @@ function DropdownToggle({ children }: DropdownToggleProps) { } DropdownToggle.displayName = 'DropdownToggle'; -DropdownToggle.propTypes = propTypes; /** @component */ export default DropdownToggle;