Skip to content

Commit 9fce5cd

Browse files
committed
fix: Update tooltip component
1 parent 76acd49 commit 9fce5cd

File tree

4 files changed

+110
-82
lines changed

4 files changed

+110
-82
lines changed

pages/tooltip/internal-tooltip-examples.page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ function InteractivePositionControlExample() {
9494
onBlur: () => setShowTooltip(false),
9595
}}
9696
>
97-
Hover me
97+
Hover
9898
</Button>
9999
{showTooltip && (
100100
<Tooltip

src/tooltip/__tests__/tooltip.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ function renderTooltip(props: Partial<TooltipProps>) {
3030
getTrack={props.getTrack ?? (() => null)}
3131
trackKey={props.trackKey}
3232
content={props.content ?? ''}
33-
onEscape={props.onEscape ?? (() => {})}
33+
onEscape={props.onEscape}
3434
/>
3535
);
3636
return new TooltipInternalWrapper(container);

src/tooltip/index.tsx

Lines changed: 14 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,24 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33
'use client';
4-
import React, { useEffect } from 'react';
5-
import clsx from 'clsx';
4+
import React from 'react';
65

7-
import { Portal } from '@cloudscape-design/component-toolkit/internal';
8-
9-
import { Transition } from '../internal/components/transition';
10-
import { fireNonCancelableEvent } from '../internal/events';
11-
import PopoverArrow from '../popover/arrow';
12-
import PopoverBody from '../popover/body';
13-
import PopoverContainer from '../popover/container';
14-
import { InternalTooltipProps, TooltipProps } from './interfaces';
15-
16-
import styles from './styles.css.js';
6+
import useBaseComponent from '../internal/hooks/use-base-component';
7+
import { applyDisplayName } from '../internal/utils/apply-display-name';
8+
import { getExternalProps } from '../internal/utils/external-props';
9+
import { TooltipProps } from './interfaces';
10+
import InternalTooltip from './internal';
1711

1812
export { TooltipProps };
1913

20-
export default function Tooltip({
21-
content,
22-
getTrack,
23-
trackKey,
24-
position = 'top',
25-
__dismissOnScroll,
26-
className,
27-
onEscape,
28-
}: InternalTooltipProps) {
29-
const trackRef = React.useRef<HTMLElement | SVGElement | null>(null);
30-
31-
// Update the ref with the current tracked element
32-
React.useEffect(() => {
33-
trackRef.current = getTrack();
14+
const Tooltip = ({ position = 'top', ...rest }: TooltipProps) => {
15+
const baseComponentProps = useBaseComponent('Tooltip', {
16+
props: { position },
3417
});
18+
const externalProps = getExternalProps(rest);
3519

36-
if (!trackKey && (typeof content === 'string' || typeof content === 'number')) {
37-
trackKey = content;
38-
}
39-
40-
useEffect(() => {
41-
const controller = new AbortController();
42-
window.addEventListener(
43-
'keydown',
44-
(event: KeyboardEvent) => {
45-
if (event.key === 'Escape') {
46-
// Prevent any surrounding modals or dialogs from acting on this Esc.
47-
event.stopPropagation();
48-
fireNonCancelableEvent(onEscape);
49-
}
50-
},
51-
{
52-
// The tooltip is often activated on mouseover, which means the focus can
53-
// be anywhere else on the page. Capture also means that this gets called
54-
// before any wrapper modals or dialogs can detect it and act on it.
55-
capture: true,
56-
signal: controller.signal,
57-
}
58-
);
59-
return () => {
60-
controller.abort();
61-
};
62-
}, [onEscape]);
20+
return <InternalTooltip position={position} {...externalProps} {...baseComponentProps} />;
21+
};
6322

64-
return (
65-
<Portal>
66-
<div className={clsx(styles.root, className)} data-testid={trackKey}>
67-
<Transition in={true}>
68-
{() => (
69-
<PopoverContainer
70-
trackRef={trackRef}
71-
trackKey={trackKey}
72-
size="medium"
73-
fixedWidth={false}
74-
position={position}
75-
zIndex={7000}
76-
arrow={position => <PopoverArrow position={position} />}
77-
hideOnOverscroll={__dismissOnScroll}
78-
className={className}
79-
>
80-
<PopoverBody dismissButton={false} dismissAriaLabel={undefined} onDismiss={undefined} header={undefined}>
81-
{content}
82-
</PopoverBody>
83-
</PopoverContainer>
84-
)}
85-
</Transition>
86-
</div>
87-
</Portal>
88-
);
89-
}
23+
applyDisplayName(Tooltip, 'Tooltip');
24+
export default Tooltip;

src/tooltip/internal.tsx

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,96 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
3-
export { default, TooltipProps } from './index.js';
3+
import React, { useEffect } from 'react';
4+
import clsx from 'clsx';
5+
6+
import { Portal } from '@cloudscape-design/component-toolkit/internal';
7+
8+
import { getBaseProps } from '../internal/base-component';
9+
import { Transition } from '../internal/components/transition';
10+
import { fireNonCancelableEvent } from '../internal/events';
11+
import { InternalBaseComponentProps } from '../internal/hooks/use-base-component';
12+
import PopoverArrow from '../popover/arrow';
13+
import PopoverBody from '../popover/body';
14+
import PopoverContainer from '../popover/container';
15+
import { InternalTooltipProps } from './interfaces';
16+
17+
import styles from './styles.css.js';
18+
19+
type InternalTooltipComponentProps = InternalTooltipProps & InternalBaseComponentProps;
20+
21+
export default function InternalTooltip({
22+
content,
23+
getTrack,
24+
trackKey,
25+
position = 'top',
26+
__dismissOnScroll,
27+
onEscape,
28+
__internalRootRef,
29+
...restProps
30+
}: InternalTooltipComponentProps) {
31+
const baseProps = getBaseProps(restProps);
32+
const trackRef = React.useRef<HTMLElement | SVGElement | null>(null);
33+
34+
// Update the ref with the current tracked element
35+
React.useEffect(() => {
36+
trackRef.current = getTrack();
37+
});
38+
39+
if (!trackKey && (typeof content === 'string' || typeof content === 'number')) {
40+
trackKey = content;
41+
}
42+
43+
useEffect(() => {
44+
const controller = new AbortController();
45+
window.addEventListener(
46+
'keydown',
47+
(event: KeyboardEvent) => {
48+
if (event.key === 'Escape') {
49+
// Prevent any surrounding modals or dialogs from acting on this Esc.
50+
event.stopPropagation();
51+
fireNonCancelableEvent(onEscape);
52+
}
53+
},
54+
{
55+
// The tooltip is often activated on mouseover, which means the focus can
56+
// be anywhere else on the page. Capture also means that this gets called
57+
// before any wrapper modals or dialogs can detect it and act on it.
58+
capture: true,
59+
signal: controller.signal,
60+
}
61+
);
62+
return () => {
63+
controller.abort();
64+
};
65+
}, [onEscape]);
66+
67+
return (
68+
<Portal>
69+
<div
70+
{...baseProps}
71+
className={clsx(styles.root, baseProps.className)}
72+
data-testid={trackKey}
73+
ref={__internalRootRef}
74+
>
75+
<Transition in={true}>
76+
{() => (
77+
<PopoverContainer
78+
trackRef={trackRef}
79+
trackKey={trackKey}
80+
size="medium"
81+
fixedWidth={false}
82+
position={position}
83+
zIndex={7000}
84+
arrow={position => <PopoverArrow position={position} />}
85+
hideOnOverscroll={__dismissOnScroll}
86+
>
87+
<PopoverBody dismissButton={false} dismissAriaLabel={undefined} onDismiss={undefined} header={undefined}>
88+
{content}
89+
</PopoverBody>
90+
</PopoverContainer>
91+
)}
92+
</Transition>
93+
</div>
94+
</Portal>
95+
);
96+
}

0 commit comments

Comments
 (0)