Skip to content

Commit

Permalink
Refactor, styling
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanahuckova committed Jan 8, 2020
1 parent 4eec863 commit c6554a6
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 144 deletions.
7 changes: 4 additions & 3 deletions packages/grafana-ui/src/components/Chart/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface TooltipContentProps<T extends Dimensions = any> {
// timeZone: TimeZone;
pos: FlotPosition;
mode: TooltipMode;
isContext?: boolean;
}

export interface TooltipProps {
Expand All @@ -43,10 +44,10 @@ export interface TooltipProps {
// - single - display single series info
// - multi - display all series info
mode?: TooltipMode;
className?: any;
isContext?: boolean;
}

export const Tooltip: React.FC<TooltipProps> = ({ content, position, offset, className }) => {
export const Tooltip: React.FC<TooltipProps> = ({ content, position, offset, isContext }) => {
if (position) {
return (
<Portal
Expand All @@ -59,7 +60,7 @@ export const Tooltip: React.FC<TooltipProps> = ({ content, position, offset, cla
height: 100%;
`}
>
<TooltipContainer position={position} offset={offset || { x: 0, y: 0 }} className={className}>
<TooltipContainer position={position} offset={offset || { x: 0, y: 0 }} isContext={isContext}>
{content}
</TooltipContainer>
</Portal>
Expand Down
17 changes: 9 additions & 8 deletions packages/grafana-ui/src/components/Chart/TooltipContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ interface TooltipContainerProps {
position: { x: number; y: number };
offset: { x: number; y: number };
children?: JSX.Element;
className?: any;
isContext?: boolean;
}

const getTooltipContainerStyles = stylesFactory((theme: GrafanaTheme) => {
const bgColor = selectThemeVariant({ light: theme.colors.gray5, dark: theme.colors.dark1 }, theme.type);
const contextBg = selectThemeVariant({ light: theme.colors.white, dark: theme.colors.black }, theme.type);
const boxShadowColor = selectThemeVariant({ light: theme.colors.gray3, dark: theme.colors.black }, theme.type);
return {
wrapper: css`
overflow: hidden;
Expand All @@ -24,10 +26,14 @@ const getTooltipContainerStyles = stylesFactory((theme: GrafanaTheme) => {
padding: ${theme.spacing.sm};
border-radius: ${theme.border.radius.sm};
`,
context: css`
box-shadow: 0 2px 5px 0 ${boxShadowColor};
background: ${contextBg};
`,
};
});

export const TooltipContainer: React.FC<TooltipContainerProps> = ({ position, offset, children, className }) => {
export const TooltipContainer: React.FC<TooltipContainerProps> = ({ position, offset, children, isContext }) => {
const theme = useTheme();
const tooltipRef = useRef<HTMLDivElement>(null);
const { width, height } = useWindowSize();
Expand Down Expand Up @@ -70,12 +76,7 @@ export const TooltipContainer: React.FC<TooltipContainerProps> = ({ position, of
top: 0,
transform: `translate3d(${placement.x}px, ${placement.y}px, 0)`,
}}
className={cx(
styles.wrapper,
css`
${className}
`
)}
className={cx(styles.wrapper, { [styles.context]: isContext })}
>
{children}
</div>
Expand Down
132 changes: 29 additions & 103 deletions packages/grafana-ui/src/components/Graph/Graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import _ from 'lodash';
import { FlotPosition, FlotItem } from './types';
import { TooltipProps, TooltipContentProps, ActiveDimensions, Tooltip } from '../Chart/Tooltip';
import { GraphTooltip } from './GraphTooltip/GraphTooltip';
import { ContextTooltip } from './GraphTooltip/ContextTooltip';
import { GraphDimensions } from './GraphTooltip/types';

export interface GraphProps {
Expand Down Expand Up @@ -118,12 +117,15 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
);
}

renderTooltip = () => {
renderTooltip = (isContextTooltip?: boolean) => {
const { children, series } = this.props;
const { pos, activeItem, isTooltipVisible } = this.state;
const { pos, contextPos, activeItem, contextItem, isTooltipVisible, isContextVisible } = this.state;
let tooltipElement: React.ReactElement<TooltipProps> | null = null;
const isVisible = isContextTooltip ? isContextVisible : isTooltipVisible;
const position = isContextTooltip ? contextPos : pos;
const item = isContextTooltip ? contextItem : activeItem;

if (!isTooltipVisible || !pos || series.length === 0) {
if (!isVisible || !position || series.length === 0) {
return null;
}

Expand All @@ -149,24 +151,24 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
const tooltipMode = tooltipElementProps.mode || 'single';

// If mode is single series and user is not hovering over item, skip rendering
if (!activeItem && tooltipMode === 'single') {
if (!item && tooltipMode === 'single') {
return null;
}

// Check if tooltip needs to be rendered with custom tooltip component, otherwise default to GraphTooltip
const tooltipContentRenderer = tooltipElementProps.tooltipComponent || GraphTooltip;
// Indicates column(field) index in y-axis dimension
const seriesIndex = activeItem ? activeItem.series.seriesIndex : 0;
const seriesIndex = item ? item.series.seriesIndex : 0;
// Indicates row index in active field values
const rowIndex = activeItem ? activeItem.dataIndex : undefined;
const rowIndex = item ? item.dataIndex : undefined;

const activeDimensions: ActiveDimensions<GraphDimensions> = {
// Described x-axis active item
// When hovering over an item - let's take it's dataIndex, otherwise undefined
// Tooltip itself needs to figure out correct datapoint display information based on pos passed to it
xAxis: [seriesIndex, rowIndex],
// Describes y-axis active item
yAxis: activeItem ? [activeItem.series.seriesIndex, activeItem.dataIndex] : null,
yAxis: item ? [item.series.seriesIndex, item.dataIndex] : null,
};

const tooltipContentProps: TooltipContentProps<GraphDimensions> = {
Expand All @@ -182,93 +184,18 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
),
},
activeDimensions,
pos,
pos: position,
mode: tooltipElementProps.mode || 'single',
isContext: isContextTooltip,
};

const tooltipContent = React.createElement(tooltipContentRenderer, { ...tooltipContentProps });

return React.cloneElement<TooltipProps>(tooltipElement as React.ReactElement<TooltipProps>, {
content: tooltipContent,
position: { x: pos.pageX, y: pos.pageY },
position: { x: position.pageX, y: position.pageY },
offset: { x: 10, y: 10 },
});
};

renderContextTooltip = () => {
const { children, series } = this.props;
const { contextPos, contextItem, isContextVisible } = this.state;
let tooltipElement: React.ReactElement<TooltipProps> | null = null;

if (!isContextVisible || !contextPos || series.length === 0) {
return null;
}

// Find children that indicate tooltip to be rendered
React.Children.forEach(children, c => {
// We have already found tooltip
if (tooltipElement) {
return;
}
// @ts-ignore
const childType = c && c.type && (c.type.displayName || c.type.name);

if (childType === Tooltip.displayName) {
tooltipElement = c as React.ReactElement<TooltipProps>;
}
});
// If no tooltip provided, skip rendering
if (!tooltipElement) {
return null;
}
const tooltipElementProps = (tooltipElement as React.ReactElement<TooltipProps>).props;

const tooltipMode = tooltipElementProps.mode || 'single';

// If mode is single series and user is not hovering over item, skip rendering
if (!contextItem && tooltipMode === 'single') {
return null;
}

// Check if tooltip needs to be rendered with custom tooltip component, otherwise default to GraphTooltip
const tooltipContentRenderer = tooltipElementProps.tooltipComponent || ContextTooltip;
// Indicates column(field) index in y-axis dimension
const seriesIndex = contextItem ? contextItem.series.seriesIndex : 0;
// Indicates row index in active field values
const rowIndex = contextItem ? contextItem.dataIndex : undefined;

const activeDimensions: ActiveDimensions<GraphDimensions> = {
// Described x-axis active item
// When hovering over an item - let's take it's dataIndex, otherwise undefined
// Tooltip itself needs to figure out correct datapoint display information based on pos passed to it
xAxis: [seriesIndex, rowIndex],
// Describes y-axis active item
yAxis: contextItem ? [contextItem.series.seriesIndex, contextItem.dataIndex] : null,
};

const tooltipContentProps: TooltipContentProps<GraphDimensions> = {
dimensions: {
// time/value dimension columns are index-aligned - see getGraphSeriesModel
xAxis: createDimension(
'xAxis',
series.map(s => s.timeField)
),
yAxis: createDimension(
'yAxis',
series.map(s => s.valueField)
),
},
activeDimensions,
pos: contextPos,
mode: tooltipElementProps.mode || 'single',
};

const tooltipContent = React.createElement(tooltipContentRenderer, { ...tooltipContentProps });
return React.cloneElement<TooltipProps>(tooltipElement as React.ReactElement<TooltipProps>, {
content: tooltipContent,
position: { x: contextPos.pageX, y: contextPos.pageY },
offset: { x: 15, y: 15 },
className: { backgroundColor: 'white', boxShadow: `0 2px 5px 0 #acb6bf` },
isContext: isContextTooltip,
});
};

Expand Down Expand Up @@ -378,23 +305,22 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
render() {
const { height, width, series } = this.props;
const noDataToBeDisplayed = series.length === 0;
const tooltip = this.renderTooltip();
const context = this.renderTooltip(true);
return (
<>
<div className="graph-panel">
<div
className="graph-panel__chart"
ref={e => (this.element = e)}
style={{ height, width }}
onMouseLeave={() => {
this.setState({ isTooltipVisible: false });
}}
/>

{noDataToBeDisplayed && <div className="datapoints-warning">No data</div>}
{this.renderTooltip()}
</div>
{this.renderContextTooltip()}
</>
<div className="graph-panel">
<div
className="graph-panel__chart"
ref={e => (this.element = e)}
style={{ height, width }}
onMouseLeave={() => {
this.setState({ isTooltipVisible: false });
}}
/>
{noDataToBeDisplayed && <div className="datapoints-warning">No data</div>}
{tooltip}
{context}
</div>
);
}
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
import React from 'react';
import { css, cx } from 'emotion';
import { TooltipContentProps } from '../../Chart/Tooltip';
import { SingleModeGraphTooltip } from './SingleModeGraphTooltip';
import { MultiModeGraphTooltip } from './MultiModeGraphTooltip';
import { GraphDimensions } from './types';
import { stylesFactory } from '../../../themes/stylesFactory';
import { selectThemeVariant } from '../../../themes/selectThemeVariant';
import { useTheme } from '../../../themes/ThemeContext';
import { GrafanaTheme } from '@grafana/data';

const getStyles = stylesFactory((theme: GrafanaTheme) => {
const contextBg = selectThemeVariant({ light: theme.colors.white, dark: theme.colors.black }, theme.type);
return {
context: css`
width: 500px;
background: ${contextBg};
pointer-events: auto;
padding: 5px;
`,
};
});

export const GraphTooltip: React.FC<TooltipContentProps<GraphDimensions>> = ({
mode = 'single',
dimensions,
activeDimensions,
pos,
isContext,
}) => {
// When
// [1] no active dimension or
Expand All @@ -18,10 +36,21 @@ export const GraphTooltip: React.FC<TooltipContentProps<GraphDimensions>> = ({
return null;
}

const theme = useTheme();
const styles = getStyles(theme);

if (mode === 'single') {
return <SingleModeGraphTooltip dimensions={dimensions} activeDimensions={activeDimensions} />;
return (
<div className={cx({ [styles.context]: isContext })}>
<SingleModeGraphTooltip dimensions={dimensions} activeDimensions={activeDimensions} />
</div>
);
} else {
return <MultiModeGraphTooltip dimensions={dimensions} activeDimensions={activeDimensions} pos={pos} />;
return (
<div className={cx({ [styles.context]: isContext })}>
<MultiModeGraphTooltip dimensions={dimensions} activeDimensions={activeDimensions} pos={pos} />
</div>
);
}
};

Expand Down

0 comments on commit c6554a6

Please sign in to comment.