Skip to content

Commit

Permalink
Merge pull request #502 from influxdata/feat-escape-key
Browse files Browse the repository at this point in the history
Allow popovers to be closed via the Escape key
  • Loading branch information
alexpaxton authored Jun 11, 2020
2 parents 5e28bb5 + 387e1e6 commit 3e17379
Show file tree
Hide file tree
Showing 10 changed files with 245 additions and 51 deletions.
17 changes: 16 additions & 1 deletion .storybook/Story.scss
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,15 @@ body.sb-show-main {
&.top {
align-items: flex-start;
}

code {
background-color: $g0-obsidian;
color: $c-galaxy;
font-weight: 700;
line-height: 1em;
padding: 3px 7px;
border-radius: 3px;
}
}

.overlay--example {
Expand Down Expand Up @@ -262,6 +271,12 @@ body.sb-show-main {
}
}

.story--invisible-table {
td {
padding: 22px;
}
}

/*
Markdown Styles
------------------------------------------------------------------------------
Expand Down Expand Up @@ -779,8 +794,8 @@ $color-grid-card-width: 154px;

&.relative {
position: relative;
top: initial;
right: initial;
margin: 12px;
}
}

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#### 2.2.1 (Unreleased)

- [#508](https://github.com/influxdata/clockface/pull/508): Add `testID` to thumb elements in `DapperScrollbars`
- [#502](https://github.com/influxdata/clockface/pull/502): Enable `Popover` to be closed by the escape key

#### 2.2.0 [2020-05-12]

Expand Down
4 changes: 4 additions & 0 deletions src/Components/Popover/Base/Popover.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
.cf-popover {
position: fixed;
z-index: 9999;

&:focus {
outline: none;
}
}

.cf-popover--contents {
Expand Down
46 changes: 38 additions & 8 deletions src/Components/Popover/Base/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ import uuid from 'uuid'
import {PopoverDialog} from './PopoverDialog'

// Utils
import {createPortalElement, destroyPortalElement} from '../../../Utils/index'
import {
createPortalElement,
destroyPortalElement,
updatePortalEventListener,
} from '../../../Utils/index'

// Styles
import './Popover.scss'
Expand Down Expand Up @@ -49,7 +53,7 @@ export interface PopoverProps extends StandardFunctionProps {
position?: PopoverPosition
/** Means of applying color to popover */
appearance: Appearance
/** Overrides internal popover expanded state */
/** For external control of the popover state, overrides escape key behavior */
visible?: boolean
/** Disables the popover's show interaction */
disabled?: boolean
Expand Down Expand Up @@ -78,7 +82,7 @@ export const PopoverRoot = forwardRef<PopoverRef, PopoverProps>(
className,
triggerRef,
caretSize = 8,
visible = false,
visible,
disabled = false,
testID = 'popover',
distanceFromTrigger = 4,
Expand All @@ -98,15 +102,23 @@ export const PopoverRoot = forwardRef<PopoverRef, PopoverProps>(
const newPortalID = `cf-${popoverPortalName}-portal-${uuid.v4()}`
setPortalID(newPortalID)

createPortalElement(newPortalID, popoverPortalName)
handleAddEventListeners()
createPortalElement(
newPortalID,
popoverPortalName,
undefined,
handleAddEscapeKeyListener
)

handleAddEventListenersToTrigger()

return (): void => {
updatePortalEventListener(portalID, handleRemoveEscapeKeyListener)
destroyPortalElement(newPortalID)
handleRemoveEventListeners()
handleRemoveEventListenersFromTrigger()
}
}, [])

// Show or hide dialog in reponse to changes in "visible" prop
useEffect(() => {
if (visible) {
handleShowDialog()
Expand Down Expand Up @@ -169,6 +181,14 @@ export const PopoverRoot = forwardRef<PopoverRef, PopoverProps>(
}
}

const handleEscapeKey = (e: KeyboardEvent): void => {
// Only hide dialog on escape key press if the
// popover is not being controlled externally
if (e.key === 'Escape' && visible === undefined) {
handleHideDialog()
}
}

const handleShowDialog = (): void => {
onShow && onShow()
setExpanded(true)
Expand All @@ -179,7 +199,17 @@ export const PopoverRoot = forwardRef<PopoverRef, PopoverProps>(
setExpanded(false)
}

const handleAddEventListeners = (): void => {
const handleAddEscapeKeyListener = (portalElement: HTMLElement): void => {
portalElement.addEventListener('keydown', handleEscapeKey)
}

const handleRemoveEscapeKeyListener = (
portalElement: HTMLElement
): void => {
portalElement.removeEventListener('keydown', handleEscapeKey)
}

const handleAddEventListenersToTrigger = (): void => {
if (!triggerRef.current) {
return
}
Expand All @@ -198,7 +228,7 @@ export const PopoverRoot = forwardRef<PopoverRef, PopoverProps>(
}
}

const handleRemoveEventListeners = (): void => {
const handleRemoveEventListenersFromTrigger = (): void => {
if (!triggerRef.current) {
return
}
Expand Down
17 changes: 17 additions & 0 deletions src/Components/Popover/Base/PopoverDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, {
useRef,
RefObject,
MouseEvent,
useEffect,
useLayoutEffect,
forwardRef,
} from 'react'
Expand Down Expand Up @@ -146,6 +147,21 @@ export const PopoverDialog = forwardRef<PopoverDialogRef, PopoverDialogProps>(
handleUpdateStyles()
})

// Ensure styles are updated when the
// enableDefaultStyles prop changes
useEffect(() => {
handleUpdateStyles()
}, [enableDefaultStyles])

// Ensure dialog element is in focus on mount
// in order to enable escape key behavior
useEffect(() => {
const okayToPullFocus = !document.activeElement
if (dialogRef.current && okayToPullFocus) {
dialogRef.current.focus()
}
}, [])

return (
<ClickOutside onClickOutside={onClickOutside}>
<div
Expand All @@ -154,6 +170,7 @@ export const PopoverDialog = forwardRef<PopoverDialogRef, PopoverDialogProps>(
className={popoverDialogClassName}
data-testid={`${testID}--dialog`}
onMouseLeave={onMouseLeave}
tabIndex={-1}
>
<div
ref={ref}
Expand Down
6 changes: 5 additions & 1 deletion src/Components/Popover/Documentation/Popover.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,17 @@ Make sure to use the available `onHide` argument and pass it into your dismiss b
### Example
<!-- STORY -->

### Trying to Make a Custom Popover?
### Want more control over style within a Popover?

By default `Popover` will apply reasonable default styles to the dialog to save you time. However you may be building a more specialized design and want more control. Rather than fight the default styles you can simply turn them off:
```tsx
<Popover enableDefaultStyles={false} />
```

### Gotchas

By default all `Popover` instances will be in focus when they appear so that pressing the `Escape` key dismisses them. If a `Popover` has its state controlled externally via the `visible` prop then the `Escape` key listener is disabled in deference.

<!-- STORY HIDE START -->

<!-- STORY HIDE END -->
Expand Down
Loading

0 comments on commit 3e17379

Please # to comment.