-
Notifications
You must be signed in to change notification settings - Fork 220
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
Typescript and docs #794
Typescript and docs #794
Changes from all commits
bb6c132
4b88bf5
bec7378
e88183b
3e96fa8
070d2cf
cf2b0e9
e15838c
fb09e97
262108b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,16 @@ | ||
{ | ||
"extends": ["@react-bootstrap"], | ||
"extends": [ | ||
"@react-bootstrap", | ||
"4catalyzer-typescript", | ||
"prettier", | ||
"prettier/react", | ||
"prettier/@typescript-eslint" | ||
], | ||
"plugins": ["prettier"], | ||
"rules": { | ||
"jsx-a11y/no-autofocus": "off", | ||
"prettier/prettier": "error" | ||
"prettier/prettier": "error", | ||
"import/extensions": "off", | ||
"@typescript-eslint/no-empty-function": "off" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,13 +2,13 @@ import matches from 'dom-helpers/matches'; | |
import qsa from 'dom-helpers/querySelectorAll'; | ||
import React, { useCallback, useRef, useEffect, useMemo } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { useUncontrolled } from 'uncontrollable'; | ||
import { useUncontrolledProp } from 'uncontrollable'; | ||
import usePrevious from '@restart/hooks/usePrevious'; | ||
import useCallbackRef from '@restart/hooks/useCallbackRef'; | ||
import useForceUpdate from '@restart/hooks/useForceUpdate'; | ||
import useEventCallback from '@restart/hooks/useEventCallback'; | ||
|
||
import DropdownContext from './DropdownContext'; | ||
import DropdownContext, { DropDirection } from './DropdownContext'; | ||
import DropdownMenu from './DropdownMenu'; | ||
import DropdownToggle from './DropdownToggle'; | ||
|
||
|
@@ -46,7 +46,7 @@ const propTypes = { | |
* Selectors should be relative to the menu component: | ||
* e.g. ` > li:not('.disabled')` | ||
*/ | ||
itemSelector: PropTypes.string.isRequired, | ||
itemSelector: PropTypes.string, | ||
|
||
/** | ||
* Align the menu to the 'end' side of the placement side of the Dropdown toggle. The default placement is `top-start` or `bottom-start`. | ||
|
@@ -69,7 +69,7 @@ const propTypes = { | |
* 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'`. | ||
* | ||
* ```js | ||
* ```ts static | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not editable in place |
||
* function( | ||
* isOpen: boolean, | ||
* event: SyntheticEvent, | ||
|
@@ -81,46 +81,51 @@ const propTypes = { | |
onToggle: PropTypes.func, | ||
}; | ||
|
||
const defaultProps = { | ||
itemSelector: '* > *', | ||
}; | ||
export interface DropdownInjectedProps { | ||
onKeyDown: React.KeyboardEventHandler; | ||
} | ||
|
||
export interface DropdownProps { | ||
drop?: DropDirection; | ||
alignEnd?: boolean; | ||
defaultShow?: boolean; | ||
show?: boolean; | ||
onToggle: (nextShow: boolean, event?: React.SyntheticEvent) => void; | ||
itemSelector?: string; | ||
focusFirstItemOnShow?: false | true | 'keyboard'; | ||
children: (arg: { props: DropdownInjectedProps }) => React.ReactNode; | ||
} | ||
|
||
/** | ||
* `Dropdown` is set of structural components for building, accessible dropdown menus with close-on-click, | ||
* keyboard navigation, and correct focus handling. As with all the react-overlay's | ||
* components its BYOS (bring your own styles). Dropdown is primarily | ||
* built from three base components, you should compose to build your Dropdowns. | ||
* | ||
* - `Dropdown`, which wraps the menu and toggle, and handles keyboard navigation | ||
* - `Dropdown.Toggle` generally a button that triggers the menu opening | ||
* - `Dropdown.Menu` The overlaid, menu, positioned to the toggle with PopperJs | ||
* @displayName Dropdown | ||
*/ | ||
function Dropdown({ | ||
drop, | ||
alignEnd, | ||
defaultShow, | ||
show: rawShow, | ||
onToggle: rawOnToggle, | ||
itemSelector, | ||
itemSelector = '* > *', | ||
focusFirstItemOnShow, | ||
children, | ||
}) { | ||
}: DropdownProps) { | ||
const forceUpdate = useForceUpdate(); | ||
const { show, onToggle } = useUncontrolled( | ||
{ defaultShow, show: rawShow, onToggle: rawOnToggle }, | ||
{ show: 'onToggle' }, | ||
const [show, onToggle] = useUncontrolledProp( | ||
rawShow, | ||
defaultShow!, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. non-null assertion here seems somewhat wrong? this is potentially legitimately null-y, no? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It can, the bang is just to make the type correct without specifying the generic There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. non-null assertion here seems somewhat wrong? this is potentially legitimately null-y, no? |
||
rawOnToggle, | ||
); | ||
|
||
const [toggleElement, setToggle] = useCallbackRef(); | ||
const [toggleElement, setToggle] = useCallbackRef<HTMLElement>(); | ||
|
||
// We use normal refs instead of useCallbackRef in order to populate the | ||
// the value as quickly as possible, otherwise the effect to focus the element | ||
// may run before the state value is set | ||
const menuRef = useRef(); | ||
const menuRef = useRef<HTMLElement | null>(null); | ||
const menuElement = menuRef.current; | ||
|
||
const setMenu = useCallback( | ||
(ref) => { | ||
(ref: null | HTMLElement) => { | ||
menuRef.current = ref; | ||
// ensure that a menu set triggers an update for consumers | ||
forceUpdate(); | ||
|
@@ -129,7 +134,7 @@ function Dropdown({ | |
); | ||
|
||
const lastShow = usePrevious(show); | ||
const lastSourceEvent = useRef(null); | ||
const lastSourceEvent = useRef<string | null>(null); | ||
const focusInDropdown = useRef(false); | ||
|
||
const toggle = useCallback( | ||
|
@@ -185,12 +190,12 @@ function Dropdown({ | |
|
||
if ( | ||
focusType === false || | ||
(focusType === 'keyboard' && !/^key.+$/.test(type)) | ||
(focusType === 'keyboard' && !/^key.+$/.test(type!)) | ||
) { | ||
return; | ||
} | ||
|
||
let first = qsa(menuRef.current, itemSelector)[0]; | ||
const first = qsa(menuRef.current!, itemSelector)[0]; | ||
if (first && first.focus) first.focus(); | ||
}); | ||
|
||
|
@@ -207,19 +212,20 @@ function Dropdown({ | |
lastSourceEvent.current = null; | ||
}); | ||
|
||
const getNextFocusedChild = (current, offset) => { | ||
const getNextFocusedChild = (current: HTMLElement, offset: number) => { | ||
if (!menuRef.current) return null; | ||
|
||
let items = qsa(menuRef.current, itemSelector); | ||
const items = qsa(menuRef.current, itemSelector); | ||
|
||
let index = items.indexOf(current) + offset; | ||
index = Math.max(0, Math.min(index, items.length)); | ||
|
||
return items[index]; | ||
}; | ||
|
||
const handleKeyDown = (event) => { | ||
const { key, target } = event; | ||
const handleKeyDown = (event: React.KeyboardEvent) => { | ||
const { key } = event; | ||
const target = event.target as HTMLElement; | ||
|
||
// Second only to https://github.com/twbs/bootstrap/blob/8cfbf6933b8a0146ac3fbc369f19e520bd1ebdac/js/src/dropdown.js#L400 | ||
// in inscrutability | ||
|
@@ -238,7 +244,7 @@ function Dropdown({ | |
|
||
switch (key) { | ||
case 'ArrowUp': { | ||
let next = getNextFocusedChild(target, -1); | ||
const next = getNextFocusedChild(target, -1); | ||
if (next && next.focus) next.focus(); | ||
event.preventDefault(); | ||
|
||
|
@@ -249,7 +255,7 @@ function Dropdown({ | |
if (!show) { | ||
toggle(event); | ||
} else { | ||
let next = getNextFocusedChild(target, 1); | ||
const next = getNextFocusedChild(target, 1); | ||
if (next && next.focus) next.focus(); | ||
} | ||
return; | ||
|
@@ -271,7 +277,6 @@ function Dropdown({ | |
Dropdown.displayName = 'ReactOverlaysDropdown'; | ||
|
||
Dropdown.propTypes = propTypes; | ||
Dropdown.defaultProps = defaultProps; | ||
|
||
Dropdown.Menu = DropdownMenu; | ||
Dropdown.Toggle = DropdownToggle; | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import React from 'react'; | ||
|
||
export type DropDirection = 'up' | 'down' | 'left' | 'right'; | ||
|
||
export type DropdownContextValue = { | ||
toggle: (nextShow: boolean, event?: React.SyntheticEvent | Event) => void; | ||
menuElement: HTMLElement | null; | ||
toggleElement: HTMLElement | null; | ||
setMenu: (ref: HTMLElement | null) => void; | ||
setToggle: (ref: HTMLElement | null) => void; | ||
|
||
show: boolean; | ||
alignEnd?: boolean; | ||
drop?: DropDirection; | ||
}; | ||
|
||
const DropdownContext = React.createContext<DropdownContextValue | null>(null); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. might be safer to do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. needed for examples and things where we just show e.g. the Dropdown Menu |
||
|
||
export default DropdownContext; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mmaguebo