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

DH-11923: Fix column tooltip when above the table #306

Merged
merged 5 commits into from
Nov 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/code-studio/src/styleguide/MockIrisGridTreeModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ class MockIrisGridTreeModel extends IrisGridModel {

set pendingDataMap(value) {}

getColumnIndexByName(name) {
return Number(name);
}

textForCell(column, row) {
return (
this.editedData[column]?.[row] ?? this.model.textForCell(column, row)
Expand Down
11 changes: 7 additions & 4 deletions packages/components/src/popper/Popper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import classNames from 'classnames';
import { CSSTransition } from 'react-transition-group';
import PopperJs, { PopperOptions } from 'popper.js';
import PopperJs, { PopperOptions, ReferenceObject } from 'popper.js';
import PropTypes from 'prop-types';
import ThemeExport from '../ThemeExport';
import './Popper.scss';
Expand All @@ -33,6 +33,7 @@ interface PopperProps {
isShown: boolean;
closeOnBlur: boolean;
interactive: boolean;
referenceObject: ReferenceObject;
}

interface PopperState {
Expand All @@ -51,6 +52,7 @@ class Popper extends Component<PopperProps, PopperState> {
isShown: PropTypes.bool,
closeOnBlur: PropTypes.bool,
interactive: PropTypes.bool,
referenceObject: PropTypes.shape({}),
};

static defaultProps = {
Expand All @@ -66,6 +68,7 @@ class Popper extends Component<PopperProps, PopperState> {
isShown: false,
interactive: false,
closeOnBlur: false,
referenceObject: null,
};

constructor(props: PopperProps) {
Expand Down Expand Up @@ -136,7 +139,7 @@ class Popper extends Component<PopperProps, PopperState> {

initPopper(): void {
let { popper } = this.state;
const { closeOnBlur } = this.props;
const { closeOnBlur, referenceObject } = this.props;

if (popper) {
return;
Expand All @@ -158,7 +161,7 @@ class Popper extends Component<PopperProps, PopperState> {
parent = this.container.current;
}

popper = new PopperJs(parent, this.element, options);
popper = new PopperJs(referenceObject || parent, this.element, options);
popper.scheduleUpdate();

// delayed due to scheduleUpdate
Expand Down Expand Up @@ -293,4 +296,4 @@ class Popper extends Component<PopperProps, PopperState> {
}

export default Popper;
export type { PopperOptions };
export type { PopperOptions, ReferenceObject };
13 changes: 11 additions & 2 deletions packages/components/src/popper/Tooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { Component } from 'react';
import classNames from 'classnames';
import Log from '@deephaven/log';
import Popper, { PopperOptions } from './Popper';
import Popper, { PopperOptions, ReferenceObject } from './Popper';

const log = Log.module('Tooltip');

Expand All @@ -12,6 +12,7 @@ interface TooltipProps {
reshowTimeout: number;
children: React.ReactNode;
popperClassName: string;
referenceObject: ReferenceObject;
}

interface TooltipState {
Expand Down Expand Up @@ -47,6 +48,7 @@ class Tooltip extends Component<TooltipProps, TooltipState> {
popperClassName: '',
reshowTimeout: Tooltip.defaultReshowTimeout,
timeout: Tooltip.defaultTimeout,
referenceObject: null,
};

static handleHidden(): void {
Expand Down Expand Up @@ -280,7 +282,13 @@ class Tooltip extends Component<TooltipProps, TooltipState> {
}

render(): JSX.Element {
const { interactive, children, options, popperClassName } = this.props;
const {
interactive,
children,
options,
referenceObject,
popperClassName,
} = this.props;
const { isShown } = this.state;
return (
<div ref={this.container} style={{ display: 'none' }}>
Expand All @@ -290,6 +298,7 @@ class Tooltip extends Component<TooltipProps, TooltipState> {
ref={this.popper}
onExited={this.handleExited}
interactive={interactive}
referenceObject={referenceObject}
>
<div className="tooltip-content"> {isShown && children}</div>
</Popper>
Expand Down
1 change: 1 addition & 0 deletions packages/iris-grid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"@fortawesome/react-fontawesome": "^0.1.12",
"classnames": "^2.3.1",
"deep-equal": "^2.0.4",
"lodash.clamp": "^4.0.3",
"lodash.debounce": "^4.0.8",
"lodash.throttle": "^4.1.1",
"memoize-one": "^5.1.1",
Expand Down
65 changes: 62 additions & 3 deletions packages/iris-grid/src/IrisGrid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import dh, { PropTypes as APIPropTypes } from '@deephaven/jsapi-shim';
import { Pending, PromiseUtils, ValidationError } from '@deephaven/utils';
import throttle from 'lodash.throttle';
import debounce from 'lodash.debounce';
import clamp from 'lodash.clamp';
import PendingDataBottomBar from './PendingDataBottomBar';
import IrisGridCopyHandler from './IrisGridCopyHandler';
import FilterInputField from './FilterInputField';
Expand Down Expand Up @@ -2421,23 +2422,80 @@ export class IrisGrid extends Component {
}

let columnTooltip = null;
if (shownColumnTooltip != null && metrics) {
if (shownColumnTooltip != null && metrics && this.gridWrapper) {
const {
columnHeaderHeight,
visibleColumnXs,
visibleColumnWidths,
width,
} = metrics;
const columnX = visibleColumnXs.get(shownColumnTooltip);
const columnWidth = visibleColumnWidths.get(shownColumnTooltip);

/**
* Create a wrapper dom element, the size of the column header.
* The wrapper acts as tooltip parent, the tooltip component
* will trigger hide on mouseleave of wrapper or tooltip.
* The wrapper should be bound to within the grid dimensions,
* so popper only remains triggered while mouse is inside the panel.
*/
const boundedLeft = Math.max(0, columnX);
let boundedWidth = columnWidth;
if (columnX + columnWidth > width) {
// column is extending past right edge
boundedWidth = width - columnX;
} else if (columnX < 0) {
// column is extending past left edge
boundedWidth = columnWidth - Math.abs(columnX);
}

const wrapperStyle = {
position: 'absolute',
top: 0,
left: columnX,
width: columnWidth,
left: boundedLeft,
width: boundedWidth,
height: columnHeaderHeight,
pointerEvents: 'none',
};

/**
* Because the popper parent wrapper center is no longer the same as
* the column label center, we create a popper virtual ref, to handle
* positioning and keep the popper centered on the label. Creates a
* 1px x headerHeight virtual object, placed centered on the column
* label, clamped to 0 + margin to width - margin. We add a margin,
* otherwise the arrow wants to escape the boundary.
*/
const gridRect = this.gridWrapper.getBoundingClientRect();
const popperMargin = 20;
const virtualReference = {
clientWidth: 1,
clientHeight: columnHeaderHeight,
getBoundingClientRect() {
return {
top: gridRect.top,
left:
gridRect.left +
clamp(
columnX + columnWidth / 2,
popperMargin,
width - popperMargin
),
bottom: gridRect.top + columnHeaderHeight,
right:
gridRect.left +
clamp(
columnX + columnWidth / 2,
popperMargin,
width - popperMargin
) +
1,
width: 1,
height: columnHeaderHeight,
};
},
};

const popperOptions = {
placement: 'bottom',
modifiers: {
Expand All @@ -2459,6 +2517,7 @@ export class IrisGrid extends Component {
interactive
options={popperOptions}
ref={this.handleTooltipRef}
referenceObject={virtualReference}
>
<ColumnStatistics
model={model}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ class IrisGridColumnTooltipMouseHandler extends GridMouseHandler {
return false;
}

onWheel() {
this.destroyColumnTooltip();
}

onMove(gridPoint) {
const { y, column, row } = gridPoint;
const { shownColumnTooltip } = this.irisGrid.state;
Expand All @@ -58,14 +62,6 @@ class IrisGridColumnTooltipMouseHandler extends GridMouseHandler {

return false;
}

onLeave(gridPoint) {
const { y } = gridPoint;
if (y < 0) {
this.destroyColumnTooltip();
}
return false;
}
}

export default IrisGridColumnTooltipMouseHandler;