Skip to content
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

Using FloatingUI with Tooltips #1311

Merged
merged 60 commits into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
1bfd0fa
Adding floating ui
gretanausedaite May 22, 2023
9fa8894
Trying to make that work
gretanausedaite May 23, 2023
0a1fd99
Adding tooltip stuff
gretanausedaite May 29, 2023
0e9e02c
Merge remote-tracking branch 'origin/dev' into greta/flaoting-ui
gretanausedaite May 29, 2023
701e11e
Tooltip in use
gretanausedaite May 29, 2023
846ccc5
move useTooltip hook to hooks
gretanausedaite May 29, 2023
500abf5
rename some props
gretanausedaite May 30, 2023
e4526b2
Testing commit message
gretanausedaite May 30, 2023
6caa8e8
Merge remote-tracking branch 'origin/dev' into greta/flaoting-ui
gretanausedaite May 31, 2023
78349e3
test tooltips
gretanausedaite May 31, 2023
5612aab
code to follow
gretanausedaite May 31, 2023
489353f
Merge branch 'dev' into greta/flaoting-ui
gretanausedaite Jun 5, 2023
c63f4bc
Merge branch 'dev' into greta/flaoting-ui
gretanausedaite Jun 7, 2023
6a1ff7d
Trying to move things around
gretanausedaite Jun 7, 2023
15a4e1f
Updating tooltip
gretanausedaite Jun 7, 2023
10ca0d5
doing something
gretanausedaite Jun 7, 2023
bb3d015
Merge branch 'dev' into greta/flaoting-ui
gretanausedaite Jun 7, 2023
57fb938
aria tests
gretanausedaite Jun 8, 2023
6c92a27
added copyright back
gretanausedaite Jun 8, 2023
1d9711c
Merge branch 'dev' into greta/flaoting-ui
gretanausedaite Jun 9, 2023
036eba8
+- working
gretanausedaite Jun 9, 2023
0a4f064
Test image update
gretanausedaite Jun 9, 2023
2c6be71
cleanup
gretanausedaite Jun 9, 2023
aba9381
move dep from devdep and portal prop
gretanausedaite Jun 12, 2023
4a7eeac
Merge remote-tracking branch 'origin/dev' into greta/flaoting-ui
gretanausedaite Jun 12, 2023
ed9b87e
Adding reference will not be easy
gretanausedaite Jun 12, 2023
39039e1
Merge branch 'dev' into greta/flaoting-ui
gretanausedaite Jun 12, 2023
d685395
Merge branch 'dev' into greta/flaoting-ui
gretanausedaite Jun 13, 2023
de7cc7b
middleware props
gretanausedaite Jun 13, 2023
7941e3c
Strange things happening
gretanausedaite Jun 14, 2023
a968a8a
Maybe it works?
gretanausedaite Jun 14, 2023
852f8f2
Merge branch 'dev' into greta/flaoting-ui
gretanausedaite Jun 14, 2023
4067a2b
delay
gretanausedaite Jun 15, 2023
4bafd1f
hoverable fix
gretanausedaite Jun 16, 2023
78b969c
soma updates
gretanausedaite Jun 16, 2023
0388ed9
Merge remote-tracking branch 'origin/dev' into greta/flaoting-ui
gretanausedaite Jun 19, 2023
4218e10
Images update
gretanausedaite Jun 19, 2023
ceb1fad
Code and test update
gretanausedaite Jun 19, 2023
7eb56db
small change undo
gretanausedaite Jun 19, 2023
5f55605
Unit test update
gretanausedaite Jun 20, 2023
632a611
middle of fixing unit tests
gretanausedaite Jun 20, 2023
9d49e79
Merge branch 'dev' into greta/flaoting-ui
gretanausedaite Jun 22, 2023
f89929b
tests fixes v2
gretanausedaite Jun 22, 2023
8238668
Merge branch 'dev' into greta/flaoting-ui
gretanausedaite Jun 27, 2023
2fe2428
Figured out ref stuff!
gretanausedaite Jun 27, 2023
79b6e93
Fixed unit tests?
gretanausedaite Jun 27, 2023
3688e31
merge children ref
gretanausedaite Jun 27, 2023
ea653d9
add comments
gretanausedaite Jun 27, 2023
06f69d0
Maybe fix build?
gretanausedaite Jun 27, 2023
862810a
fix ref
gretanausedaite Jun 28, 2023
efb03ed
Update comments
gretanausedaite Jun 28, 2023
11df6c4
Update image?
gretanausedaite Jun 28, 2023
4262507
changeset
gretanausedaite Jun 28, 2023
0a8e749
image update
gretanausedaite Jun 28, 2023
7bf4cc0
Merge branch 'dev' into greta/flaoting-ui
gretanausedaite Jun 28, 2023
b0aa00e
missing props and jsdoc
gretanausedaite Jun 28, 2023
1eb021f
Merge branch 'dev' into greta/flaoting-ui
gretanausedaite Jun 29, 2023
4867592
Update package
gretanausedaite Jun 29, 2023
8ee3dbc
Update .changeset/silly-roses-yell.md
gretanausedaite Jun 30, 2023
a46162e
Merge branch 'dev' into greta/flaoting-ui
gretanausedaite Jun 30, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this one is broken. We rely on reference prop from tippy to do this, so need an equivalent here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the reference!

Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified .../storybook/cypress-visual-screenshots/baseline/Breadcrumbs.test.ts-Overflow.png
100644 → 100755
mayank99 marked this conversation as resolved.
Show resolved Hide resolved
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion apps/website/src/examples/Avatar.groupoverflowtooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default () => {
];

