Skip to content

Commit

Permalink
feat: implement custom callback for static legend (#615)
Browse files Browse the repository at this point in the history
  • Loading branch information
TCL735 authored Jun 16, 2021
1 parent 649f4a5 commit 142499b
Show file tree
Hide file tree
Showing 15 changed files with 391 additions and 41 deletions.
17 changes: 17 additions & 0 deletions giraffe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,23 @@ When using the comma separated values (CSV) from the Flux query as the `fluxResp

- **orientationThreshold**: _number. Optional. Defaults to undefined when excluded._ The number of columns in the legend that will determine the direction of columns in the legend. When _undefined_ or when the total number of columns is less than or equal to it, the columns in the tooltip will display horizontally. When the total number of columns is greater, the columns will display vertically.

- **renderEffect**: _function(object). Optional. Defaults to an empty function (no operation) when excluded._ A callback function that executes after each render. The purpose is to allow the consumer to adjust `config` properties based on certain aspects of the static legend. An example would be auto-adjusting the height of the static legend to fit its contents. This function is given an _options_ object as the argument which has the following properties:
- **totalHeight**: _number._ The height of the `<Plot>` including the static legend.

- **staticLegendHeight**: _number._ The height of the static legend.

- **legendDataLength**: _number._ The total number of "fill" columns plus any additional columns added by the static legend to be rendered.

- **lineCount**: _number._ The total number of rows, when in a horizontal orientation, excluding the header row. Or the total number of columns, when in a vertical orientation, excluding the header column.

- **lineSpacingRatio**: _number._ The [relative unit length](https://developer.mozilla.org/en-US/docs/Web/CSS/length) in **em** of the spacing between rows. When in a horizontal orientation, this number in **em** is equal to the spacing. When in a vertical orientation, the spacing is twice this number in **em**.

- **padding**: _number._ The default padding added around all of the content in the static legend. Does not include any padding that is applied by **style** or any other custom styling.

- **headerTextMetrics**: _Object._ An object whose properties are estimates of the width and height in the header row (horizontal orientation) or header column (vertical orientation).

- **sampleTextMetrics**: _Object._ An object whose properties are estimates of the width and height of each entry in an example row (horizontal orientation) or example column (vertical orientation).

- **style**: _Object. Optional._ An object containing the key-value pairs used for inline styling the class `.giraffe-static-legend` by using the [style property](https://developer.mozilla.org/en-US/docs/Web/API/ElementCSSInlineStyle/style). Primarily used for adjusting `margin` and `padding`. May be used to add additional styling to Static Legend, but does not affect the following styles: `backgroundColor`, `border`, `bottom`, `color`, `cursor`, `font`, `height`, `left`, `opacity`, `overflow`, `position`, `right`, `top`, `width`.

- **valueAxis**: _string. Optional. Defaults to 'y' when not included. Valid values are either 'x' or 'y'. This is to set where the 'values' that are displayed in the tooltip come from (which axis, x or y)
Expand Down
2 changes: 1 addition & 1 deletion giraffe/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@influxdata/giraffe",
"version": "2.14.2",
"version": "2.15.0",
"main": "dist/index.js",
"module": "src/index.js",
"license": "MIT",
Expand Down
41 changes: 32 additions & 9 deletions giraffe/src/components/Legend.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import React, {CSSProperties} from 'react'
import {FunctionComponent} from 'react'

import {LegendData, Config} from '../types'
import {Config, LegendData, LegendType} from '../types'
import {generateLegendStyles, LegendPillsStyles} from './LegendStyles'
import {
LEGEND_COLUMN_CLASSNAME,
STATIC_LEGEND_COLUMN_CLASSNAME,
} from '../constants'

interface Props {
type: LegendType
data: LegendData
config: Config
isScrollable?: boolean
}

export const Legend: FunctionComponent<Props> = ({
type: legendType,
data,
config,
isScrollable = false,
Expand Down Expand Up @@ -48,6 +54,7 @@ export const Legend: FunctionComponent<Props> = ({
const maxLength = switchToVertical ? width : height

const styles = generateLegendStyles(
legendType,
isScrollable,
columns,
switchToVertical,
Expand All @@ -62,9 +69,12 @@ export const Legend: FunctionComponent<Props> = ({
style={styles.table}
data-testid="giraffe-legend-table"
>
{!colorizeRows && <LegendPillColumn styles={styles.pills} />}
{!colorizeRows && (
<LegendPillColumn type={legendType} styles={styles.pills} />
)}
{columns.map(({name, values}, i) => (
<LegendColumn
type={legendType}
key={name}
name={name}
maxLength={maxLength}
Expand All @@ -79,6 +89,7 @@ export const Legend: FunctionComponent<Props> = ({
}

interface LegendColumnProps {
type: LegendType
name: string
maxLength: number
values: string[]
Expand All @@ -88,6 +99,7 @@ interface LegendColumnProps {
}

const LegendColumn: FunctionComponent<LegendColumnProps> = ({
type: legendType,
name,
maxLength,
values,
Expand All @@ -96,14 +108,19 @@ const LegendColumn: FunctionComponent<LegendColumnProps> = ({
columnValueStyles,
}) => {
const valuesLimitedByPlotDimensions = values.slice(0, maxLength)
const classNameBase =
legendType === 'static'
? STATIC_LEGEND_COLUMN_CLASSNAME
: LEGEND_COLUMN_CLASSNAME

return (
<div className="giraffe-legend-column" style={columnStyle}>
<div className="giraffe-legend-column-header" style={columnHeaderStyle}>
<div className={classNameBase} style={columnStyle}>
<div className={`${classNameBase}-header`} style={columnHeaderStyle}>
{name}
</div>
{valuesLimitedByPlotDimensions.map((value, i) => (
<div
className="giraffe-legend-column-value"
className={`${classNameBase}-value`}
key={i}
style={columnValueStyles[i]}
>
Expand All @@ -117,22 +134,28 @@ const LegendColumn: FunctionComponent<LegendColumnProps> = ({
LegendColumn.displayName = 'LegendColumn'

interface LegendPillColumnProps {
type: LegendType
styles: LegendPillsStyles
}

const LegendPillColumn: FunctionComponent<LegendPillColumnProps> = ({
type: legendType,
styles,
}) => {
const {column, header, value, pills} = styles
const classNameBase =
legendType === 'static'
? STATIC_LEGEND_COLUMN_CLASSNAME
: LEGEND_COLUMN_CLASSNAME

return (
<div className="giraffe-legend-column" style={column}>
<div className="giraffe-legend-column-header" style={header}>
<div className={classNameBase} style={column}>
<div className={`${classNameBase}-header`} style={header}>
&nbsp;
</div>
{pills.map((pill, i) => (
<div className="giraffe-legend-column-value" key={i} style={value}>
<div className="giraffe-legend-column-pill" style={pill} />
<div className={`${classNameBase}-value`} key={i} style={value}>
<div className={`${classNameBase}-pill`} style={pill} />
</div>
))}
</div>
Expand Down
77 changes: 65 additions & 12 deletions giraffe/src/components/LegendStyles.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import {CSSProperties} from 'react'
import {LegendData, ColumnType} from '../types'
import {ColumnType, LegendData, LegendType} from '../types'

// Style Constants
import {
STATIC_LEGEND_LINE_SPACING_RATIO,
STATIC_LEGEND_SCROLL_PADDING,
} from '../constants'

const legendColumnGap = '12px'
const legendColumnPadding = '15px'
const legendColumnMaxWidth = '200px'
const legendTablePadding = '4px'

Expand Down Expand Up @@ -37,6 +41,7 @@ export interface LegendStyles {
}

export const generateLegendStyles = (
legendType: LegendType,
isScrollable: boolean,
legendData: LegendData,
switchToVertical: boolean,
Expand All @@ -60,13 +65,21 @@ export const generateLegendStyles = (
const headers = legendColumnHeaderStyle(switchToVertical, fontColor)
const values = legendData.map(column => {
return column.values.map((_, i) =>
legendColumnValueStyle(
i,
column.colors,
colorizeRows,
fontBrightColor,
switchToVertical
)
legendType === 'tooltip'
? tooltipColumnValueStyle(
i,
column.colors,
colorizeRows,
fontBrightColor,
switchToVertical
)
: staticLegendColumnValueStyle(
i,
column.colors,
colorizeRows,
fontBrightColor,
switchToVertical
)
)
})

Expand Down Expand Up @@ -119,9 +132,9 @@ const legendColumnStyle = (
}

if (isScrollableColumn) {
columnStyle.paddingBottom = legendColumnPadding
columnStyle.paddingBottom = STATIC_LEGEND_SCROLL_PADDING
if (index === columnCount) {
columnStyle.paddingRight = legendColumnPadding
columnStyle.paddingRight = STATIC_LEGEND_SCROLL_PADDING
}
}
return columnStyle
Expand All @@ -147,7 +160,7 @@ const legendColumnHeaderStyle = (
}
}

const legendColumnValueStyle = (
const tooltipColumnValueStyle = (
i: number,
colors: string[],
colorizeRows: boolean,
Expand Down Expand Up @@ -186,6 +199,46 @@ const legendColumnValueStyle = (
}
}

const staticLegendColumnValueStyle = (
i: number,
colors: string[],
colorizeRows: boolean,
fontBrightColor: string,
switchToVertical: boolean
): React.CSSProperties => {
let color = fontBrightColor

if (colorizeRows && colors) {
color = colors[i]
}

if (switchToVertical) {
return {
maxWidth: legendColumnMaxWidth,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
color,
display: 'table-cell',
padding: `${2 * STATIC_LEGEND_LINE_SPACING_RATIO}em`,
fontWeight: 600,
lineHeight: '1em',
}
}

return {
maxWidth: legendColumnMaxWidth,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
fontWeight: 600,
padding: `${STATIC_LEGEND_LINE_SPACING_RATIO}em`,
lineHeight: '1.25em',
height: '1.25em',
color,
}
}

const LegendPillsColumnStyles = (
switchToVertical: boolean,
legendData: LegendData
Expand Down
48 changes: 38 additions & 10 deletions giraffe/src/components/StaticLegendBox.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import React, {FunctionComponent} from 'react'
import React, {FunctionComponent, useEffect, useMemo} from 'react'
import {
Formatter,
LayerSpec,
LegendPropertyNames,
SizedConfig,
StaticLegend,
} from '../types'
import {CONFIG_DEFAULTS, STATIC_LEGEND_DEFAULTS} from '../constants/index'
import {
CONFIG_DEFAULTS,
STATIC_LEGEND_DEFAULTS,
STATIC_LEGEND_BOX_PADDING,
STATIC_LEGEND_LINE_SPACING_RATIO,
STATIC_LEGEND_SCROLL_PADDING,
} from '../constants/index'
import {Legend} from './Legend'
import {DapperScrollbars} from './DapperScrollbars'
import {getLegendData} from '../utils/legend/staticLegend'
import {getStaticLegendTexMetrics} from '../utils/textMetrics'

interface StaticLegendBoxProps extends StaticLegend {
config: SizedConfig
Expand Down Expand Up @@ -48,7 +55,10 @@ export const StaticLegendBox: FunctionComponent<StaticLegendBoxProps> = props =>
const {
config: configOverride,
staticLegend: staticLegendOverride,
} = overrideLegendConfig(config, staticLegend)
} = useMemo(() => overrideLegendConfig(config, staticLegend), [
config,
staticLegend,
])

const {style = {}} = staticLegendOverride

Expand All @@ -62,18 +72,31 @@ export const StaticLegendBox: FunctionComponent<StaticLegendBoxProps> = props =>

const layerConfig = configOverride.layers[staticLegendOverride.layer]
const valueColumnKey = layerConfig[staticLegendOverride.valueAxis]
const legendData = getLegendData(
layerConfig.type,
spec,
valueColumnKey,
columnFormatter
const legendData = useMemo(
() =>
getLegendData(layerConfig.type, spec, valueColumnKey, columnFormatter),
[layerConfig.type, spec, valueColumnKey, columnFormatter]
)

useEffect(() => {
const {headerTextMetrics, sampleTextMetrics} = getStaticLegendTexMetrics()
staticLegendOverride.renderEffect({
totalHeight: height + top,
staticLegendHeight: height,
legendDataLength: legendData.length,
lineCount: legendData.length ? legendData[0].values.length : 0,
lineSpacingRatio: STATIC_LEGEND_LINE_SPACING_RATIO,
padding: 2 * STATIC_LEGEND_BOX_PADDING + STATIC_LEGEND_SCROLL_PADDING,
headerTextMetrics,
sampleTextMetrics,
})
}, [legendData, staticLegendOverride])

return (
<div
className="giraffe-static-legend"
style={{
padding: 10, // overridable, must be at the top
padding: STATIC_LEGEND_BOX_PADDING, // overridable, must be at the top
...style,
backgroundColor,
border,
Expand All @@ -94,7 +117,12 @@ export const StaticLegendBox: FunctionComponent<StaticLegendBoxProps> = props =>
data-testid="giraffe-static-legend"
>
<DapperScrollbars removeTracksWhenNotUsed={true} autoHide={true}>
<Legend data={legendData} config={configOverride} isScrollable={true} />
<Legend
type="static"
data={legendData}
config={configOverride}
isScrollable={true}
/>
</DapperScrollbars>
</div>
)
Expand Down
2 changes: 1 addition & 1 deletion giraffe/src/components/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const Tooltip: FunctionComponent<Props> = ({data, config}) => {
return null
}

const tooltipContents = <Legend data={data} config={config} />
const tooltipContents = <Legend type="tooltip" data={data} config={config} />

return createPortal(
<div
Expand Down
8 changes: 8 additions & 0 deletions giraffe/src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export const STATIC_LEGEND_DEFAULTS: Partial<StaticLegend> = {
cursor: 'auto',
heightRatio: 0.2,
layer: 0,
renderEffect: () => {},
valueAxis: 'y',
widthRatio: 1.0,
}
Expand Down Expand Up @@ -142,6 +143,13 @@ export const STATIC_LEGEND_MINIMUM_HEIGHT_RATIO = 0
export const STATIC_LEGEND_MAXIMUM_WIDTH_RATIO = 1.0
export const STATIC_LEGEND_MINIMUM_WIDTH_RATIO = 0

export const STATIC_LEGEND_LINE_SPACING_RATIO = 1 / 6
export const STATIC_LEGEND_BOX_PADDING = 10
export const STATIC_LEGEND_SCROLL_PADDING = 15

export const STATIC_LEGEND_COLUMN_CLASSNAME = 'giraffe-static-legend-column'
export const LEGEND_COLUMN_CLASSNAME = 'giraffe-legend-column'

export const TOOLTIP_MINIMUM_OPACITY = 0
export const TOOLTIP_MAXIMUM_OPACITY = 1.0

Expand Down
Loading

0 comments on commit 142499b

Please sign in to comment.