From b2bf1771fc5784c9eae74b7f708030ce35dcf9a5 Mon Sep 17 00:00:00 2001
From: Jason Quense
Date: Thu, 23 Jul 2020 13:11:48 -0400
Subject: [PATCH] feat: allow renderProp pattern in OverlayTrigger (#5316)
---
package.json | 4 +-
src/OverlayTrigger.tsx | 74 +++++++------------
www/src/components/Heading.js | 15 +---
www/src/components/NavMain.js | 4 +-
www/src/components/SideNav.js | 6 +-
www/src/components/Toc.js | 6 +-
www/src/examples/Overlays/OverlayTrigger.js | 19 -----
www/src/examples/Overlays/Trigger.js | 15 ++++
.../examples/Overlays/TriggerRenderProp.js | 21 ++++++
www/src/pages/components/overlays.js | 27 ++++++-
yarn.lock | 16 ++--
11 files changed, 107 insertions(+), 100 deletions(-)
delete mode 100644 www/src/examples/Overlays/OverlayTrigger.js
create mode 100644 www/src/examples/Overlays/Trigger.js
create mode 100644 www/src/examples/Overlays/TriggerRenderProp.js
diff --git a/package.json b/package.json
index ac86f72144..7b627aba82 100644
--- a/package.json
+++ b/package.json
@@ -76,8 +76,8 @@
"invariant": "^2.2.4",
"prop-types": "^15.7.2",
"prop-types-extra": "^1.1.0",
- "react-overlays": "^4.0.0",
- "react-transition-group": "^4.0.0",
+ "react-overlays": "^4.1.0",
+ "react-transition-group": "^4.4.1",
"uncontrollable": "^7.0.0",
"warning": "^4.0.3"
},
diff --git a/src/OverlayTrigger.tsx b/src/OverlayTrigger.tsx
index 849fdf23a0..e3caacea71 100644
--- a/src/OverlayTrigger.tsx
+++ b/src/OverlayTrigger.tsx
@@ -5,7 +5,6 @@ import useTimeout from '@restart/hooks/useTimeout';
import safeFindDOMNode from 'react-overlays/safeFindDOMNode';
import warning from 'warning';
import { useUncontrolledProp } from 'uncontrollable';
-import { Modifier } from 'react-overlays/esm/usePopper';
import Overlay, { OverlayChildren, OverlayProps } from './Overlay';
export type OverlayTriggerType = 'hover' | 'click' | 'focus';
@@ -16,9 +15,15 @@ export type OverlayInjectedProps = {
onFocus?: (...args: any[]) => any;
};
+export type OverlayTriggerRenderProps = OverlayInjectedProps & {
+ ref: React.Ref;
+};
+
export interface OverlayTriggerProps
extends Omit {
- children: React.ReactElement;
+ children:
+ | React.ReactElement
+ | ((props: OverlayTriggerRenderProps) => React.ReactNode);
trigger?: OverlayTriggerType | OverlayTriggerType[];
delay?: OverlayDelay;
show?: boolean;
@@ -51,9 +56,9 @@ function normalizeDelay(delay?: OverlayDelay) {
// for cases when the trigger is disabled and mouseOut/Over can cause flicker
// moving from one child element to another.
function handleMouseOverOut(
- handler: (...args: any[]) => any,
+ handler: (...args: [React.MouseEvent, ...any[]]) => any,
args: [React.MouseEvent, ...any[]],
- relatedNative,
+ relatedNative: 'fromElement' | 'toElement',
) {
const [e] = args;
const target = e.currentTarget;
@@ -188,9 +193,10 @@ function OverlayTrigger({
const delay = normalizeDelay(propsDelay);
- const child = React.Children.only(children);
-
- const { onFocus, onBlur, onClick } = child.props;
+ const { onFocus, onBlur, onClick } =
+ typeof children !== 'function'
+ ? React.Children.only(children).props
+ : ({} as any);
const getTarget = useCallback(
() => safeFindDOMNode(triggerNodeRef.current),
@@ -228,7 +234,7 @@ function OverlayTrigger({
const handleFocus = useCallback(
(...args: any[]) => {
handleShow();
- if (onFocus) onFocus(...args);
+ onFocus?.(...args);
},
[handleShow, onFocus],
);
@@ -236,7 +242,7 @@ function OverlayTrigger({
const handleBlur = useCallback(
(...args: any[]) => {
handleHide();
- if (onBlur) onBlur(...args);
+ onBlur?.(...args);
},
[handleHide, onBlur],
);
@@ -263,34 +269,6 @@ function OverlayTrigger({
[handleHide],
);
- // We add aria-describedby in the case where the overlay is a role="tooltip"
- // for other cases describedby isn't appropriate (e.g. a popover with inputs) so we don't add it.
- const ariaModifier: Modifier<'ariaDescribedBy', Record> = {
- name: 'ariaDescribedBy',
- enabled: true,
- phase: 'afterWrite',
- effect: ({ state }) => {
- return () => {
- if ('removeAttribute' in state.elements.reference)
- state.elements.reference.removeAttribute('aria-describedby');
- };
- },
- fn: ({ state }) => {
- const { popper, reference } = state.elements;
-
- if (!show || !reference) return;
-
- const role = popper.getAttribute('role') || '';
- if (
- popper.id &&
- role.toLowerCase() === 'tooltip' &&
- 'setAttribute' in reference
- ) {
- reference.setAttribute('aria-describedby', popper.id);
- }
- },
- };
-
const triggers: string[] = trigger == null ? [] : [].concat(trigger as any);
const triggerProps: any = {};
@@ -312,25 +290,23 @@ function OverlayTrigger({
triggerProps.onMouseOut = handleMouseOut;
}
- // TODO: fix typing
- // @ts-ignore
- const modifiers = [ariaModifier].concat(popperConfig.modifiers || []);
return (
<>
-
- {cloneElement(child as any, triggerProps)}
-
+ {typeof children === 'function' ? (
+ children({ ...triggerProps, ref: triggerNodeRef })
+ ) : (
+
+ {cloneElement(children as any, triggerProps)}
+
+ )}
{overlay}
diff --git a/www/src/components/Heading.js b/www/src/components/Heading.js
index e84a131275..590bb47cbd 100644
--- a/www/src/components/Heading.js
+++ b/www/src/components/Heading.js
@@ -10,18 +10,7 @@ const styles = css`
composes: __heading from global;
position: relative;
- pointer-events: none;
-
- &:before {
- display: block;
- height: 6rem;
- margin-top: -6rem;
- visibility: hidden;
- content: '';
- }
- }
- .inner {
- pointer-events: auto;
+ scroll-margin-top: 5rem;
}
`;
@@ -35,7 +24,7 @@ const Heading = ({ h, id, title, className, children, registerNode }) => {
const H = `h${h}`;
return (
- {children}
+ {children}
);
};
diff --git a/www/src/components/NavMain.js b/www/src/components/NavMain.js
index 48cf6c08f7..bd2e586a15 100644
--- a/www/src/components/NavMain.js
+++ b/www/src/components/NavMain.js
@@ -32,8 +32,6 @@ const Banner = styled(Navbar).attrs({
}
@include media-breakpoint-up(md) {
- position: sticky;
- top: 0rem;
z-index: 1040;
}
`;
@@ -51,7 +49,7 @@ const StyledNavbar = styled(Navbar).attrs({
@include media-breakpoint-up(md) {
position: sticky;
- top: 4rem;
+ top: 0rem;
z-index: 1040;
}
`;
diff --git a/www/src/components/SideNav.js b/www/src/components/SideNav.js
index 8d0b564c2c..f1df14219c 100644
--- a/www/src/components/SideNav.js
+++ b/www/src/components/SideNav.js
@@ -18,15 +18,17 @@ const MenuButton = styled(Button).attrs({ variant: 'link' })`
const SidePanel = styled('div')`
@import '../css/theme';
+ $top: 4rem;
+
composes: d-flex flex-column from global;
background-color: #f7f7f7;
@include media-breakpoint-up(md) {
position: sticky;
- top: 4rem;
+ top: $top;
z-index: 1000;
- height: calc(100vh - 4rem);
+ height: calc(100vh - #{$top});
background-color: #f7f7f7;
border-right: 1px solid $divider;
}
diff --git a/www/src/components/Toc.js b/www/src/components/Toc.js
index 3b6df3a84b..42ecb052cf 100644
--- a/www/src/components/Toc.js
+++ b/www/src/components/Toc.js
@@ -6,10 +6,12 @@ export const TocContext = React.createContext();
const SidePanel = styled('div')`
@import '../css/theme';
+ $top: 4rem;
+
order: 2;
position: sticky;
- top: 4rem;
- height: calc(100vh - 4rem);
+ top: $top;
+ height: calc(100vh - #{$top});
padding-top: 1.5rem;
padding-bottom: 1.5rem;
font-size: 0.875rem;
diff --git a/www/src/examples/Overlays/OverlayTrigger.js b/www/src/examples/Overlays/OverlayTrigger.js
deleted file mode 100644
index cee3c9f162..0000000000
--- a/www/src/examples/Overlays/OverlayTrigger.js
+++ /dev/null
@@ -1,19 +0,0 @@
-function renderTooltip(props) {
- return (
-
- Simple tooltip
-
- );
-}
-
-const Example = () => (
-
- Hover me to see
-
-);
-
-render( );
diff --git a/www/src/examples/Overlays/Trigger.js b/www/src/examples/Overlays/Trigger.js
new file mode 100644
index 0000000000..c79f5cf470
--- /dev/null
+++ b/www/src/examples/Overlays/Trigger.js
@@ -0,0 +1,15 @@
+const renderTooltip = (props) => (
+
+ Simple tooltip
+
+);
+
+render(
+
+ Hover me to see
+ ,
+);
diff --git a/www/src/examples/Overlays/TriggerRenderProp.js b/www/src/examples/Overlays/TriggerRenderProp.js
new file mode 100644
index 0000000000..e0c6d3a4fb
--- /dev/null
+++ b/www/src/examples/Overlays/TriggerRenderProp.js
@@ -0,0 +1,21 @@
+render(
+ Check out this avatar}
+ >
+ {({ ref, ...triggerHandler }) => (
+
+
+ Hover to see
+
+ )}
+ ,
+);
diff --git a/www/src/pages/components/overlays.js b/www/src/pages/components/overlays.js
index 58cfbd3b53..9806c2d194 100644
--- a/www/src/pages/components/overlays.js
+++ b/www/src/pages/components/overlays.js
@@ -5,10 +5,12 @@ import { css } from 'astroturf';
import LinkedHeading from '../../components/LinkedHeading';
import ComponentApi from '../../components/ComponentApi';
import ReactPlayground from '../../components/ReactPlayground';
+import Callout from '../../components/Callout';
import Disabled from '../../examples/Overlays/Disabled';
import Overlay from '../../examples/Overlays/Overlay';
-import OverlayTrigger from '../../examples/Overlays/OverlayTrigger';
+import OverlayTrigger from '../../examples/Overlays/Trigger';
+import TriggerRenderProp from '../../examples/Overlays/TriggerRenderProp';
import PopoverBasic from '../../examples/Overlays/PopoverBasic';
import PopoverContained from '../../examples/Overlays/PopoverContained';
import PopoverPositioned from '../../examples/Overlays/PopoverPositioned';
@@ -91,7 +93,7 @@ export default withLayout(function TooltipSection({ data }) {
-
+
OverlayTrigger
@@ -116,6 +118,27 @@ export default withLayout(function TooltipSection({ data }) {
+
+ Customizing trigger behavior
+
+
+
+ For more advanced behaviors {''}
accepts a
+ function child that passes in the injected ref
and event
+ handlers that coorespond to the configured trigger
prop.
+
+
+ You can manually apply the props to any element you want or split them
+ up. The example below shows how to position the overlay to a different
+ element than the one that triggers its visibility.
+
+
+ Pro Tip: Using the function form of OverlayTrigger
+ avoids a React.findDOMNode
call, for those trying to be
+ strict mode compliant.
+
+
+
Tooltips
diff --git a/yarn.lock b/yarn.lock
index 9bd663185c..797efaa47d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7601,10 +7601,10 @@ react-lifecycles-compat@^3.0.4:
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
-react-overlays@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-4.0.0.tgz#7fbcb60d12fee3733e9e4dd216b225e94fb3befe"
- integrity sha512-LpznWocwgeB5oWKg6cDdkqKP7MbX4ClKbJqgZGUMXPRBBYcqrgM6TjjZ/8DeurNU//GuqwQMjhmo/JVma4XEWw==
+react-overlays@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-4.1.0.tgz#755a890519b02e3904845172d5223ff2dfb1bb29"
+ integrity sha512-vdRpnKe0ckWOOD9uWdqykLUPHLPndIiUV7XfEKsi5008xiyHCfL8bxsx4LbMrfnxW1LzRthLyfy50XYRFNQqqw==
dependencies:
"@babel/runtime" "^7.4.5"
"@popperjs/core" "^2.0.0"
@@ -7625,10 +7625,10 @@ react-test-renderer@^16.0.0-0, react-test-renderer@^16.13.1:
react-is "^16.8.6"
scheduler "^0.19.1"
-react-transition-group@^4.0.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.3.0.tgz#fea832e386cf8796c58b61874a3319704f5ce683"
- integrity sha512-1qRV1ZuVSdxPlPf4O8t7inxUGpdyO5zG9IoNfJxSO0ImU2A1YWkEQvFPuIPZmMLkg5hYs7vv5mMOyfgSkvAwvw==
+react-transition-group@^4.4.1:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9"
+ integrity sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==
dependencies:
"@babel/runtime" "^7.5.5"
dom-helpers "^5.0.1"