Fix "in contacts" tooltip exceeding desired boundary

See [#4907][0].

[0]: https://github.com/signalapp/Signal-Desktop/pull/4907
This commit is contained in:
Will Golledge 2021-10-18 17:10:22 -05:00 committed by Evan Hahn
parent 54974b377a
commit 2cd02855fc
5 changed files with 65 additions and 4 deletions

View file

@ -9,15 +9,26 @@ import { LocalizerType } from '../types/Util';
type PropsType = { type PropsType = {
className?: string; className?: string;
tooltipContainerRef?: React.RefObject<HTMLElement>;
i18n: LocalizerType; i18n: LocalizerType;
}; };
export const InContactsIcon = (props: PropsType): JSX.Element => { export const InContactsIcon = (props: PropsType): JSX.Element => {
const { className, i18n } = props; const { className, i18n, tooltipContainerRef } = props;
/* eslint-disable jsx-a11y/no-noninteractive-tabindex */ /* eslint-disable jsx-a11y/no-noninteractive-tabindex */
return ( return (
<Tooltip content={i18n('contactInAddressBook')}> <Tooltip
content={i18n('contactInAddressBook')}
popperModifiers={[
{
name: 'preventOverflow',
options: {
boundary: tooltipContainerRef?.current || undefined,
},
},
]}
>
<span <span
aria-label={i18n('contactInAddressBook')} aria-label={i18n('contactInAddressBook')}
className={classNames('module-in-contacts-icon__icon', className)} className={classNames('module-in-contacts-icon__icon', className)}

View file

@ -81,6 +81,26 @@ story.add('Sticky', () => (
</Tooltip> </Tooltip>
)); ));
story.add('With Applied Popper Modifiers', () => {
return (
<Tooltip
{...createProps({
direction: TooltipPlacement.Bottom,
})}
popperModifiers={[
{
name: 'offset',
options: {
offset: [80, 80],
},
},
]}
>
{Trigger}
</Tooltip>
);
});
story.add('Dark Theme', () => ( story.add('Dark Theme', () => (
<Tooltip <Tooltip
{...createProps({ {...createProps({

View file

@ -5,6 +5,7 @@ import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { noop } from 'lodash'; import { noop } from 'lodash';
import { Manager, Reference, Popper } from 'react-popper'; import { Manager, Reference, Popper } from 'react-popper';
import type { StrictModifiers } from '@popperjs/core';
import { Theme, themeClassName } from '../util/theme'; import { Theme, themeClassName } from '../util/theme';
import { refMerger } from '../util/refMerger'; import { refMerger } from '../util/refMerger';
import { offsetDistanceModifier } from '../util/popperUtil'; import { offsetDistanceModifier } from '../util/popperUtil';
@ -70,6 +71,7 @@ export type PropsType = {
content: string | JSX.Element; content: string | JSX.Element;
className?: string; className?: string;
direction?: TooltipPlacement; direction?: TooltipPlacement;
popperModifiers?: Array<StrictModifiers>;
sticky?: boolean; sticky?: boolean;
theme?: Theme; theme?: Theme;
}; };
@ -81,6 +83,7 @@ export const Tooltip: React.FC<PropsType> = ({
direction, direction,
sticky, sticky,
theme, theme,
popperModifiers = [],
}) => { }) => {
const [isHovering, setIsHovering] = React.useState(false); const [isHovering, setIsHovering] = React.useState(false);
@ -99,7 +102,10 @@ export const Tooltip: React.FC<PropsType> = ({
</TooltipEventWrapper> </TooltipEventWrapper>
)} )}
</Reference> </Reference>
<Popper placement={direction} modifiers={[offsetDistanceModifier(12)]}> <Popper
placement={direction}
modifiers={[offsetDistanceModifier(12), ...popperModifiers]}
>
{({ arrowProps, placement, ref, style }) => {({ arrowProps, placement, ref, style }) =>
showTooltip && ( showTooltip && (
<div <div

View file

@ -113,12 +113,15 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
private menuTriggerRef: React.RefObject<any>; private menuTriggerRef: React.RefObject<any>;
public headerRef: React.RefObject<HTMLDivElement>;
public constructor(props: PropsType) { public constructor(props: PropsType) {
super(props); super(props);
this.state = { isNarrow: false, modalState: ModalState.NothingOpen }; this.state = { isNarrow: false, modalState: ModalState.NothingOpen };
this.menuTriggerRef = React.createRef(); this.menuTriggerRef = React.createRef();
this.headerRef = React.createRef();
this.showMenuBound = this.showMenu.bind(this); this.showMenuBound = this.showMenu.bind(this);
} }
@ -163,6 +166,7 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
<InContactsIcon <InContactsIcon
className="module-ConversationHeader__header__info__title__in-contacts-icon" className="module-ConversationHeader__header__info__title__in-contacts-icon"
i18n={i18n} i18n={i18n}
tooltipContainerRef={this.headerRef}
/> />
) : null} ) : null}
</div> </div>
@ -596,7 +600,11 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
); );
} }
return <div className="module-ConversationHeader__header">{contents}</div>; return (
<div className="module-ConversationHeader__header" ref={this.headerRef}>
{contents}
</div>
);
} }
public render(): ReactNode { public render(): ReactNode {

View file

@ -12792,6 +12792,22 @@
"updated": "2020-05-20T20:10:43.540Z", "updated": "2020-05-20T20:10:43.540Z",
"reasonDetail": "Used to reference popup menu" "reasonDetail": "Used to reference popup menu"
}, },
{
"rule": "React-createRef",
"path": "ts/components/conversation/ConversationHeader.js",
"line": " this.headerRef = react_1.default.createRef();",
"reasonCategory": "usageTrusted",
"updated": "2021-01-18T22:24:05.937Z",
"reasonDetail": "Used to reference popup menu boundaries element"
},
{
"rule": "React-createRef",
"path": "ts/components/conversation/ConversationHeader.tsx",
"line": " this.headerRef = React.createRef();",
"reasonCategory": "usageTrusted",
"updated": "2021-01-18T22:24:05.937Z",
"reasonDetail": "Used to reference popup menu boundaries element"
},
{ {
"rule": "React-useRef", "rule": "React-useRef",
"path": "ts/components/conversation/ConversationHero.tsx", "path": "ts/components/conversation/ConversationHero.tsx",