/**
* Ref is set on the last avatar for tooltip positioning.
* Ref is set on the last avatar for Tooltip positioning.
*/
const avatarRef = React.useRef<HTMLDivElement>(null);

Expand Down
1 change: 1 addition & 0 deletions packages/itwinui-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
},
"devDependencies": {
"@babel/core": "^7.12.10",
"@floating-ui/react": "^0.24.1",
mayank99 marked this conversation as resolved.
Show resolved Hide resolved
"@itwin/itwinui-css": "^2.0.0-dev.1",
"@itwin/itwinui-variables": "2.1.0-dev.0",
"@swc/cli": "^0.1.57",
Expand Down
8 changes: 1 addition & 7 deletions packages/itwinui-react/src/core/Slider/Thumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,7 @@ export const Thumb = (props: ThumbProps) => {
const { style, className, ...rest } = thumbProps || {};

return (
<Tooltip
placement='top'
trigger={
tooltipProps?.visible == null ? 'mouseenter click focus' : undefined
}
{...tooltipProps}
>
<Tooltip placement='top' followTrigger {...tooltipProps}>
<Box
{...rest}
data-index={index}
Expand Down
214 changes: 174 additions & 40 deletions packages/itwinui-react/src/core/Tooltip/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,192 @@
*--------------------------------------------------------------------------------------------*/
import * as React from 'react';
import cx from 'classnames';
import { Popover, Box } from '../utils/index.js';
import type { CommonProps, PopoverProps } from '../utils/index.js';
import {
useMergeRefs,
useFloating,
autoUpdate,
offset,
flip,
shift,
useHover,
useFocus,
useDismiss,
useRole,
useInteractions,
useClick,
FloatingPortal,
} from '@floating-ui/react';
import type { Placement } from '@floating-ui/react';
import { Box, type PolymorphicForwardRefComponent } from '../utils/index.js';
import { ThemeContext } from '../ThemeProvider/ThemeContext.js';
import ReactDOM from 'react-dom';

export type TooltipProps = {
type TooltipOptions = {
/**
* Placement of the Tooltip
* @default 'top'
*/
placement?: Placement;
/**
* Property for manual visibility control
*/
visible?: boolean;
/**
* Function that allows users to control Tooltip visibility
*/
toggleVisible?: (open: boolean) => void;
mayank99 marked this conversation as resolved.
Show resolved Hide resolved
/**
* Use this if you want Tooltip to follow moving trigger element
* @default false;
*/
followTrigger?: boolean;
};

type TooltipOwnProps = {
/**
* Content of the tooltip.
*/
content: React.ReactNode;
content?: React.ReactNode;
mayank99 marked this conversation as resolved.
Show resolved Hide resolved
/**
* Element to have tooltip on. Has to be a valid JSX element and needs to forward its ref.
* If not specified, the `reference` prop should be used instead.
*/
children?: JSX.Element;
} & Omit<PopoverProps, 'className'> &
Omit<CommonProps, 'title'>;
children?: React.ReactNode;
/**
* Element to append tooltip to.
* Appends to ThemeProvider portalContainerRef by default.
*/
appendTo?: HTMLElement;
mayank99 marked this conversation as resolved.
Show resolved Hide resolved
};

/**
* Basic tooltip component to display informative content when an element is hovered or focused.
* Uses the {@link Popover} component, which is a wrapper around [tippy.js](https://atomiks.github.io/tippyjs).
* @example
* <Tooltip content='tooltip text' placement='top'><div>Hover here</div></Tooltip>
* @example
* const buttonRef = React.useRef();
* ...
* <Button ref={buttonRef} />
* <Tooltip content='tooltip text' reference={buttonRef} />
*/
export const Tooltip = (props: TooltipProps) => {
const { content, children, className, style, visible, ref, id, ...rest } =
props;
const useTooltip = ({
placement = 'top',
visible: controlledOpen,
toggleVisible: setControlledOpen,
followTrigger = false,
mayank99 marked this conversation as resolved.
Show resolved Hide resolved
}: TooltipOptions = {}) => {
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false);

const open = controlledOpen ?? uncontrolledOpen;
const setOpen = setControlledOpen ?? setUncontrolledOpen;

const data = useFloating({
placement,
open,
onOpenChange: setOpen,
whileElementsMounted: (referenceEl, floatingEl, update) =>
autoUpdate(referenceEl, floatingEl, update, {
animationFrame: followTrigger,
}),
middleware: [offset(5), flip(), shift()],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allow users to customise middleware?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe adding prop would be ok?

middleware?: {
  offset?: number;
  flip?: boolean;
  ..etc;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, lets control all the names now. Other option would be to allow user to pass any and all middleware, but then they might start doing weird things

I also think it should be possible for the user to toggle autoUpdate

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

autoUpdateOptions were added.

mayank99 marked this conversation as resolved.
Show resolved Hide resolved
});

const context = data.context;

const hover = useHover(context);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be allow users to customise interactions? eg. disable tooltip on focus.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be nice to give control, but i fear that developers will start removing focus as trigger, making tooltips inaccessible

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not giving control for now. We can add that later.

Copy link
Contributor

@mayank99 mayank99 Jun 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hover should have some delay and also use safePolygon (with negative buffer).
currently the tooltip closes as soon as you leave the reference element, so it fails WCAG 1.4.13 (see "Hoverable" part). Adding delay and safePolygon should fix both of these issues.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also missing controlled enabled (similar to useFocus)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed this one.

const focus = useFocus(context, {
enabled: controlledOpen == null,
});
const click = useClick(context);

const dismiss = useDismiss(context);
const role = useRole(context, { role: 'tooltip' });

const interactions = useInteractions([hover, focus, click, dismiss, role]);

return React.useMemo(
() => ({
open,
setOpen,
...interactions,
...data,
}),
[open, setOpen, interactions, data],
);
};

const TooltipContext = React.createContext<
mayank99 marked this conversation as resolved.
Show resolved Hide resolved
(ReturnType<typeof useTooltip> & TooltipOwnProps) | null
>(null);

const useTooltipContext = () => {
const context = React.useContext(TooltipContext);

if (context == null) {
throw new Error('Tooltip components must be wrapped in <Tooltip />');
}

return context;
};

const TooltipComponent = ({
content,
children,
appendTo,
...options
}: TooltipOwnProps & TooltipOptions) => {
const tooltip = useTooltip(options);
return (
<Popover
visible={visible}
interactive={false}
content={
<Box
className={cx('iui-tooltip', className)}
style={style}
role='tooltip'
id={id}
>
{content}
</Box>
}
offset={[0, 4]}
<TooltipContext.Provider value={{ ...tooltip, appendTo }}>
<TooltipTrigger>{children}</TooltipTrigger>
<TooltipContent>{content}</TooltipContent>
</TooltipContext.Provider>
);
};

const TooltipTrigger = React.forwardRef((props, propRef) => {
const { children, ...rest } = props;
const context = useTooltipContext();
const ref = useMergeRefs([context.refs.setReference, propRef]);

return React.isValidElement(children)
? React.cloneElement(
children,
context.getReferenceProps({
ref,
...rest,
...children.props,
}),
)
: null;
// eslint-disable-next-line @typescript-eslint/ban-types
}) as PolymorphicForwardRefComponent<'div', {}>;
mayank99 marked this conversation as resolved.
Show resolved Hide resolved

const TooltipContent = React.forwardRef((props, propRef) => {
const { children, className, ...rest } = props;
const context = useTooltipContext();
const themeInfo = React.useContext(ThemeContext);
const ref = useMergeRefs([context.refs.setFloating, propRef]);
mayank99 marked this conversation as resolved.
Show resolved Hide resolved

const contentBox = (
<Box
className={cx('iui-tooltip', className)}
ref={ref}
{...rest}
style={context.floatingStyles}
{...context.getFloatingProps(rest)}
>
{children && React.cloneElement(children, { title: undefined })}
</Popover>
{children}
</Box>
);
};

export default Tooltip;
if (!context.open) {
return null;
}

const portalRoot = context.appendTo ?? themeInfo?.portalContainerRef?.current;

return portalRoot ? (
ReactDOM.createPortal(contentBox, portalRoot)
) : (
<FloatingPortal>{contentBox}</FloatingPortal>
mayank99 marked this conversation as resolved.
Show resolved Hide resolved
);
// eslint-disable-next-line @typescript-eslint/ban-types
}) as PolymorphicForwardRefComponent<'div', {}>;

/**
* Basic tooltip component to display informative content when an element is hovered or focused.
* Uses [FloatingUI](https://floating-ui.com/).
* @example
* <Tooltip content='tooltip text' placement='top'>Hover here</Tooltip>
*/
export const Tooltip = TooltipComponent;
mayank99 marked this conversation as resolved.
Show resolved Hide resolved
45 changes: 45 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1775,6 +1775,34 @@
resolved "https://registry.yarnpkg.com/@fal-works/esbuild-plugin-global-externals/-/esbuild-plugin-global-externals-2.1.2.tgz#c05ed35ad82df8e6ac616c68b92c2282bd083ba4"
integrity sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==

"@floating-ui/core@^1.2.6":
version "1.2.6"
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.2.6.tgz#d21ace437cc919cdd8f1640302fa8851e65e75c0"
integrity sha512-EvYTiXet5XqweYGClEmpu3BoxmsQ4hkj3QaYA6qEnigCWffTP3vNRwBReTdrwDwo7OoJ3wM8Uoe9Uk4n+d4hfg==

"@floating-ui/dom@^1.2.7":
version "1.2.8"
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.2.8.tgz#aee0f6ccc0787ab8fe741487a6e5e95b7b125375"
integrity sha512-XLwhYV90MxiHDq6S0rzFZj00fnDM+A1R9jhSioZoMsa7G0Q0i+Q4x40ajR8FHSdYDE1bgjG45mIWe6jtv9UPmg==
dependencies:
"@floating-ui/core" "^1.2.6"

"@floating-ui/react-dom@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.0.tgz#7514baac526c818892bbcc84e1c3115008c029f9"
integrity sha512-Ke0oU3SeuABC2C4OFu2mSAwHIP5WUiV98O9YWoHV4Q5aT6E9k06DV0Khi5uYspR8xmmBk08t8ZDcz3TR3ARkEg==
dependencies:
"@floating-ui/dom" "^1.2.7"

"@floating-ui/react@^0.24.1":
version "0.24.1"
resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.24.1.tgz#9ef81c78045c4b88ac062755826a3daf7cbe23d6"
integrity sha512-qjCKUZDEz/4bnJmu4gn66TqsoX912/re8JGEi3pXazsphmyh327l0UpTgpBAT3WkNbnzAH7Adt3wKlLMNtfupw==
dependencies:
"@floating-ui/react-dom" "^2.0.0"
aria-hidden "^1.1.3"
tabbable "^6.0.1"

"@fontsource/noto-sans-mono@^4.5.11":
version "4.5.11"
resolved "https://registry.yarnpkg.com/@fontsource/noto-sans-mono/-/noto-sans-mono-4.5.11.tgz#e213d695b3ef8fb2060ca3d86738b969f1ea0599"
Expand Down Expand Up @@ -4488,6 +4516,13 @@ argparse@^2.0.1:
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==

aria-hidden@^1.1.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.3.tgz#14aeb7fb692bbb72d69bebfa47279c1fd725e954"
integrity sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==
dependencies:
tslib "^2.0.0"

aria-query@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.0.tgz#210c21aaf469613ee8c9a62c7f86525e058db52c"
Expand Down Expand Up @@ -13759,6 +13794,11 @@ synckit@^0.8.4:
"@pkgr/utils" "^2.3.1"
tslib "^2.4.0"

tabbable@^6.0.1:
version "6.1.2"
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.1.2.tgz#b0d3ca81d582d48a80f71b267d1434b1469a3703"
integrity sha512-qCN98uP7i9z0fIS4amQ5zbGBOq+OSigYeGvPy7NDk8Y9yncqDZ9pRPgfsc2PJIVM9RrJj7GIfuRgmjoUU9zTHQ==

table@^6.7.5:
version "6.8.0"
resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca"
Expand Down Expand Up @@ -14083,6 +14123,11 @@ tslib@^1.8.1, tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==

tslib@^2.0.0:
version "2.5.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.2.tgz#1b6f07185c881557b0ffa84b111a0106989e8338"
integrity sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==

tslib@^2.0.1, tslib@^2.1.0, tslib@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
Expand Down