Page MenuHomePhabricator

D4913.diff
No OneTemporary

D4913.diff

diff --git a/web/chat/composed-message.react.js b/web/chat/composed-message.react.js
--- a/web/chat/composed-message.react.js
+++ b/web/chat/composed-message.react.js
@@ -26,11 +26,11 @@
import { tooltipPositions } from './tooltip-utils';
const availableTooltipPositionsForViewerMessage = [
- tooltipPositions.TOP_RIGHT,
+ tooltipPositions.RIGHT_TOP,
tooltipPositions.LEFT,
];
const availableTooltipPositionsForNonViewerMessage = [
- tooltipPositions.TOP_LEFT,
+ tooltipPositions.LEFT_TOP,
tooltipPositions.RIGHT,
];
diff --git a/web/chat/message-timestamp-tooltip.react.js b/web/chat/message-timestamp-tooltip.react.js
--- a/web/chat/message-timestamp-tooltip.react.js
+++ b/web/chat/message-timestamp-tooltip.react.js
@@ -21,7 +21,7 @@
const availablePositionsForComposedViewerMessage = [tooltipPositions.LEFT];
const availablePositionsForNonComposedOrNonViewerMessage = [
- tooltipPositions.BOTTOM_RIGHT,
+ tooltipPositions.RIGHT_BOTTOM,
];
type Props = {
@@ -99,7 +99,7 @@
top: tooltipPointing,
};
className = css.messageRightTooltip;
- } else if (tooltipPosition === tooltipPositions.TOP_LEFT) {
+ } else if (tooltipPosition === tooltipPositions.LEFT_TOP) {
const tooltipPointing = Math.min(
containerHeight - messagePosition.top,
containerHeight,
@@ -109,7 +109,7 @@
bottom: tooltipPointing + sizeOfTooltipArrow,
};
className = css.messageTopLeftTooltip;
- } else if (tooltipPosition === tooltipPositions.TOP_RIGHT) {
+ } else if (tooltipPosition === tooltipPositions.RIGHT_TOP) {
const tooltipPointing = Math.min(
containerHeight - messagePosition.top,
containerHeight,
@@ -119,14 +119,14 @@
bottom: tooltipPointing + sizeOfTooltipArrow,
};
className = css.messageTopRightTooltip;
- } else if (tooltipPosition === tooltipPositions.BOTTOM_LEFT) {
+ } else if (tooltipPosition === tooltipPositions.LEFT_BOTTOM) {
const tooltipPointing = Math.min(messagePosition.bottom, containerHeight);
style = {
left: messagePosition.left,
top: tooltipPointing + sizeOfTooltipArrow,
};
className = css.messageBottomLeftTooltip;
- } else if (tooltipPosition === tooltipPositions.BOTTOM_RIGHT) {
+ } else if (tooltipPosition === tooltipPositions.RIGHT_BOTTOM) {
const centerOfMessage = messagePosition.top + messagePosition.height / 2;
const tooltipPointing = Math.max(
Math.min(centerOfMessage, containerHeight),
diff --git a/web/chat/robotext-message.react.js b/web/chat/robotext-message.react.js
--- a/web/chat/robotext-message.react.js
+++ b/web/chat/robotext-message.react.js
@@ -26,7 +26,7 @@
// eslint-disable-next-line no-unused-vars
const availableTooltipPositionsForRobotext = [
- tooltipPositions.TOP_RIGHT,
+ tooltipPositions.RIGHT_TOP,
tooltipPositions.RIGHT,
tooltipPositions.LEFT,
];
diff --git a/web/chat/tooltip-utils.js b/web/chat/tooltip-utils.js
--- a/web/chat/tooltip-utils.js
+++ b/web/chat/tooltip-utils.js
@@ -1,20 +1,25 @@
// @flow
-import invariant from 'invariant';
import * as React from 'react';
-import { calculateMaxTextWidth } from '../utils/text-utils';
-import type { ItemAndContainerPositionInfo } from './position-types';
+import type { PositionInfo } from './position-types';
export const tooltipPositions = Object.freeze({
LEFT: 'left',
RIGHT: 'right',
- BOTTOM_LEFT: 'bottom-left',
- BOTTOM_RIGHT: 'bottom-right',
- TOP_LEFT: 'top-left',
- TOP_RIGHT: 'top-right',
+ LEFT_BOTTOM: 'left-bottom',
+ RIGHT_BOTTOM: 'right-bottom',
+ LEFT_TOP: 'left-top',
+ RIGHT_TOP: 'right-top',
+ TOP: 'top',
+ BOTTOM: 'bottom',
});
+type TooltipSize = {
+ +height: number,
+ +width: number,
+};
+
export type TooltipPositionStyle = {
+xCoord: number,
+yCoord: number,
@@ -32,122 +37,141 @@
};
const sizeOfTooltipArrow = 10; // 7px arrow + 3px extra
-const tooltipMenuItemHeight = 22; // 17px line-height + 5px padding bottom
-const tooltipInnerTopPadding = 5; // 5px bottom is included in last item
-const tooltipInnerPadding = 10;
+const appTopBarHeight = 65;
+// eslint-disable-next-line no-unused-vars
const font =
'14px -apple-system, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", ' +
'"Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", ui-sans-serif';
type FindTooltipPositionArgs = {
- +pointingToInfo: ItemAndContainerPositionInfo,
- +tooltipTexts: $ReadOnlyArray<string>,
+ +sourcePositionInfo: PositionInfo,
+ +tooltipSize: TooltipSize,
+availablePositions: $ReadOnlyArray<TooltipPosition>,
- +layoutPosition: 'relative' | 'absolute',
+ +defaultPosition: TooltipPosition,
+ +preventDisplayingBelowSource?: boolean,
};
+
function findTooltipPosition({
- pointingToInfo,
- tooltipTexts,
+ sourcePositionInfo,
+ tooltipSize,
availablePositions,
- layoutPosition,
+ defaultPosition,
+ preventDisplayingBelowSource,
}: FindTooltipPositionArgs): TooltipPosition {
- const { itemPosition: pointingTo, containerPosition } = pointingToInfo;
+ if (!window) {
+ return defaultPosition;
+ }
+ const appContainerPositionInfo: PositionInfo = {
+ height: window.innerHeight - appTopBarHeight,
+ width: window.innerWidth,
+ top: appTopBarHeight,
+ bottom: window.innerHeight,
+ left: 0,
+ right: window.innerWidth,
+ };
+
+ const pointingTo = sourcePositionInfo;
const {
- height: containerHeight,
top: containerTop,
- width: containerWidth,
left: containerLeft,
- } = containerPosition;
-
- const textWidth = calculateMaxTextWidth(tooltipTexts, font);
- const width = textWidth + tooltipInnerPadding + sizeOfTooltipArrow;
- const numberOfTooltipItems = tooltipTexts.length;
- const tooltipHeight =
- numberOfTooltipItems * tooltipMenuItemHeight + tooltipInnerTopPadding;
- const heightWithArrow = tooltipHeight + sizeOfTooltipArrow;
-
- const absolutePositionedTooltip = layoutPosition === 'absolute';
-
- let canBeDisplayedInLeftPosition,
- canBeDisplayedInRightPosition,
- canBeDisplayedInTopPosition,
- canBeDisplayedInBottomPosition;
- if (absolutePositionedTooltip) {
- const pointingCenter = pointingTo.top + pointingTo.height / 2;
- const currentTop = Math.max(pointingTo.top, 0);
- const currentBottom = Math.min(pointingTo.bottom, containerHeight);
- const currentPointing = Math.max(
- Math.min(pointingCenter, containerHeight),
- 0,
- );
- const canBeDisplayedSideways =
- currentPointing - tooltipHeight / 2 + containerTop >= 0 &&
- currentPointing + tooltipHeight / 2 + containerTop <= window.innerHeight;
-
- canBeDisplayedInLeftPosition =
- pointingTo.left - width + containerLeft >= 0 && canBeDisplayedSideways;
- canBeDisplayedInRightPosition =
- pointingTo.right + width + containerLeft <= window.innerWidth &&
- canBeDisplayedSideways;
- canBeDisplayedInTopPosition =
- currentTop - heightWithArrow + containerTop >= 0;
- canBeDisplayedInBottomPosition =
- currentBottom + heightWithArrow + containerTop <= window.innerHeight;
- } else {
- const canBeDisplayedSideways =
- pointingTo.top - (tooltipHeight - pointingTo.height) / 2 >= 0 &&
- pointingTo.bottom + (tooltipHeight - pointingTo.height) / 2 <=
- containerHeight;
- canBeDisplayedInLeftPosition =
- pointingTo.left - width >= 0 && canBeDisplayedSideways;
- canBeDisplayedInRightPosition =
- pointingTo.right + width <= containerWidth && canBeDisplayedSideways;
- canBeDisplayedInTopPosition = pointingTo.top - heightWithArrow >= 0;
- canBeDisplayedInBottomPosition =
- pointingTo.bottom + heightWithArrow <= containerHeight;
- }
+ right: containerRight,
+ bottom: containerBottom,
+ } = appContainerPositionInfo;
+
+ const tooltipWidth = tooltipSize.width;
+ const tooltipHeight = tooltipSize.height;
+
+ const canBeDisplayedOnLeft = containerLeft + tooltipWidth <= pointingTo.left;
+ const canBeDisplayedOnRight =
+ tooltipWidth + pointingTo.right <= containerRight;
+
+ const willCoverSidebarOnTopSideways =
+ preventDisplayingBelowSource &&
+ pointingTo.top + tooltipHeight > pointingTo.bottom;
+
+ const canBeDisplayedOnTopSideways =
+ pointingTo.top >= containerTop &&
+ pointingTo.top + tooltipHeight <= containerBottom &&
+ !willCoverSidebarOnTopSideways;
+
+ const canBeDisplayedOnBottomSideways =
+ pointingTo.bottom <= containerBottom &&
+ pointingTo.bottom - tooltipHeight >= containerTop;
+
+ const verticalCenterOfPointingTo = pointingTo.top + pointingTo.height / 2;
+ const horizontalCenterOfPointingTo = pointingTo.left + pointingTo.width / 2;
+
+ const willCoverSidebarInTheMiddleSideways =
+ preventDisplayingBelowSource &&
+ verticalCenterOfPointingTo + tooltipHeight / 2 > pointingTo.bottom;
+
+ const canBeDisplayedInTheMiddleSideways =
+ verticalCenterOfPointingTo - tooltipHeight / 2 >= containerTop &&
+ verticalCenterOfPointingTo + tooltipHeight / 2 <= containerBottom &&
+ !willCoverSidebarInTheMiddleSideways;
+
+ const canBeDisplayedOnTop =
+ pointingTo.top - tooltipHeight >= containerTop &&
+ horizontalCenterOfPointingTo - tooltipWidth / 2 >= containerLeft &&
+ horizontalCenterOfPointingTo + tooltipWidth / 2 <= containerRight;
+
+ const canBeDisplayedOnBottom =
+ pointingTo.bottom + tooltipHeight <= containerBottom &&
+ horizontalCenterOfPointingTo - tooltipWidth / 2 >= containerLeft &&
+ horizontalCenterOfPointingTo + tooltipWidth / 2 <= containerRight &&
+ !preventDisplayingBelowSource;
for (const tooltipPosition of availablePositions) {
- invariant(
- numberOfTooltipItems === 1 ||
- (tooltipPosition !== tooltipPositions.RIGHT &&
- tooltipPosition !== tooltipPositions.LEFT),
- `${tooltipPosition} position can be used only for single element tooltip`,
- );
if (
tooltipPosition === tooltipPositions.RIGHT &&
- canBeDisplayedInRightPosition
+ canBeDisplayedOnRight &&
+ canBeDisplayedInTheMiddleSideways
) {
return tooltipPosition;
} else if (
- tooltipPosition === tooltipPositions.BOTTOM_RIGHT &&
- canBeDisplayedInBottomPosition
+ tooltipPosition === tooltipPositions.RIGHT_BOTTOM &&
+ canBeDisplayedOnRight &&
+ canBeDisplayedOnBottomSideways
) {
return tooltipPosition;
} else if (
tooltipPosition === tooltipPositions.LEFT &&
- canBeDisplayedInLeftPosition
+ canBeDisplayedOnLeft &&
+ canBeDisplayedInTheMiddleSideways
+ ) {
+ return tooltipPosition;
+ } else if (
+ tooltipPosition === tooltipPositions.LEFT_BOTTOM &&
+ canBeDisplayedOnLeft &&
+ canBeDisplayedOnBottomSideways
+ ) {
+ return tooltipPosition;
+ } else if (
+ tooltipPosition === tooltipPositions.LEFT_TOP &&
+ canBeDisplayedOnLeft &&
+ canBeDisplayedOnTopSideways
) {
return tooltipPosition;
} else if (
- tooltipPosition === tooltipPositions.BOTTOM_LEFT &&
- canBeDisplayedInBottomPosition
+ tooltipPosition === tooltipPositions.RIGHT_TOP &&
+ canBeDisplayedOnRight &&
+ canBeDisplayedOnTopSideways
) {
return tooltipPosition;
} else if (
- tooltipPosition === tooltipPositions.TOP_LEFT &&
- canBeDisplayedInTopPosition
+ tooltipPosition === tooltipPositions.TOP &&
+ canBeDisplayedOnTop
) {
return tooltipPosition;
} else if (
- tooltipPosition === tooltipPositions.TOP_RIGHT &&
- canBeDisplayedInTopPosition
+ tooltipPosition === tooltipPositions.BOTTOM &&
+ canBeDisplayedOnBottom
) {
return tooltipPosition;
}
}
- return availablePositions[availablePositions.length - 1];
+ return defaultPosition;
}
export { findTooltipPosition, sizeOfTooltipArrow };
diff --git a/web/chat/tooltip.react.js b/web/chat/tooltip.react.js
--- a/web/chat/tooltip.react.js
+++ b/web/chat/tooltip.react.js
@@ -4,7 +4,7 @@
import * as React from 'react';
import type { ItemAndContainerPositionInfo } from './position-types';
-import { findTooltipPosition, type TooltipPosition } from './tooltip-utils';
+import { type TooltipPosition, tooltipPositions } from './tooltip-utils';
import css from './tooltip.css';
type Style = {
@@ -25,34 +25,15 @@
>,
};
function TooltipMenu(props: TooltipMenuProps): React.Node {
- const {
- availableTooltipPositions,
- targetPositionInfo,
- layoutPosition,
- getStyle,
- children,
- } = props;
+ const { getStyle, children } = props;
+ // eslint-disable-next-line no-unused-vars
const tooltipTexts = React.useMemo(
() => React.Children.map(children, item => item.props.text),
[children],
);
- const tooltipPosition = React.useMemo(
- () =>
- findTooltipPosition({
- pointingToInfo: targetPositionInfo,
- tooltipTexts,
- availablePositions: availableTooltipPositions,
- layoutPosition,
- }),
- [
- tooltipTexts,
- targetPositionInfo,
- availableTooltipPositions,
- layoutPosition,
- ],
- );
+ const tooltipPosition = React.useMemo(() => tooltipPositions.LEFT, []);
const tooltipStyle = React.useMemo(() => getStyle(tooltipPosition), [
getStyle,
diff --git a/web/utils/tooltip-utils.test.js b/web/utils/tooltip-utils.test.js
new file mode 100644
--- /dev/null
+++ b/web/utils/tooltip-utils.test.js
@@ -0,0 +1,204 @@
+// @flow
+
+import type { PositionInfo } from '../chat/position-types';
+import { findTooltipPosition, tooltipPositions } from '../chat/tooltip-utils';
+
+const QHDWindow = {
+ width: 2560,
+ height: 1440,
+};
+
+const tooltipSourcePositionCenter: PositionInfo = {
+ width: 200,
+ height: 300,
+ left: QHDWindow.width / 2 - 100,
+ top: QHDWindow.height / 2 - 150,
+ right: QHDWindow.width / 2 + 100,
+ bottom: QHDWindow.height / 2 + 150,
+};
+
+const tooltipSourcePositionTopRight: PositionInfo = {
+ width: 200,
+ height: 300,
+ left: QHDWindow.width - 200,
+ top: 65, // app top bar height
+ right: QHDWindow.width,
+ bottom: 300 + 65, // tooltip height + app top bar height
+};
+
+const tooltipSourcePositionBottomLeft: PositionInfo = {
+ width: 200,
+ height: 300,
+ left: 0,
+ top: QHDWindow.height - 300,
+ right: 200,
+ bottom: QHDWindow.height,
+};
+
+const tooltipSizeSmall = {
+ width: 100,
+ height: 200,
+};
+
+const tooltipSizeBig = {
+ width: 300,
+ height: 500,
+};
+
+const allTooltipPositions = [
+ tooltipPositions.LEFT,
+ tooltipPositions.LEFT_TOP,
+ tooltipPositions.LEFT_BOTTOM,
+ tooltipPositions.RIGHT,
+ tooltipPositions.RIGHT_TOP,
+ tooltipPositions.RIGHT_BOTTOM,
+ tooltipPositions.TOP,
+ tooltipPositions.BOTTOM,
+];
+
+const sidewaysTooltipPositions = [
+ tooltipPositions.LEFT,
+ tooltipPositions.LEFT_TOP,
+ tooltipPositions.LEFT_BOTTOM,
+ tooltipPositions.RIGHT,
+ tooltipPositions.RIGHT_TOP,
+ tooltipPositions.RIGHT_BOTTOM,
+];
+
+const topAndBottomTooltipPositions = [
+ tooltipPositions.TOP,
+ tooltipPositions.BOTTOM,
+];
+
+const onlyLeftTooltipPositions = [
+ tooltipPositions.LEFT,
+ tooltipPositions.LEFT_BOTTOM,
+ tooltipPositions.LEFT_TOP,
+];
+
+beforeAll(() => {
+ window.innerWidth = QHDWindow.width;
+ window.innerHeight = QHDWindow.height;
+});
+
+afterAll(() => {
+ window.innerWidth = 1024;
+ window.innerHeight = 768;
+});
+
+describe('findTooltipPosition', () => {
+ it(
+ 'should return first position if there is enough space ' +
+ 'in every direction',
+ () =>
+ expect(
+ findTooltipPosition({
+ sourcePositionInfo: tooltipSourcePositionCenter,
+ tooltipSize: tooltipSizeSmall,
+ availablePositions: allTooltipPositions,
+ defaultPosition: allTooltipPositions[0],
+ }),
+ ).toMatch(allTooltipPositions[0]),
+ );
+
+ it(
+ 'should return first non-left position ' +
+ 'if there is no space on the left',
+ () =>
+ expect(
+ findTooltipPosition({
+ sourcePositionInfo: tooltipSourcePositionBottomLeft,
+ tooltipSize: tooltipSizeSmall,
+ availablePositions: sidewaysTooltipPositions,
+ defaultPosition: sidewaysTooltipPositions[0],
+ }),
+ ).toMatch(tooltipPositions.RIGHT),
+ );
+
+ it('should return bottom position if there is no space on the top ', () =>
+ expect(
+ findTooltipPosition({
+ sourcePositionInfo: tooltipSourcePositionTopRight,
+ tooltipSize: tooltipSizeSmall,
+ availablePositions: topAndBottomTooltipPositions,
+ defaultPosition: topAndBottomTooltipPositions[0],
+ }),
+ ).toMatch(tooltipPositions.BOTTOM));
+
+ it(
+ 'should return top left position if the tooltip is higher than the ' +
+ 'source object and there is no enough space on the top',
+ () =>
+ expect(
+ findTooltipPosition({
+ sourcePositionInfo: tooltipSourcePositionTopRight,
+ tooltipSize: tooltipSizeBig,
+ availablePositions: onlyLeftTooltipPositions,
+ defaultPosition: onlyLeftTooltipPositions[0],
+ }),
+ ).toMatch(tooltipPositions.LEFT_TOP),
+ );
+
+ it(
+ 'should return bottom position on left ' +
+ 'to prevent covering element below source',
+ () =>
+ expect(
+ findTooltipPosition({
+ sourcePositionInfo: tooltipSourcePositionCenter,
+ tooltipSize: tooltipSizeBig,
+ availablePositions: onlyLeftTooltipPositions,
+ defaultPosition: onlyLeftTooltipPositions[0],
+ preventDisplayingBelowSource: true,
+ }),
+ ).toMatch(tooltipPositions.LEFT_BOTTOM),
+ );
+
+ it(
+ 'should return first position ' +
+ 'that does not cover element below source ',
+ () =>
+ expect(
+ findTooltipPosition({
+ sourcePositionInfo: tooltipSourcePositionCenter,
+ tooltipSize: tooltipSizeBig,
+ availablePositions: [
+ tooltipPositions.BOTTOM,
+ tooltipPositions.RIGHT,
+ tooltipPositions.RIGHT_TOP,
+ tooltipPositions.LEFT,
+ tooltipPositions.LEFT_TOP,
+ tooltipPositions.TOP,
+ tooltipPositions.LEFT_BOTTOM,
+ ],
+ defaultPosition: tooltipPositions.BOTTOM,
+ preventDisplayingBelowSource: true,
+ }),
+ ).toMatch(tooltipPositions.TOP),
+ );
+
+ it(
+ 'should return default position ' +
+ 'if an empty array of available is provided',
+ () =>
+ expect(
+ findTooltipPosition({
+ sourcePositionInfo: tooltipSourcePositionCenter,
+ tooltipSize: tooltipSizeSmall,
+ availablePositions: [],
+ defaultPosition: tooltipPositions.LEFT_BOTTOM,
+ }),
+ ).toMatch(tooltipPositions.LEFT_BOTTOM),
+ );
+
+ it('should return default position if an no position is available', () =>
+ expect(
+ findTooltipPosition({
+ sourcePositionInfo: tooltipSourcePositionTopRight,
+ tooltipSize: tooltipSizeBig,
+ availablePositions: allTooltipPositions,
+ defaultPosition: tooltipPositions.BOTTOM,
+ preventDisplayingBelowSource: true,
+ }),
+ ).toMatch(tooltipPositions.BOTTOM));
+});

File Metadata

Mime Type
text/plain
Expires
Tue, Nov 26, 6:55 AM (21 h, 18 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2583350
Default Alt Text
D4913.diff (18 KB)

Event Timeline