diff --git a/.playground/index.html b/.playground/index.html index 88ed506a55..653990094a 100644 --- a/.playground/index.html +++ b/.playground/index.html @@ -20,6 +20,11 @@ bottom: 100px; right: 100px; } + .chart { + position: relative; + width: 100%; + height: 50%; + } diff --git a/.playground/index.tsx b/.playground/index.tsx index 6b61630233..4a2edacb17 100644 --- a/.playground/index.tsx +++ b/.playground/index.tsx @@ -1,8 +1,7 @@ import '@babel/polyfill'; -import '@elastic/eui/dist/eui_theme_light.css'; import React from 'react'; import ReactDOM from 'react-dom'; -import '../src/index.scss'; +import '../src/theme_light.scss'; import { Playground } from './playgroud'; ReactDOM.render(, document.getElementById('root') as HTMLElement); diff --git a/.playground/playgroud.tsx b/.playground/playgroud.tsx index 84646e614c..65934cfccd 100644 --- a/.playground/playgroud.tsx +++ b/.playground/playgroud.tsx @@ -16,61 +16,71 @@ import { KIBANA_METRICS } from '../src/lib/series/utils/test_dataset_kibana'; export class Playground extends React.Component { render() { return ( - - - - d.toFixed(2)} - /> - - - - - - + <> + {this.renderChart(Position.Right)} + {this.renderChart(Position.Bottom)} + + ); + } + renderChart(legendPosition: Position) { + return ( +
+ + + + d.toFixed(2)} + /> + + + + + + +
); } } diff --git a/.playground/webpack.config.js b/.playground/webpack.config.js index 3e67272542..3fbf7b4f6e 100644 --- a/.playground/webpack.config.js +++ b/.playground/webpack.config.js @@ -1,6 +1,6 @@ -const path = require('path'); module.exports = { entry: './index.tsx', + mode: 'development', output: { filename: 'bundle.js', path: __dirname, diff --git a/.storybook/config.ts b/.storybook/config.ts index af8e9664b3..f9656bcc9f 100644 --- a/.storybook/config.ts +++ b/.storybook/config.ts @@ -2,11 +2,9 @@ import { withInfo } from '@storybook/addon-info'; import { withKnobs } from '@storybook/addon-knobs'; import { withOptions } from '@storybook/addon-options'; import { addDecorator, configure } from '@storybook/react'; -import '../src/index.scss'; -import './style.scss'; import { switchTheme } from './theme_service'; - switchTheme('light'); +import './style.scss'; addDecorator( withOptions({ diff --git a/.storybook/style.scss b/.storybook/style.scss index 7a0eccb27e..f7df75d346 100644 --- a/.storybook/style.scss +++ b/.storybook/style.scss @@ -1,10 +1,10 @@ -@import '../node_modules/@elastic/eui/src/themes/eui/eui_colors_dark.scss'; - .story-chart { + box-sizing: border-box; background: white; } .story-chart-dark { - background: $euiColorEmptyShade; + box-sizing: border-box; + background: #1a1b20; } #root { background-color: blanchedalmond; @@ -14,6 +14,7 @@ width: 100%; height: 400px; position: relative; + box-sizing: border-box; } #story-root + div table { border: 1px solid gray; diff --git a/.storybook/theme_service.ts b/.storybook/theme_service.ts index 4c4af2dd84..dca20473b3 100644 --- a/.storybook/theme_service.ts +++ b/.storybook/theme_service.ts @@ -1,8 +1,8 @@ /* tslint:disable */ // @ts-ignore -import themeDark from '!!style-loader/useable?{attrs:{"nonce":"Pk1rZ1XDlMuYe8ubWV3Lh0BzwrTigJQ="}}!css-loader!@elastic/eui/dist/eui_theme_dark.css'; +import themeDark from '!!style-loader/useable?{attrs:{"nonce":"Pk1rZ1XDlMuYe8ubWV3Lh0BzwrTigJQ="}}!css-loader!sass-loader!../src/theme_dark.scss'; // @ts-ignore -import themeLight from '!!style-loader/useable?{attrs:{"nonce":"Pk1rZ1XDlMuYe8ubWV3Lh0BzwrTigJQ="}}!css-loader!@elastic/eui/dist/eui_theme_light.css'; +import themeLight from '!!style-loader/useable?{attrs:{"nonce":"Pk1rZ1XDlMuYe8ubWV3Lh0BzwrTigJQ="}}!css-loader!sass-loader!../src/theme_light.scss'; export function switchTheme(theme: string) { switch (theme) { diff --git a/README.md b/README.md index f838689836..6c320d9e5d 100644 --- a/README.md +++ b/README.md @@ -25,12 +25,6 @@ To install the Elastic Charts into an existing project, use the `yarn` CLI (`npm yarn add @elastic/charts ``` -Note that Elastic Charts itself has some dependencies itself mostly around styles and management of dates and times. If you are installing it into a blank project you will need to install the following with it. You can read more about other ways to [consume Elastic Charts][consuming]. - -``` -yarn add @elastic/eui @elastic/datemath moment -``` - ## Running Locally ### Node diff --git a/package.json b/package.json index 4bb0b88b86..977eda820d 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "cz": "git-cz", "build:clean": "rm -rf ./dist", "build:ts": "tsc -p ./tsconfig.json", - "build:sass": "node-sass src/index.scss dist/style.css --output-style compressed", + "build:sass": "node-sass src/theme_light.scss dist/theme_light.css --output-style compressed && node-sass src/theme_dark.scss dist/theme_dark.css --output-style compressed && node-sass src/theme_only_light.scss dist/theme_only_light.css --output-style compressed && node-sass src/theme_only_dark.scss dist/theme_only_dark.css --output-style compressed", "build": "yarn build:clean && yarn build:ts && yarn build:sass", "start": "yarn storybook", "storybook": "start-storybook -p 9001 -c .storybook", @@ -50,8 +50,7 @@ "@babel/polyfill": "^7.4.4", "@commitlint/cli": "^7.5.2", "@commitlint/config-conventional": "^7.5.0", - "@elastic/datemath": "^5.0.2", - "@elastic/eui": "10.4.1", + "@elastic/eui": "^11.2.1", "@semantic-release/changelog": "^3.0.2", "@semantic-release/commit-analyzer": "^6.1.0", "@semantic-release/git": "^7.0.8", @@ -80,6 +79,7 @@ "@types/storybook__addon-knobs": "^4.0.1", "@types/storybook__addon-options": "^4.0.1", "@types/storybook__react": "^4.0.0", + "@types/uuid": "^3.4.4", "babel-loader": "^8.0.5", "canvas": "^2.4.1", "commitizen": "^3.0.7", @@ -91,7 +91,6 @@ "husky": "^1.3.1", "jest": "^24.1.0", "jest-environment-jsdom-fourteen": "^0.1.0", - "moment": "^2.24.0", "node-sass": "^4.11.0", "prettier": "1.16.4", "prettier-tslint": "^0.4.2", @@ -119,7 +118,6 @@ "d3-shape": "^1.3.4", "fp-ts": "^1.14.2", "konva": "^2.6.0", - "lodash": "^4.17.11", "luxon": "^1.11.3", "mobx": "^4.9.2", "mobx-react": "^5.4.3", @@ -130,12 +128,8 @@ "react-konva": "16.8.3", "react-spring": "^8.0.8", "resize-observer-polyfill": "^1.5.1", - "ts-debounce": "^1.0.0" - }, - "peerDependencies": { - "@elastic/datemath": "^5.0.2", - "@elastic/eui": "10.4.1", - "moment": "^2.20.1" + "ts-debounce": "^1.0.0", + "uuid": "^3.3.2" }, "config": { "commitizen": { diff --git a/src/components/_annotation.scss b/src/components/_annotation.scss index dccafde628..fb52e79b40 100644 --- a/src/components/_annotation.scss +++ b/src/components/_annotation.scss @@ -1,48 +1,31 @@ -.elasticChartsAnnotation { - @include euiFontSizeXS; +.echAnnotation { pointer-events: none; position: absolute; - z-index: $euiZLevel9; - max-width: $euiSizeXL * 10; - overflow: hidden; - overflow-wrap: break-word; - transition: opacity $euiAnimSpeedNormal; user-select: none; } -.elasticChartsAnnotation--hidden, .elasticChartsAnnotation__tooltip--hidden { +.echAnnotation--hidden, +.echAnnotation__tooltip--hidden { opacity: 0; } -.elasticChartsAnnotation__tooltip { - @include euiBottomShadow($color: $euiColorFullShade); +.echAnnotation__tooltip { + @include euiToolTipStyle; @include euiFontSizeXS; - pointer-events: none; position: absolute; - z-index: $euiZLevel9; - background-color: rgba(tintOrShade($euiColorFullShade, 25%, 80%), 0.9); - color: $euiColorGhost; - border-radius: $euiBorderRadius; - max-width: $euiSizeXL * 10; - overflow: hidden; - overflow-wrap: break-word; + padding: 0; + + // overflow: hidden; transition: opacity $euiAnimSpeedNormal; + pointer-events: none; user-select: none; } -.elasticChartsAnnotation__header { - margin: 0; - background: rgba(shade($euiColorGhost, 20%), 0.9); - color: $euiColorFullShade; - padding: 0 8px; +.echAnnotation__header { + @include euiToolTipTitle; + padding: $euiSizeXS ($euiSizeXS * 2); } -.elasticChartsAnnotation__details { - margin: 0; - padding: 0 8px; - display: flex; +.echAnnotation__details { + padding: $euiSizeXS ($euiSizeXS * 2); } - -.elasticChartsAnnotation__detailsMarker { - margin-right: 4px; -} \ No newline at end of file diff --git a/src/components/_chart.scss b/src/components/_chart.scss deleted file mode 100644 index ff18c64763..0000000000 --- a/src/components/_chart.scss +++ /dev/null @@ -1,5 +0,0 @@ -@import 'legend'; -@import 'tooltip'; -@import 'crosshair'; -@import 'highlighter'; -@import 'annotation'; diff --git a/src/components/_container.scss b/src/components/_container.scss new file mode 100644 index 0000000000..461dc1e198 --- /dev/null +++ b/src/components/_container.scss @@ -0,0 +1,15 @@ +/** + * The Base Elastic-Charts container + */ + +.echContainer { + position: relative; + width: 100%; + height: 100%; + + &:hover { + .echLegend__toggle { + opacity: 1; + } + } +} diff --git a/src/components/_crosshair.scss b/src/components/_crosshair.scss index 911ee22706..8db366ab0d 100644 --- a/src/components/_crosshair.scss +++ b/src/components/_crosshair.scss @@ -1,16 +1,11 @@ -.elasticChartsCrosshair { +.echCrosshair, +.echCrosshair__band, +.echCrosshair__line { position: absolute; pointer-events: none; } -.elasticChartsCrosshair__band { - position: absolute; - pointer-events: none; -} -.elasticChartsCrosshair__line { - position: absolute; - pointer-events: none; +.echCrosshair__line { z-index: $euiZLevel8; - background: 'transparent'; } diff --git a/src/components/_highlighter.scss b/src/components/_highlighter.scss index 49b67c2d45..5e22daeb00 100644 --- a/src/components/_highlighter.scss +++ b/src/components/_highlighter.scss @@ -1,4 +1,4 @@ -.elasticChartsHighlighter { +.echHighlighter { position: absolute; z-index: 1000; pointer-events: none; diff --git a/src/components/_index.scss b/src/components/_index.scss new file mode 100644 index 0000000000..1bc23697fa --- /dev/null +++ b/src/components/_index.scss @@ -0,0 +1,11 @@ +@import '../../node_modules/@elastic/eui/src/components/tool_tip/variables'; +@import '../../node_modules/@elastic/eui/src/components/tool_tip/mixins'; + +@import 'container'; +@import 'annotation'; +@import 'crosshair'; +@import 'highlighter'; +@import 'tooltip'; + +@import 'icons/index'; +@import 'legend/index'; diff --git a/src/components/_legend.scss b/src/components/_legend.scss deleted file mode 100644 index d4c440b5be..0000000000 --- a/src/components/_legend.scss +++ /dev/null @@ -1,160 +0,0 @@ -$elasticChartsLegendMaxWidth: $euiSize * 10; -$elasticChartsLegendMaxHeight: $euiSize * 4; - -.elasticChartsLegend { - position: absolute; - display: flex; - align-items: flex-start; - flex-direction: row; -} -.elasticChartsLegend--collapsed { - width: $euiSize * 2 !important; - height: $euiSize * 2 !important; - .elasticChartsLegendList { - display: none; - } -} -.elasticChartsLegend--debug { - background: red; -} -.elasticChartsLegend--top { - top: 0; - left: 0; - right: 0; - width: 100%; - height: $elasticChartsLegendMaxHeight; - flex-direction: column; - order: 1; - .elasticChartsLegendList__item { - min-height: $elasticChartsLegendMaxHeight / 2 - 6px; - height: $elasticChartsLegendMaxHeight / 2 - 6px; - width: $elasticChartsLegendMaxWidth; - min-width: $elasticChartsLegendMaxWidth; - } -} -.elasticChartsLegend--bottom { - bottom: 0; - left: 0; - right: 0; - width: 100%; - height: $elasticChartsLegendMaxHeight; - flex-direction: column; - .elasticChartsLegendList__item { - min-height: $elasticChartsLegendMaxHeight / 2 - 6px; - height: $elasticChartsLegendMaxHeight / 2 - 6px; - width: $elasticChartsLegendMaxWidth; - min-width: $elasticChartsLegendMaxWidth; - } -} -.elasticChartsLegend--left { - top: 0; - bottom: 0; - left: 0; - width: $elasticChartsLegendMaxWidth; - order: 1; - .elasticChartsLegendList__item { - min-height: $euiSize * 2; - height: $euiSize * 2; - min-width: 100%; - width: 100%; - } -} -.elasticChartsLegend--right { - top: 0; - bottom: 0; - right: 0; - width: $elasticChartsLegendMaxWidth; - .elasticChartsLegendList__item { - min-height: $euiSize * 2; - height: $euiSize * 2; - min-width: 100%; - width: 100%; - } -} - -.elasticChartsLegendCollapser { - width: 2 * $euiSize; - height: 2 * $euiSize; - flex-shrink: 0; - flex-grow: 0; -} -.elasticChartsLegendCollapser--top { - order: 2; -} -.elasticChartsLegendCollapser--left { - order: 2; -} - -.elasticChartsLegendListContainer { - overflow: hidden; - flex-shrink: 1; - flex-grow: 0; - max-width: 100%; -} -.elasticChartsLegendList { - overflow-y: auto; - overflow-x: hidden; - height: 100%; - max-width: 100%; - width: 100%; - @include euiScrollBar; -} - -.elasticChartsLegendList__item { - cursor: pointer; - height: 24px; - width: $elasticChartsLegendMaxWidth; - min-width: $elasticChartsLegendMaxWidth; - &:hover { - .elasticChartsLegendListItem__title { - text-decoration: underline; - } - } - - &.elasticChartsLegendList__item--hidden { - display: none; - } -} - -.elasticChartsLegendListItem__title { - width: $elasticChartsLegendMaxWidth - 4 * $euiSize; - max-width: $elasticChartsLegendMaxWidth - 4 * $euiSize; - - &.elasticChartsLegendListItem__title--selected { - text-decoration: underline; - } - - &.elasticChartsLegendListItem__title--hasDisplayValue { - width: $elasticChartsLegendMaxWidth - 6 * $euiSize; - max-width: $elasticChartsLegendMaxWidth - 6 * $euiSize; - } -} - -.elasticChartsLegendListItem__displayValue { - text-align: right; - - &.elasticChartsLegendListItem__displayValue--hidden { - display: none; - } -} - -.elasticChartsLegend__toggle { - border-radius: $euiBorderRadius; - border-bottom-right-radius: 0; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - position: absolute; - bottom: 0; - left: 0; - opacity: 0; - background-color: $euiColorEmptyShade; - transition: opacity $euiAnimSpeedFast $euiAnimSlightResistance, - background-color $euiAnimSpeedFast $euiAnimSlightResistance $euiAnimSpeedExtraSlow; - &:focus { - box-shadow: none; - background-color: $euiFocusBackgroundColor !important; - } -} -.elasticChartsLegend__toggle--isOpen { - background-color: transparentize($euiColorDarkestShade, 0.9); -} diff --git a/src/components/_tooltip.scss b/src/components/_tooltip.scss index 1f94e21021..6699c07959 100644 --- a/src/components/_tooltip.scss +++ b/src/components/_tooltip.scss @@ -1,18 +1,15 @@ -.elasticChartsTooltip { - @include euiBottomShadow($color: $euiColorFullShade); +.echTooltip { + @include euiToolTipStyle; @include euiFontSizeXS; - pointer-events: none; position: absolute; - z-index: $euiZLevel9; - background-color: rgba(tintOrShade($euiColorFullShade, 25%, 80%), 0.9); - color: $euiColorGhost; - border-radius: $euiBorderRadius; - max-width: $euiSizeXL * 10; - overflow: hidden; - overflow-wrap: break-word; + padding: 0; transition: opacity $euiAnimSpeedNormal; + pointer-events: none; user-select: none; + table { + border-collapse: collapse; + border-spacing: 0; td, th { padding: 3px; @@ -22,29 +19,33 @@ width: 100%; } } -.elasticChartsTooltip__label { - font-weight: $euiFontWeightMedium; - color: shade($euiColorGhost, 20%); - border-left: 3px solid red; + +.echTooltip__header { + @include euiToolTipTitle; + padding: $euiSizeXS ($euiSizeXS * 2); } -.elasticChartsTooltip__rowHighlighted { - background-color: shade($euiColorGhost, 20%); - color: shade($euiColorGhost, 90%); - .elasticChartsTooltip__label { - color: shade($euiColorGhost, 90%); - } + +.echTooltip__label { + @include euiTextTruncate; + // Border indicates series color + border-left: $euiSizeXS solid transparent; } -.elasticChartsTooltip--hidden { - opacity: 0; +.echTooltip__value { + font-weight: $euiFontWeightBold; + text-align: right; + font-feature-settings: 'tnum'; +} + +.echTooltip__rowHighlighted { + background-color: transparentize($euiColorGhost, .9); + border-radius: $euiBorderRadius; } -.elasticChartsTooltip__header { - margin: 0; - background: rgba(shade($euiColorGhost, 20%), 0.9); - color: $euiColorFullShade; - padding: 0 8px; +.echTooltip--hidden { + opacity: 0; } -.elasticChartsTooltip__table { - margin: 4px; + +.echTooltip__table { + margin: $euiSizeXS; } diff --git a/src/components/annotation_tooltips.tsx b/src/components/annotation_tooltips.tsx index 0bee4f58d9..0c96b85af1 100644 --- a/src/components/annotation_tooltips.tsx +++ b/src/components/annotation_tooltips.tsx @@ -17,7 +17,7 @@ class AnnotationTooltipComponent extends React.Component const tooltipState = annotationTooltipState.get(); if (!tooltipState || !tooltipState.isVisible) { - return
; + return
; } const { transform, details, header } = tooltipState; @@ -73,7 +73,7 @@ class AnnotationTooltipComponent extends React.Component }; const markerElement = ( -
+
{icon}
); @@ -122,9 +122,9 @@ function RectAnnotationTooltip(props: { }) { const { details, position } = props; return ( -
-
-
+
+
+
{details}
@@ -139,9 +139,9 @@ function LineAnnotationTooltip(props: { }) { const { details, position, header } = props; return ( -
-

{header}

-
+
+

{header}

+
{details}
diff --git a/src/components/chart.tsx b/src/components/chart.tsx index 77013d1e49..3e2041d4cb 100644 --- a/src/components/chart.tsx +++ b/src/components/chart.tsx @@ -3,14 +3,15 @@ import { Provider } from 'mobx-react'; import React, { CSSProperties, Fragment } from 'react'; import { SpecsParser } from '../specs/specs_parser'; import { ChartStore } from '../state/chart_state'; +import { htmlIdGenerator } from '../utils/utils'; import { AnnotationTooltip } from './annotation_tooltips'; import { ChartResizer } from './chart_resizer'; import { Crosshair } from './crosshair'; import { Highlighter } from './highlighter'; -import { Legend } from './legend'; -import { LegendButton } from './legend_button'; +import { Legend } from './legend/legend'; +import { LegendButton } from './legend/legend_button'; import { ReactiveChart as ReactChart } from './react_canvas/reactive_chart'; -import { ReactiveChart as SVGChart } from './svg/reactive_chart'; +// import { ReactiveChart as SVGChart } from './svg/reactive_chart'; import { Tooltips } from './tooltips'; interface ChartProps { @@ -27,9 +28,11 @@ export class Chart extends React.Component { renderer: 'canvas', }; private chartSpecStore: ChartStore; + private legendId: string; constructor(props: any) { super(props); this.chartSpecStore = new ChartStore(); + this.legendId = htmlIdGenerator()('legend'); } render() { const { renderer, size, className } = this.props; @@ -43,7 +46,7 @@ export class Chart extends React.Component { } else { containerStyle = {}; } - const chartClass = classNames('elasticCharts', className); + const chartClass = classNames('echContainer', className); return ( @@ -51,12 +54,13 @@ export class Chart extends React.Component {
- {renderer === 'svg' && } + {// TODO reenable when SVG rendered is aligned with canvas one + renderer === 'svg' && } {renderer === 'canvas' && } - - + +
diff --git a/src/components/crosshair.tsx b/src/components/crosshair.tsx index f50153cddf..231bc224c3 100644 --- a/src/components/crosshair.tsx +++ b/src/components/crosshair.tsx @@ -21,11 +21,11 @@ class CrosshairComponent extends React.Component { render() { const { isCrosshairVisible } = this.props.chartStore!; if (!isCrosshairVisible.get()) { - return
; + return
; } return ( -
+
{this.renderBand()} {this.renderLine()}
@@ -49,7 +49,7 @@ class CrosshairComponent extends React.Component { background: band.fill, }; - return
; + return
; } renderLine() { @@ -82,7 +82,7 @@ class CrosshairComponent extends React.Component { borderLeftStyle: line.dash ? 'dashed' : 'solid', }; } - return
; + return
; } } diff --git a/src/components/highlighter.tsx b/src/components/highlighter.tsx index 95a0d3efa0..fb73e01db8 100644 --- a/src/components/highlighter.tsx +++ b/src/components/highlighter.tsx @@ -20,7 +20,7 @@ class HighlighterComponent extends React.Component { const left = chartDimensions.left + chartTransform.x; const top = chartDimensions.top + chartTransform.y; return ( - + {highlightedGeometries.map((geom, i) => { const { color, x, y } = geom; diff --git a/src/components/icons/_icon.scss b/src/components/icons/_icon.scss new file mode 100644 index 0000000000..2d4a0c720e --- /dev/null +++ b/src/components/icons/_icon.scss @@ -0,0 +1,15 @@ +.echIcon { + flex-shrink: 0; // Ensures it never scales down below it's intended size + display: inline-block; + vertical-align: middle; + fill: currentColor; + + svg { + transform: translate(0, 0); // Hack to fix Firefox "softness" + } + + &:focus { + opacity: 1; // We often hide icons on hover. Make sure they appear on focus. + background: $euiFocusBackgroundColor; + } +} diff --git a/src/components/icons/_index.scss b/src/components/icons/_index.scss new file mode 100644 index 0000000000..673336d14a --- /dev/null +++ b/src/components/icons/_index.scss @@ -0,0 +1 @@ +@import 'icon'; diff --git a/src/components/icons/assets/alert.tsx b/src/components/icons/assets/alert.tsx new file mode 100644 index 0000000000..83f4c02f40 --- /dev/null +++ b/src/components/icons/assets/alert.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { Props } from '../icon'; + +// tslint:disable:max-line-length +export function AlertIcon(extraProps: Props) { + return ( + + + + ); +} diff --git a/src/components/icons/assets/dot.tsx b/src/components/icons/assets/dot.tsx new file mode 100644 index 0000000000..58921040ba --- /dev/null +++ b/src/components/icons/assets/dot.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { Props } from '../icon'; + +export function DotIcon(extraProps: Props) { + return ( + + + + + + + + + ); +} diff --git a/src/components/icons/assets/empty.tsx b/src/components/icons/assets/empty.tsx new file mode 100644 index 0000000000..2df15faba9 --- /dev/null +++ b/src/components/icons/assets/empty.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { Props } from '../icon'; + +export function EmptyIcon(extraProps: Props) { + return ; +} diff --git a/src/components/icons/assets/eye.tsx b/src/components/icons/assets/eye.tsx new file mode 100644 index 0000000000..79c6ed2800 --- /dev/null +++ b/src/components/icons/assets/eye.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { Props } from '../icon'; +// tslint:disable:max-line-length +export function EyeIcon(extraProps: Props) { + return ( + + + + ); +} diff --git a/src/components/icons/assets/eye_closed.tsx b/src/components/icons/assets/eye_closed.tsx new file mode 100644 index 0000000000..f5d093a703 --- /dev/null +++ b/src/components/icons/assets/eye_closed.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { Props } from '../icon'; +// tslint:disable:max-line-length +export function EyeClosedIcon(extraProps: Props) { + return ( + + + + ); +} diff --git a/src/components/icons/assets/list.tsx b/src/components/icons/assets/list.tsx new file mode 100644 index 0000000000..1e4b5526f5 --- /dev/null +++ b/src/components/icons/assets/list.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { Props } from '../icon'; + +// tslint:disable:max-line-length +export function ListIcon(extraProps: Props) { + return ( + + + + ); +} diff --git a/src/components/icons/assets/question_in_circle.tsx b/src/components/icons/assets/question_in_circle.tsx new file mode 100644 index 0000000000..b0d03af21f --- /dev/null +++ b/src/components/icons/assets/question_in_circle.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { Props } from '../icon'; +// tslint:disable:max-line-length +export function QuestionInCircle(extraProps: Props) { + return ( + + > + + + ); +} diff --git a/src/components/icons/icon.tsx b/src/components/icons/icon.tsx new file mode 100644 index 0000000000..357b4f9de6 --- /dev/null +++ b/src/components/icons/icon.tsx @@ -0,0 +1,64 @@ +import classNames from 'classnames'; +import React, { FunctionComponent, SVGAttributes } from 'react'; +import { AlertIcon } from './assets/alert'; +import { DotIcon } from './assets/dot'; +import { EmptyIcon } from './assets/empty'; +import { EyeIcon } from './assets/eye'; +import { EyeClosedIcon } from './assets/eye_closed'; +import { ListIcon } from './assets/list'; +import { QuestionInCircle } from './assets/question_in_circle'; + +export type Omit = Pick>; + +const typeToIconMap = { + alert: AlertIcon, + dot: DotIcon, + empty: EmptyIcon, + eye: EyeIcon, + eyeClosed: EyeClosedIcon, + list: ListIcon, + questionInCircle: QuestionInCircle, +}; +export type IconColor = string; + +export type IconType = keyof typeof typeToIconMap; + +export interface IconProps { + className?: string; + 'aria-label'?: string; + 'data-test-subj'?: string; + type?: IconType; + color?: IconColor; +} + +export type Props = Omit, 'color'> & IconProps; + +export const Icon: FunctionComponent = ({ type, color, className, tabIndex, ...rest }) => { + let optionalCustomStyles = null; + + if (color) { + optionalCustomStyles = { color }; + } + + const classes = classNames('echIcon', className); + + const Svg = (type && typeToIconMap[type]) || EmptyIcon; + + // This is a fix for IE and Edge, which ignores tabindex="-1" on an SVG, but respects + // focusable="false". + // - If there's no tab index specified, we'll default the icon to not be focusable, + // which is how SVGs behave in Chrome, Safari, and FF. + // - If tab index is -1, then the consumer wants the icon to not be focusable. + // - For all other values, the consumer wants the icon to be focusable. + const focusable = tabIndex == null || tabIndex === -1 ? 'false' : 'true'; + + return ( + + ); +}; diff --git a/src/components/legend.tsx b/src/components/legend.tsx deleted file mode 100644 index 46ba0bfa89..0000000000 --- a/src/components/legend.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import classNames from 'classnames'; -import { inject, observer } from 'mobx-react'; -import React from 'react'; -import { isVertical } from '../lib/axes/axis_utils'; -import { LegendItem } from '../lib/series/legend'; -import { ChartStore } from '../state/chart_state'; -import { LegendElement } from './legend_element'; - -interface ReactiveChartProps { - chartStore?: ChartStore; // FIX until we find a better way on ts mobx -} - -class LegendComponent extends React.Component { - static displayName = 'Legend'; - - onCollapseLegend = () => { - this.props.chartStore!.toggleLegendCollapsed(); - } - - render() { - const { - initialized, - legendItems, - legendPosition, - showLegend, - legendCollapsed, - debug, - chartTheme, - } = this.props.chartStore!; - - if ( - !showLegend.get() || - !initialized.get() || - legendItems.size === 0 || - legendPosition === undefined - ) { - return null; - } - - const legendClasses = classNames( - 'elasticChartsLegend', - `elasticChartsLegend--${legendPosition}`, - { - 'elasticChartsLegend--collapsed': legendCollapsed.get(), - 'elasticChartsLegend--debug': debug, - }, - ); - let paddingStyle; - let legendItemGrow = false; - if (isVertical(legendPosition)) { - paddingStyle = { - paddingTop: chartTheme.chartMargins.top, - paddingBottom: chartTheme.chartMargins.bottom, - }; - legendItemGrow = true; - } else { - paddingStyle = { - paddingLeft: chartTheme.chartMargins.left, - paddingRight: chartTheme.chartMargins.right, - }; - legendItemGrow = true; - } - return ( -
-
- - {[...legendItems.values()].map((item) => { - const { isLegendItemVisible } = item; - - const legendItemProps = { - key: item.key, - className: classNames('elasticChartsLegendList__item', 'euiIEFlexWrapFix', { - 'elasticChartsLegendList__item--hidden': !isLegendItemVisible, - }), - onMouseEnter: this.onLegendItemMouseover(item.key), - onMouseLeave: this.onLegendItemMouseout, - }; - - return ( - - {this.renderLegendElement(item, item.key, legendItemGrow, legendItemProps)} - - ); - })} - -
-
- ); - } - - private onLegendItemMouseover = (legendItemKey: string) => () => { - this.props.chartStore!.onLegendItemOver(legendItemKey); - } - - private onLegendItemMouseout = () => { - this.props.chartStore!.onLegendItemOut(); - } - - private renderLegendElement = ( - { color, label, isSeriesVisible, displayValue }: LegendItem, - legendItemKey: string, - legendItemGrow: boolean, - containerProps: { - key: string; - className: string; - onMouseEnter: (key: React.MouseEvent) => void; - onMouseLeave: () => void; - }, - ) => { - const tooltipValues = this.props.chartStore!.legendItemTooltipValues.get(); - let tooltipValue; - - if (tooltipValues && tooltipValues.get(legendItemKey)) { - tooltipValue = tooltipValues.get(legendItemKey); - } - - const display = tooltipValue != null ? tooltipValue : displayValue.formatted; - const props = { color, label, isSeriesVisible, legendItemKey, displayValue: display }; - - return ; - } -} - -export const Legend = inject('chartStore')(observer(LegendComponent)); diff --git a/src/components/legend/_index.scss b/src/components/legend/_index.scss new file mode 100644 index 0000000000..8caba8f13e --- /dev/null +++ b/src/components/legend/_index.scss @@ -0,0 +1,5 @@ +@import 'variables'; +@import 'legend'; +@import 'legend_button'; +@import 'legend_list'; +@import 'legend_item'; diff --git a/src/components/legend/_legend.scss b/src/components/legend/_legend.scss new file mode 100644 index 0000000000..e4637a849f --- /dev/null +++ b/src/components/legend/_legend.scss @@ -0,0 +1,70 @@ +// Legend + +.echLegend { + @include euiOverflowShadow; + // Padding supplied in JS to match chart margins + position: absolute !important; // Override shadow + overflow: hidden; +} + +.echLegend--collapsed { + display: none; +} + +.echLegend--debug { + background: red; +} + +.echLegend--top, +.echLegend--bottom { + left: $euiSizeL; + right: 0; + height: $echLegendMaxHeight; + + .echLegendList { + flex-direction: row; + flex-wrap: wrap; + } + + .echLegendItem { + margin-right: $euiSizeL; + width: $echLegendMaxWidth; + } +} + +.echLegend--top { + top: 0; +} +.echLegend--bottom { + bottom: 0; +} + +.echLegend--left, +.echLegend--right { + top: 0; + bottom: 0; + width: $echLegendMaxWidth; + + .echLegendList { + flex-direction: column; + } + + .echLegendItem { + width: 100%; + } +} + +.echLegend--left { + left: 0; +} +.echLegend--right { + right: 0; +} + +.echLegendListContainer { + @include euiScrollBar; + height: 100%; + width: 100%; + overflow-y: auto; + overflow-x: hidden; +} diff --git a/src/components/legend/_legend_button.scss b/src/components/legend/_legend_button.scss new file mode 100644 index 0000000000..e634285241 --- /dev/null +++ b/src/components/legend/_legend_button.scss @@ -0,0 +1,23 @@ +.echLegendButton { + padding: $euiSizeXS; + line-height: 1; + opacity: 0.35; + border-radius: $euiBorderRadius; + background-color: $euiColorEmptyShade; + position: absolute; + bottom: 0; + left: 0; + transition: + opacity $euiAnimSpeedFast $euiAnimSlightResistance, + background-color $euiAnimSpeedFast $euiAnimSlightResistance; + + &:hover, + &:focus { + opacity: 1; + background-color: $euiFocusBackgroundColor; + } +} + +.echLegendButton--isOpen { + opacity: 1; +} diff --git a/src/components/legend/_legend_item.scss b/src/components/legend/_legend_item.scss new file mode 100644 index 0000000000..8c43177b28 --- /dev/null +++ b/src/components/legend/_legend_item.scss @@ -0,0 +1,55 @@ +.echLegendItem { + color: $euiTextColor; + height: $echLegendItemHeight; + width: 100%; + display: flex; + flex-wrap: nowrap; + justify-content: space-between; + user-select: none; + align-items: center; + + &:hover { + .echLegendItem__title { + text-decoration: underline; + } + } +} + +.echLegendItem__color { + margin-right: $euiSizeXS; +} + +.echLegendItem__visibility { + margin-right: $euiSizeXS; + + &:hover { + cursor: pointer; + } +} + +.echLegendItem__title { + @include euiFontSizeXS; + @include euiTextTruncate; + margin-right: $euiSizeXS; + flex: 1; +} + +.echLegendItem__title--selected { + text-decoration: underline; +} + +.echLegendItem__title--hasClickListener { + &:hover { + cursor: pointer; + } +} + +.echLegendItem__displayValue { + @include euiFontSizeXS; + text-align: right; + font-feature-settings: 'tnum'; + + &.echLegendItem__displayValue--hidden { + display: none; + } +} diff --git a/src/components/legend/_legend_list.scss b/src/components/legend/_legend_list.scss new file mode 100644 index 0000000000..41d95552fe --- /dev/null +++ b/src/components/legend/_legend_list.scss @@ -0,0 +1,3 @@ +.echLegendList { + display: flex; +} diff --git a/src/components/legend/_variables.scss b/src/components/legend/_variables.scss new file mode 100644 index 0000000000..9eb2df5292 --- /dev/null +++ b/src/components/legend/_variables.scss @@ -0,0 +1,3 @@ +$echLegendMaxWidth: 200px; +$echLegendMaxHeight: $euiSizeXL * 2; +$echLegendItemHeight: ($echLegendMaxHeight / 2) - 6px; diff --git a/src/components/legend/legend.tsx b/src/components/legend/legend.tsx new file mode 100644 index 0000000000..b17e1b9f5f --- /dev/null +++ b/src/components/legend/legend.tsx @@ -0,0 +1,122 @@ +import classNames from 'classnames'; +import { inject, observer } from 'mobx-react'; +import React from 'react'; +import { isVertical } from '../../lib/axes/axis_utils'; +import { LegendItem as SeriesLegendItem } from '../../lib/series/legend'; +import { ChartStore } from '../../state/chart_state'; +import { LegendItem } from './legend_item'; + +interface LegendProps { + chartStore?: ChartStore; // FIX until we find a better way on ts mobx + legendId: string; +} + +class LegendComponent extends React.Component { + static displayName = 'Legend'; + + onCollapseLegend = () => { + this.props.chartStore!.toggleLegendCollapsed(); + } + + render() { + const { legendId } = this.props; + const { + initialized, + legendItems, + legendPosition, + showLegend, + legendCollapsed, + debug, + chartTheme, + } = this.props.chartStore!; + + if ( + !showLegend.get() || + !initialized.get() || + legendItems.size === 0 || + legendPosition === undefined + ) { + return null; + } + + const legendClasses = classNames('echLegend', `echLegend--${legendPosition}`, { + 'echLegend--collapsed': legendCollapsed.get(), + 'echLegend--debug': debug, + }); + let paddingStyle; + if (isVertical(legendPosition)) { + paddingStyle = { + paddingTop: chartTheme.chartMargins.top, + paddingBottom: chartTheme.chartMargins.bottom, + }; + } + return ( +
+
+
+ {[...legendItems.values()].map((item) => { + // const { isLegendItemVisible } = item; + + // const legendItemProps = { + // key: item.key, + // className: classNames('echLegendList__item', { + // 'echLegendList__item--hidden': !isLegendItemVisible, + // }), + // onMouseEnter: this.onLegendItemMouseover(item.key), + // onMouseLeave: this.onLegendItemMouseout, + // }; + + return this.renderLegendElement( + item, + item.key, + this.onLegendItemMouseover(item.key), + this.onLegendItemMouseout, + ); + })} +
+
+
+ ); + } + + onLegendItemMouseover = (legendItemKey: string) => () => { + this.props.chartStore!.onLegendItemOver(legendItemKey); + } + + onLegendItemMouseout = () => { + this.props.chartStore!.onLegendItemOut(); + } + + private renderLegendElement = ( + { color, label, isSeriesVisible, displayValue }: SeriesLegendItem, + legendItemKey: string, + onMouseEnter: (event: React.MouseEvent) => void, + onMouseLeave: () => void, + ) => { + const tooltipValues = this.props.chartStore!.legendItemTooltipValues.get(); + let tooltipValue; + + if (tooltipValues && tooltipValues.get(legendItemKey)) { + tooltipValue = tooltipValues.get(legendItemKey); + } + + const display = tooltipValue != null ? tooltipValue : displayValue.formatted; + const props = { color, label, isSeriesVisible, legendItemKey, displayValue: display }; + + return ( + + ); + } +} + +export const Legend = inject('chartStore')(observer(LegendComponent)); diff --git a/src/components/legend_button.tsx b/src/components/legend/legend_button.tsx similarity index 58% rename from src/components/legend_button.tsx rename to src/components/legend/legend_button.tsx index dab7f3e54f..028fcb73de 100644 --- a/src/components/legend_button.tsx +++ b/src/components/legend/legend_button.tsx @@ -1,14 +1,15 @@ -import { EuiButtonIcon } from '@elastic/eui'; import classNames from 'classnames'; import { inject, observer } from 'mobx-react'; import React from 'react'; -import { ChartStore } from '../state/chart_state'; +import { ChartStore } from '../../state/chart_state'; +import { Icon } from '../icons/icon'; -interface ReactiveChartProps { +interface LegendButtonProps { chartStore?: ChartStore; + legendId: string; } -class LegendButtonComponent extends React.Component { +class LegendButtonComponent extends React.Component { static displayName = 'Legend'; onCollapseLegend = () => { this.props.chartStore!.toggleLegendCollapsed(); @@ -21,16 +22,21 @@ class LegendButtonComponent extends React.Component { return null; } const isOpen = !legendCollapsed.get(); - const className = classNames('elasticChartsLegend__toggle', { - 'elasticChartsLegend__toggle--isOpen': isOpen, + const className = classNames('echLegendButton', { + 'echLegendButton--isOpen': isOpen, }); return ( - + title={legendCollapsed.get() ? 'Expand legend' : 'Collapse legend'} + aria-controls={this.props.legendId} + > + + ); } } diff --git a/src/components/legend/legend_item.tsx b/src/components/legend/legend_item.tsx new file mode 100644 index 0000000000..5d474e74c1 --- /dev/null +++ b/src/components/legend/legend_item.tsx @@ -0,0 +1,168 @@ +import classNames from 'classnames'; +import { inject, observer } from 'mobx-react'; +import React from 'react'; +import { Icon } from '../icons/icon'; + +import { ChartStore } from '../../state/chart_state'; + +interface LegendItemProps { + chartStore?: ChartStore; // FIX until we find a better way on ts mobx + legendItemKey: string; + color: string | undefined; + label: string | undefined; + isSeriesVisible?: boolean; + displayValue: string; + onMouseEnter: (event: React.MouseEvent) => void; + onMouseLeave: () => void; +} + +interface LegendItemState { + isColorPickerOpen: boolean; +} + +class LegendItemComponent extends React.Component { + static displayName = 'LegendItem'; + + constructor(props: LegendItemProps) { + super(props); + this.state = { + isColorPickerOpen: false, + }; + } + + closeColorPicker = () => { + this.setState({ + isColorPickerOpen: false, + }); + } + + toggleColorPicker = () => { + this.setState({ + isColorPickerOpen: !this.state.isColorPickerOpen, + }); + } + + render() { + const { legendItemKey } = this.props; + const { color, label, isSeriesVisible, displayValue, onMouseEnter, onMouseLeave } = this.props; + + const onTitleClick = this.onLegendTitleClick(legendItemKey); + + const showLegendDisplayValue = this.props.chartStore!.showLegendDisplayValue.get(); + const isSelected = legendItemKey === this.props.chartStore!.selectedLegendItemKey.get(); + const hasDisplayValue = this.props.chartStore!.showLegendDisplayValue.get(); + const hasTitleClickListener = Boolean(this.props.chartStore!.onLegendItemClickListener); + return ( +
+ {this.renderColor(this.toggleColorPicker, color)} + {this.renderVisibilityButton(legendItemKey, isSeriesVisible)} + {this.renderTitle(label, onTitleClick, hasTitleClickListener, isSelected, hasDisplayValue)} + {this.renderDisplayValue(displayValue, showLegendDisplayValue, isSeriesVisible)} +
+ ); + } + renderColor(colorClickAction: () => void, color: string | undefined) { + if (!color) { + return null; + } + // TODO add color picler + return ( +
+ +
+ ); + } + + renderVisibilityButton = (legendItemKey: string, isSeriesVisible: boolean = true) => { + const iconType = isSeriesVisible ? 'eye' : 'eyeClosed'; + return ( +
+ +
+ ); + } + + renderTitle( + title: string | undefined, + onTitleClick: () => void, + hasTitleClickListener: boolean, + isSelected: boolean, + hasDisplayValue: boolean, + ) { + // TODO add contextual menu panel on click + if (!title) { + return null; + } + const titleClassNames = classNames('echLegendItem__title', { + ['echLegendItem__title--hasClickListener']: hasTitleClickListener, + ['echLegendItem__title--selected']: isSelected, + ['echLegendItem__title--hasDisplayValue']: hasDisplayValue, + }); + return ( +
+ {title} +
+ ); + } + + renderDisplayValue(displayValue: string, show: boolean, isSeriesVisible: boolean | undefined) { + if (!show) { + return; + } + const displayValueClassNames = classNames('echLegendItem__displayValue', { + ['echLegendItem__displayValue--hidden']: !isSeriesVisible, + }); + return ( +
+ {displayValue} +
+ ); + } + + onLegendTitleClick = (legendItemKey: string) => () => { + this.props.chartStore!.onLegendItemClick(legendItemKey); + } + + // private onLegendItemPanelClose = () => { + // // tslint:disable-next-line:no-console + // console.log('close'); + // } + + // private onColorPickerChange = (legendItemKey: string) => (color: string) => { + // this.props.chartStore!.setSeriesColor(legendItemKey, color); + // } + + // private renderPlusButton = () => { + // return ( + // + // ); + // } + + // private renderMinusButton = () => { + // return ( + // + // ); + // } + + private onVisibilityClick = (legendItemKey: string) => (event: React.MouseEvent) => { + if (event.shiftKey) { + this.props.chartStore!.toggleSingleSeries(legendItemKey); + } else { + this.props.chartStore!.toggleSeriesVisibility(legendItemKey); + } + } +} + +export const LegendItem = inject('chartStore')(observer(LegendItemComponent)); diff --git a/src/components/legend_element.tsx b/src/components/legend_element.tsx deleted file mode 100644 index f50d1aa471..0000000000 --- a/src/components/legend_element.tsx +++ /dev/null @@ -1,207 +0,0 @@ -import { - EuiButtonIcon, - // TODO: remove ts-ignore below once typings file is included in eui for color picker - // @ts-ignore - EuiColorPicker, - EuiContextMenuPanel, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiPopover, - EuiText, -} from '@elastic/eui'; -import classNames from 'classnames'; -import { inject, observer } from 'mobx-react'; -import React from 'react'; - -import { ChartStore } from '../state/chart_state'; - -interface LegendElementProps { - chartStore?: ChartStore; // FIX until we find a better way on ts mobx - legendItemKey: string; - color: string | undefined; - label: string | undefined; - isSeriesVisible?: boolean; - displayValue: string; - key: string; - className: string; - onMouseEnter: (key: React.MouseEvent) => void; - onMouseLeave: () => void; -} - -interface LegendElementState { - isColorPickerOpen: boolean; -} - -class LegendElementComponent extends React.Component { - static displayName = 'LegendElement'; - - constructor(props: LegendElementProps) { - super(props); - this.state = { - isColorPickerOpen: false, - }; - } - - closeColorPicker = () => { - this.setState({ - isColorPickerOpen: false, - }); - } - - toggleColorPicker = () => { - this.setState({ - isColorPickerOpen: !this.state.isColorPickerOpen, - }); - } - - renderDisplayValue(displayValue: string, show: boolean) { - if (!show) { - return; - } - - return ( - - {displayValue} - - ); - } - - render() { - const { legendItemKey } = this.props; - const { color, label, isSeriesVisible, displayValue } = this.props; - const { className, onMouseEnter, onMouseLeave } = this.props; - - const onTitleClick = this.onLegendTitleClick(legendItemKey); - - const showLegendDisplayValue = this.props.chartStore!.showLegendDisplayValue.get(); - const isSelected = legendItemKey === this.props.chartStore!.selectedLegendItemKey.get(); - const titleClassNames = classNames('eui-textTruncate', 'elasticChartsLegendListItem__title', { - ['elasticChartsLegendListItem__title--selected']: isSelected, - ['elasticChartsLegendListItem__title--hasDisplayValue']: this.props.chartStore!.showLegendDisplayValue.get(), - }); - - const colorDotProps = { - color, - onClick: this.toggleColorPicker, - }; - - const colorDot = ; - - const displayValueClassNames = classNames('elasticChartsLegendListItem__displayValue', { - ['elasticChartsLegendListItem__displayValue--hidden']: !isSeriesVisible, - }); - - return ( - - - - - - - - - - {this.renderVisibilityButton(legendItemKey, isSeriesVisible)} - - - - {label} - - } - isOpen={isSelected} - closePopover={this.onLegendItemPanelClose} - panelPaddingSize="s" - anchorPosition="downCenter" - > - - - {this.renderPlusButton()} - {this.renderMinusButton()} - - - - - - {this.renderDisplayValue(displayValue, showLegendDisplayValue)} - - - ); - } - - private onLegendTitleClick = (legendItemKey: string) => () => { - this.props.chartStore!.onLegendItemClick(legendItemKey); - } - - private onLegendItemPanelClose = () => { - // tslint:disable-next-line:no-console - console.log('close'); - } - - private onColorPickerChange = (legendItemKey: string) => (color: string) => { - this.props.chartStore!.setSeriesColor(legendItemKey, color); - } - - private renderPlusButton = () => { - return ( - - ); - } - - private renderMinusButton = () => { - return ( - - ); - } - - private onVisibilityClick = (legendItemKey: string) => (event: React.MouseEvent) => { - if (event.shiftKey) { - this.props.chartStore!.toggleSingleSeries(legendItemKey); - } else { - this.props.chartStore!.toggleSeriesVisibility(legendItemKey); - } - } - - private renderVisibilityButton = (legendItemKey: string, isSeriesVisible: boolean = true) => { - const iconType = isSeriesVisible ? 'eye' : 'eyeClosed'; - return ( - - ); - } -} - -export const LegendElement = inject('chartStore')(observer(LegendElementComponent)); diff --git a/src/components/react_canvas/area_geometries.tsx b/src/components/react_canvas/area_geometries.tsx index 60b5aa4719..822fb2496f 100644 --- a/src/components/react_canvas/area_geometries.tsx +++ b/src/components/react_canvas/area_geometries.tsx @@ -25,7 +25,7 @@ interface AreaGeometriesDataState { export class AreaGeometries extends React.PureComponent< AreaGeometriesDataProps, AreaGeometriesDataState - > { +> { static defaultProps: Partial = { animated: false, }; @@ -97,7 +97,8 @@ export class AreaGeometries extends React.PureComponent< return ; }} - ); + , + ); } else { const pointProps = buildAreaPointProps({ areaIndex, @@ -141,7 +142,8 @@ export class AreaGeometries extends React.PureComponent< return ; }} - ); + , + ); } else { const areaProps = buildAreaProps({ index: i, diff --git a/src/components/react_canvas/bar_geometries.tsx b/src/components/react_canvas/bar_geometries.tsx index 1534e075ed..f28451bbf0 100644 --- a/src/components/react_canvas/bar_geometries.tsx +++ b/src/components/react_canvas/bar_geometries.tsx @@ -20,7 +20,7 @@ interface BarGeometriesDataState { export class BarGeometries extends React.PureComponent< BarGeometriesDataProps, BarGeometriesDataState - > { +> { static defaultProps: Partial = { animated: false, }; diff --git a/src/components/react_canvas/line_geometries.tsx b/src/components/react_canvas/line_geometries.tsx index 1ffb4ec0a4..8ba3b2b86f 100644 --- a/src/components/react_canvas/line_geometries.tsx +++ b/src/components/react_canvas/line_geometries.tsx @@ -25,7 +25,7 @@ interface LineGeometriesDataState { export class LineGeometries extends React.PureComponent< LineGeometriesDataProps, LineGeometriesDataState - > { +> { static defaultProps: Partial = { animated: false, }; @@ -99,7 +99,8 @@ export class LineGeometries extends React.PureComponent< return ; }} - ); + , + ); } else { const pointProps = buildLinePointProps({ lineIndex, @@ -155,7 +156,8 @@ export class LineGeometries extends React.PureComponent< return ; }} - ); + , + ); } else { const lineProps = buildLineProps({ index, diff --git a/src/components/svg/axis.tsx b/src/components/svg/axis.tsx index a13ac993a5..a486df446d 100644 --- a/src/components/svg/axis.tsx +++ b/src/components/svg/axis.tsx @@ -1,166 +1,166 @@ -import React, { SVGProps } from 'react'; -import { AxisTick, AxisTicksDimensions, isHorizontal, isVertical } from '../../lib/axes/axis_utils'; -import { AxisSpec, Position } from '../../lib/series/specs'; -import { Theme } from '../../lib/themes/theme'; -import { Dimensions } from '../../lib/utils/dimensions'; +// import React, { SVGProps } from 'react'; +// import { AxisTick, AxisTicksDimensions, isHorizontal, isVertical } from '../../lib/axes/axis_utils'; +// import { AxisSpec, Position } from '../../lib/series/specs'; +// import { Theme } from '../../lib/themes/theme'; +// import { Dimensions } from '../../lib/utils/dimensions'; -interface AxisProps { - chartTheme: Theme; - axisSpec: AxisSpec; - axisTicksDimensions: AxisTicksDimensions; - axisPosition: Dimensions; - ticks: AxisTick[]; -} +// interface AxisProps { +// chartTheme: Theme; +// axisSpec: AxisSpec; +// axisTicksDimensions: AxisTicksDimensions; +// axisPosition: Dimensions; +// ticks: AxisTick[]; +// } -export class Axis extends React.PureComponent { - render() { - return this.renderAxis(); - } - renderTickLabel = (tick: AxisTick, i: number) => { - const { - axisSpec: { tickSize, tickPadding, position }, - } = this.props; +// export class Axis extends React.PureComponent { +// render() { +// return this.renderAxis(); +// } +// renderTickLabel = (tick: AxisTick, i: number) => { +// const { +// axisSpec: { tickSize, tickPadding, position }, +// } = this.props; - const textProps: SVGProps = {}; +// const textProps: SVGProps = {}; - if (isVertical(position)) { - textProps.y = tick.position; - textProps.textAnchor = position === 'left' ? 'end' : 'start'; - textProps.x = position === 'left' ? 0 : tickSize + tickPadding; - textProps.dominantBaseline = 'middle'; - } else { - textProps.y = position === 'top' ? 0 : tickSize + tickPadding; - textProps.x = tick.position; - textProps.textAnchor = 'middle'; - textProps.dominantBaseline = 'hanging'; - } - // const transform = `translate(${textProps.x}, ${textProps.y})`; - return ( - - {tick.label} - - ); - } +// if (isVertical(position)) { +// textProps.y = tick.position; +// textProps.textAnchor = position === 'left' ? 'end' : 'start'; +// textProps.x = position === 'left' ? 0 : tickSize + tickPadding; +// textProps.dominantBaseline = 'middle'; +// } else { +// textProps.y = position === 'top' ? 0 : tickSize + tickPadding; +// textProps.x = tick.position; +// textProps.textAnchor = 'middle'; +// textProps.dominantBaseline = 'hanging'; +// } +// // const transform = `translate(${textProps.x}, ${textProps.y})`; +// return ( +// +// {tick.label} +// +// ); +// } - private renderTickLine = (tick: AxisTick, i: number) => { - const { - axisSpec: { tickSize, tickPadding, position }, - axisTicksDimensions: { maxLabelBboxHeight }, - } = this.props; +// private renderTickLine = (tick: AxisTick, i: number) => { +// const { +// axisSpec: { tickSize, tickPadding, position }, +// axisTicksDimensions: { maxLabelBboxHeight }, +// } = this.props; - const lineProps: SVGProps = {}; +// const lineProps: SVGProps = {}; - if (isVertical(position)) { - lineProps.x1 = position === 'left' ? tickPadding : 0; - lineProps.x2 = position === 'left' ? tickSize + tickPadding : tickSize; - lineProps.y1 = tick.position; - lineProps.y2 = tick.position; - } else { - lineProps.x1 = tick.position; - lineProps.x2 = tick.position; - lineProps.y1 = position === 'top' ? maxLabelBboxHeight + tickPadding : 0; - lineProps.y2 = position === 'top' ? maxLabelBboxHeight + tickPadding + tickSize : tickSize; - } +// if (isVertical(position)) { +// lineProps.x1 = position === 'left' ? tickPadding : 0; +// lineProps.x2 = position === 'left' ? tickSize + tickPadding : tickSize; +// lineProps.y1 = tick.position; +// lineProps.y2 = tick.position; +// } else { +// lineProps.x1 = tick.position; +// lineProps.x2 = tick.position; +// lineProps.y1 = position === 'top' ? maxLabelBboxHeight + tickPadding : 0; +// lineProps.y2 = position === 'top' ? maxLabelBboxHeight + tickPadding + tickSize : tickSize; +// } - return ; - } - private renderAxis = () => { - const { ticks, axisPosition } = this.props; - const translation = `translate(${axisPosition.left} ${axisPosition.top})`; - return ( - - {this.renderLine()} - {ticks.map(this.renderTickLine)} - - {ticks.filter((tick) => tick.label !== null).map(this.renderTickLabel)} - - {this.renderAxisTitle()} - - ); - } - private renderLine = () => { - const { - axisSpec: { tickSize, tickPadding, position }, - axisPosition, - axisTicksDimensions, - } = this.props; - const lineProps: SVGProps = {}; - if (orientation === 'vertical') { - lineProps.x1 = position === 'left' ? tickSize + tickPadding : 0; - lineProps.x2 = position === 'left' ? tickSize + tickPadding : 0; - lineProps.y1 = 0; - lineProps.y2 = axisPosition.height; - } else { - lineProps.x1 = 0; - lineProps.x2 = axisPosition.width; - lineProps.y1 = - position === 'top' ? axisTicksDimensions.maxLabelBboxHeight + tickSize + tickPadding : 0; - lineProps.y2 = - position === 'top' ? axisTicksDimensions.maxLabelBboxHeight + tickSize + tickPadding : 0; - } - return ; - } - private renderAxisTitle() { - const { - axisSpec: { title, position }, - } = this.props; - if (!title) { - return null; - } - if (isHorizontal(position)) { - return this.renderHoriziontalAxisTitle(); - } - return this.renderVerticalAxisTitle(); - } - private renderVerticalAxisTitle() { - const { - axisPosition: { height }, - axisSpec: { title, position, tickSize, tickPadding }, - axisTicksDimensions: { maxLabelBboxWidth }, - chartTheme: { chartMargins }, - } = this.props; +// return ; +// } +// private renderAxis = () => { +// const { ticks, axisPosition } = this.props; +// const translation = `translate(${axisPosition.left} ${axisPosition.top})`; +// return ( +// +// {this.renderLine()} +// {ticks.map(this.renderTickLine)} +// +// {ticks.filter((tick) => tick.label !== null).map(this.renderTickLabel)} +// +// {this.renderAxisTitle()} +// +// ); +// } +// private renderLine = () => { +// const { +// axisSpec: { tickSize, tickPadding, position }, +// axisPosition, +// axisTicksDimensions, +// } = this.props; +// const lineProps: SVGProps = {}; +// if (orientation === 'vertical') { +// lineProps.x1 = position === 'left' ? tickSize + tickPadding : 0; +// lineProps.x2 = position === 'left' ? tickSize + tickPadding : 0; +// lineProps.y1 = 0; +// lineProps.y2 = axisPosition.height; +// } else { +// lineProps.x1 = 0; +// lineProps.x2 = axisPosition.width; +// lineProps.y1 = +// position === 'top' ? axisTicksDimensions.maxLabelBboxHeight + tickSize + tickPadding : 0; +// lineProps.y2 = +// position === 'top' ? axisTicksDimensions.maxLabelBboxHeight + tickSize + tickPadding : 0; +// } +// return ; +// } +// private renderAxisTitle() { +// const { +// axisSpec: { title, position }, +// } = this.props; +// if (!title) { +// return null; +// } +// if (isHorizontal(position)) { +// return this.renderHoriziontalAxisTitle(); +// } +// return this.renderVerticalAxisTitle(); +// } +// private renderVerticalAxisTitle() { +// const { +// axisPosition: { height }, +// axisSpec: { title, position, tickSize, tickPadding }, +// axisTicksDimensions: { maxLabelBboxWidth }, +// chartTheme: { chartMargins }, +// } = this.props; - const top = height / 2; - const left = - position === Position.Left - ? -(maxLabelBboxWidth + chartMargins.left / 2) - : tickSize + tickPadding + maxLabelBboxWidth + +chartMargins.right / 2; - const translate = `translate(${left} ${top}) rotate(-90)`; - return ( - - - {title} - - - ); - } - private renderHoriziontalAxisTitle() { - const { - axisPosition: { width }, - axisSpec: { title, position, tickSize, tickPadding }, - axisTicksDimensions: { maxLabelBboxHeight }, - chartTheme: { chartMargins }, - } = this.props; +// const top = height / 2; +// const left = +// position === Position.Left +// ? -(maxLabelBboxWidth + chartMargins.left / 2) +// : tickSize + tickPadding + maxLabelBboxWidth + +chartMargins.right / 2; +// const translate = `translate(${left} ${top}) rotate(-90)`; +// return ( +// +// +// {title} +// +// +// ); +// } +// private renderHoriziontalAxisTitle() { +// const { +// axisPosition: { width }, +// axisSpec: { title, position, tickSize, tickPadding }, +// axisTicksDimensions: { maxLabelBboxHeight }, +// chartTheme: { chartMargins }, +// } = this.props; - const top = - position === Position.Top - ? -chartMargins.top / 2 - : maxLabelBboxHeight + tickPadding + tickSize + chartMargins.bottom / 2; - const left = width / 2; - const translate = `translate(${left} ${top} )`; - return ( - - - {title} - - - ); - } -} +// const top = +// position === Position.Top +// ? -chartMargins.top / 2 +// : maxLabelBboxHeight + tickPadding + tickSize + chartMargins.bottom / 2; +// const left = width / 2; +// const translate = `translate(${left} ${top} )`; +// return ( +// +// +// {title} +// +// +// ); +// } +// } diff --git a/src/components/svg/bar_series.tsx b/src/components/svg/bar_series.tsx index e40be82d88..525a239d9f 100644 --- a/src/components/svg/bar_series.tsx +++ b/src/components/svg/bar_series.tsx @@ -1,30 +1,30 @@ -import React from 'react'; -import { BarGeometry } from '../../lib/series/rendering'; +// import React from 'react'; +// import { BarGeometry } from '../../lib/series/rendering'; -interface BarSeriesDataProps { - animated?: boolean; - bars: BarGeometry[]; -} -export class BarSeries extends React.PureComponent { - static defaultProps: Partial = { - animated: false, - }; - render() { - const { animated, bars } = this.props; - if (animated) { - return this.renderAnimatedBars(bars); - } else { - return this.renderBars(bars); - } - } - private renderBars = (bars: BarGeometry[]) => { - return bars.map(({ x, y, width, height, color }, index) => { - return ; - }); - } - private renderAnimatedBars = (geoms: BarGeometry[]) => { - // tslint:disable-next-line:no-console - console.warn('[EUISeriesChart] Missing bar animation on SVG renderer'); - return this.renderBars(geoms); - } -} +// interface BarSeriesDataProps { +// animated?: boolean; +// bars: BarGeometry[]; +// } +// export class BarSeries extends React.PureComponent { +// static defaultProps: Partial = { +// animated: false, +// }; +// render() { +// const { animated, bars } = this.props; +// if (animated) { +// return this.renderAnimatedBars(bars); +// } else { +// return this.renderBars(bars); +// } +// } +// private renderBars = (bars: BarGeometry[]) => { +// return bars.map(({ x, y, width, height, color }, index) => { +// return ; +// }); +// } +// private renderAnimatedBars = (geoms: BarGeometry[]) => { +// // tslint:disable-next-line:no-console +// console.warn('[EUISeriesChart] Missing bar animation on SVG renderer'); +// return this.renderBars(geoms); +// } +// } diff --git a/src/components/svg/reactive_chart.tsx b/src/components/svg/reactive_chart.tsx index c273003b4d..0a74c5eb71 100644 --- a/src/components/svg/reactive_chart.tsx +++ b/src/components/svg/reactive_chart.tsx @@ -1,186 +1,186 @@ -// import { toJS } from 'mobx'; -import { inject, observer } from 'mobx-react'; -import React from 'react'; -// import { LineSeries } from '../components/line_series'; -import { ChartStore } from '../../state/chart_state'; -// import { DataSeriesType } from '../commons/series/specs'; -// import { AreaSeries } from '../components/area_series'; -import { Axis } from './axis'; -import { BarSeries } from './bar_series'; -// import { AreaSeriesGlyph } from '../utils/area_series_utils'; -// import { BarSeriesGlyph } from '../utils/bar_series_utils'; -// import { LineSeriesGlyph } from '../utils/line_series_utils'; +// // import { toJS } from 'mobx'; +// import { inject, observer } from 'mobx-react'; +// import React from 'react'; +// // import { LineSeries } from '../components/line_series'; +// import { ChartStore } from '../../state/chart_state'; +// // import { DataSeriesType } from '../commons/series/specs'; +// // import { AreaSeries } from '../components/area_series'; +// import { Axis } from './axis'; +// import { BarSeries } from './bar_series'; +// // import { AreaSeriesGlyph } from '../utils/area_series_utils'; +// // import { BarSeriesGlyph } from '../utils/bar_series_utils'; +// // import { LineSeriesGlyph } from '../utils/line_series_utils'; -interface ReactiveChartProps { - chartStore?: ChartStore; // FIX until we find a better way on ts mobx -} -// interface BarSeriesDataGlyphs { -// type: DataSeriesType; -// bars: BarSeriesGlyph[]; -// } -// interface LineSeriesDataGlyphs { -// type: DataSeriesType; -// line: LineSeriesGlyph; -// } -// interface AreaSeriesDataGlyphs { -// type: DataSeriesType; -// area: AreaSeriesGlyph; +// interface ReactiveChartProps { +// chartStore?: ChartStore; // FIX until we find a better way on ts mobx // } +// // interface BarSeriesDataGlyphs { +// // type: DataSeriesType; +// // bars: BarSeriesGlyph[]; +// // } +// // interface LineSeriesDataGlyphs { +// // type: DataSeriesType; +// // line: LineSeriesGlyph; +// // } +// // interface AreaSeriesDataGlyphs { +// // type: DataSeriesType; +// // area: AreaSeriesGlyph; +// // } -class Chart extends React.Component { - static displayName = 'ReactiveChart'; +// class Chart extends React.Component { +// static displayName = 'ReactiveChart'; - componentDidMount() { - // tslint:disable-next-line:no-console - console.log('Chart mounted'); - } +// componentDidMount() { +// // tslint:disable-next-line:no-console +// console.log('Chart mounted'); +// } - componentWillUnmount() { - // tslint:disable-next-line:no-console - console.log('Chart unmounted'); - } +// componentWillUnmount() { +// // tslint:disable-next-line:no-console +// console.log('Chart unmounted'); +// } - renderAxes = () => { - const { - axesVisibleTicks, - axesSpecs, - axesTicksDimensions, - axesPositions, - chartTheme, - } = this.props.chartStore!; - const axesComponents: JSX.Element[] = []; - axesVisibleTicks.forEach((axisTicks, axisId) => { - const axisSpec = axesSpecs.get(axisId); - const axisTicksDimensions = axesTicksDimensions.get(axisId); - const axisPosition = axesPositions.get(axisId); - const ticks = axesVisibleTicks.get(axisId); - if (!ticks || !axisSpec || !axisTicksDimensions || !axisPosition) { - return; - } - axesComponents.push( - , - ); - }); - return axesComponents; - } - // public renderLineSeries = () => { - // const { seriesGlyphs } = this.props.chartStore!; - // const points: JSX.Element[] = []; - // seriesGlyphs.forEach((spec, specId) => { - // if (spec.type !== DataSeriesType.Line) { - // return; - // } - // const lineGlyph = spec as LineSeriesDataGlyphs; - // points.push(); - // }); - // return points; - // } - // public renderPointSeries = () => { - // return null; - // } - renderBarSeries = () => { - const { geometries, canDataBeAnimated } = this.props.chartStore!; - if (!geometries) { - return; - } - return ; - } - // public renderAreaSeries = () => { - // const { seriesGlyphs } = this.props.chartStore!; - // const points: JSX.Element[] = []; - // seriesGlyphs.forEach((spec, specId) => { - // if (spec.type !== DataSeriesType.Area) { - // return; - // } - // const areaGlyph = spec as AreaSeriesDataGlyphs; - // // tslint:disable-next-line:no-console - // console.log('areaGlyph', areaGlyph); - // points.push(); - // }); - // return points; - // } - render() { - const { initialized } = this.props.chartStore!; - if (!initialized.get()) { - return null; - } +// renderAxes = () => { +// const { +// axesVisibleTicks, +// axesSpecs, +// axesTicksDimensions, +// axesPositions, +// chartTheme, +// } = this.props.chartStore!; +// const axesComponents: JSX.Element[] = []; +// axesVisibleTicks.forEach((axisTicks, axisId) => { +// const axisSpec = axesSpecs.get(axisId); +// const axisTicksDimensions = axesTicksDimensions.get(axisId); +// const axisPosition = axesPositions.get(axisId); +// const ticks = axesVisibleTicks.get(axisId); +// if (!ticks || !axisSpec || !axisTicksDimensions || !axisPosition) { +// return; +// } +// axesComponents.push( +// , +// ); +// }); +// return axesComponents; +// } +// // public renderLineSeries = () => { +// // const { seriesGlyphs } = this.props.chartStore!; +// // const points: JSX.Element[] = []; +// // seriesGlyphs.forEach((spec, specId) => { +// // if (spec.type !== DataSeriesType.Line) { +// // return; +// // } +// // const lineGlyph = spec as LineSeriesDataGlyphs; +// // points.push(); +// // }); +// // return points; +// // } +// // public renderPointSeries = () => { +// // return null; +// // } +// renderBarSeries = () => { +// const { geometries, canDataBeAnimated } = this.props.chartStore!; +// if (!geometries) { +// return; +// } +// return ; +// } +// // public renderAreaSeries = () => { +// // const { seriesGlyphs } = this.props.chartStore!; +// // const points: JSX.Element[] = []; +// // seriesGlyphs.forEach((spec, specId) => { +// // if (spec.type !== DataSeriesType.Area) { +// // return; +// // } +// // const areaGlyph = spec as AreaSeriesDataGlyphs; +// // // tslint:disable-next-line:no-console +// // console.log('areaGlyph', areaGlyph); +// // points.push(); +// // }); +// // return points; +// // } +// render() { +// const { initialized } = this.props.chartStore!; +// if (!initialized.get()) { +// return null; +// } - const { parentDimensions, chartDimensions, chartRotation } = this.props.chartStore!; - // console.log({ lineSeriesSpecs: toJS(lineSeriesSpecs)}) - // console.log({ groupDomains: toJS(groupDomains)}) - // console.log({ vLeftAxisSpec: toJS(vLeftAxisSpec)}) - // console.log({ hBottomAxisSpec: toJS(hBottomAxisSpec)}) - // console.log({ chartDimensions}); - let chartTransform = ''; - if (chartRotation === 90) { - chartTransform = `translate(${chartDimensions.width} 0) rotate(90)`; - } else if (chartRotation === -90) { - chartTransform = `translate(0 ${chartDimensions.height}) rotate(-90)`; - } - return ( -
- - {/* - - - - */} - - {/* */} - {/* {this.renderLineSeries()} - {this.renderPointSeries()} */} - - {this.renderBarSeries()} - - {/* {this.renderAreaSeries()} */} - - {this.renderAxes()} - -
- ); - } -} +// const { parentDimensions, chartDimensions, chartRotation } = this.props.chartStore!; +// // console.log({ lineSeriesSpecs: toJS(lineSeriesSpecs)}) +// // console.log({ groupDomains: toJS(groupDomains)}) +// // console.log({ vLeftAxisSpec: toJS(vLeftAxisSpec)}) +// // console.log({ hBottomAxisSpec: toJS(hBottomAxisSpec)}) +// // console.log({ chartDimensions}); +// let chartTransform = ''; +// if (chartRotation === 90) { +// chartTransform = `translate(${chartDimensions.width} 0) rotate(90)`; +// } else if (chartRotation === -90) { +// chartTransform = `translate(0 ${chartDimensions.height}) rotate(-90)`; +// } +// return ( +//
+// +// {/* +// +// +// +// */} +// +// {/* */} +// {/* {this.renderLineSeries()} +// {this.renderPointSeries()} */} +// +// {this.renderBarSeries()} +// +// {/* {this.renderAreaSeries()} */} +// +// {this.renderAxes()} +// +//
+// ); +// } +// } -export const ReactiveChart = inject('chartStore')(observer(Chart)); +// export const ReactiveChart = inject('chartStore')(observer(Chart)); diff --git a/src/components/tooltips.tsx b/src/components/tooltips.tsx index 6c72e5356c..12e4cd505c 100644 --- a/src/components/tooltips.tsx +++ b/src/components/tooltips.tsx @@ -13,29 +13,29 @@ class TooltipsComponent extends React.Component { render() { const { isTooltipVisible, tooltipData, tooltipPosition } = this.props.chartStore!; if (!isTooltipVisible.get()) { - return
; + return
; } return ( -
-

{tooltipData[0] && tooltipData[0].value}

-
+
+

{tooltipData[0] && tooltipData[0].value}

+
{tooltipData.slice(1).map(({ name, value, color, isHighlighted }, index) => { const classes = classNames({ - elasticChartsTooltip__rowHighlighted: isHighlighted, + echTooltip__rowHighlighted: isHighlighted, }); return ( - + ); })} diff --git a/src/index.scss b/src/index.scss deleted file mode 100644 index d63170069e..0000000000 --- a/src/index.scss +++ /dev/null @@ -1,23 +0,0 @@ -@import '../node_modules/@elastic/eui/src/global_styling/variables/index'; -@import '../node_modules/@elastic/eui/src/global_styling/mixins/helpers'; -@import '../node_modules/@elastic/eui/src/global_styling/mixins/shadow'; -@import '../node_modules/@elastic/eui/src/global_styling/mixins/typography'; -@import '../node_modules/@elastic/eui/src/global_styling/functions/colors'; - -@import 'components/chart'; -@import 'components/legend'; -@import 'components/tooltip'; - -.elasticCharts { - position: relative; - width: 100%; - height: 100%; -} - -.elasticCharts { - &:hover { - .elasticChartsLegend__toggle { - opacity: 1; - } - } -} diff --git a/src/lib/series/legend.ts b/src/lib/series/legend.ts index 055529e35e..cfd1d80fb6 100644 --- a/src/lib/series/legend.ts +++ b/src/lib/series/legend.ts @@ -1,7 +1,7 @@ -import { findDataSeriesByColorValues, getAxesSpecForSpecId } from '../../state/utils'; +import { getAxesSpecForSpecId } from '../../state/utils'; import { identity } from '../utils/commons'; import { AxisId, SpecId } from '../utils/ids'; -import { DataSeriesColorsValues, getSortedDataSeriesColorsValuesMap } from './series'; +import { DataSeriesColorsValues, findDataSeriesByColorValues, getSortedDataSeriesColorsValuesMap } from './series'; import { AxisSpec, BasicSeriesSpec } from './specs'; export interface LegendItem { diff --git a/src/lib/series/rendering.ts b/src/lib/series/rendering.ts index ac27cfe78a..f403871abd 100644 --- a/src/lib/series/rendering.ts +++ b/src/lib/series/rendering.ts @@ -1,5 +1,4 @@ import { area, line } from 'd3-shape'; -import { mutableIndexedGeometryMapUpsert } from '../../state/utils'; import { CanvasTextBBoxCalculator } from '../axes/canvas_text_bbox_calculator'; import { AreaSeriesStyle, @@ -100,6 +99,20 @@ export function isBarGeometry(ig: IndexedGeometry): ig is BarGeometry { return ig.hasOwnProperty('width') && ig.hasOwnProperty('height'); } +export function mutableIndexedGeometryMapUpsert( + mutableGeometriesIndex: Map, + key: any, + geometry: IndexedGeometry | IndexedGeometry[], +) { + const existing = mutableGeometriesIndex.get(key); + const upsertGeometry: IndexedGeometry[] = Array.isArray(geometry) ? geometry : [geometry]; + if (existing === undefined) { + mutableGeometriesIndex.set(key, upsertGeometry); + } else { + mutableGeometriesIndex.set(key, [...upsertGeometry, ...existing]); + } +} + export function renderPoints( shift: number, dataset: DataSeriesDatum[], @@ -194,8 +207,10 @@ export function renderBars( const barGeometries: BarGeometry[] = []; const bboxCalculator = new CanvasTextBBoxCalculator(); - const fontSize = seriesStyle && seriesStyle.displayValue ? seriesStyle.displayValue.fontSize : undefined; - const fontFamily = seriesStyle && seriesStyle.displayValue ? seriesStyle.displayValue.fontFamily : undefined; + const fontSize = + seriesStyle && seriesStyle.displayValue ? seriesStyle.displayValue.fontSize : undefined; + const fontFamily = + seriesStyle && seriesStyle.displayValue ? seriesStyle.displayValue.fontFamily : undefined; dataset.forEach((datum) => { const { y0, y1, initialY1 } = datum; @@ -226,33 +241,45 @@ export function renderBars( const x = xScale.scale(datum.x) + xScale.bandwidth * orderIndex; const width = xScale.bandwidth; - const formattedDisplayValue = displayValueSettings && displayValueSettings.valueFormatter ? - displayValueSettings.valueFormatter(initialY1) : undefined; + const formattedDisplayValue = + displayValueSettings && displayValueSettings.valueFormatter + ? displayValueSettings.valueFormatter(initialY1) + : undefined; // only show displayValue for even bars if showOverlappingValue - const displayValueText = displayValueSettings && displayValueSettings.isAlternatingValueLabel ? - (barGeometries.length % 2 === 0 ? formattedDisplayValue : undefined) - : formattedDisplayValue; - - const computedDisplayValueWidth = bboxCalculator.compute(displayValueText || '', fontSize, fontFamily).getOrElse({ - width: 0, - height: 0, - }).width; - const displayValueWidth = displayValueSettings && displayValueSettings.isValueContainedInElement ? - width : computedDisplayValueWidth; - - const hideClippedValue = displayValueSettings ? displayValueSettings.hideClippedValue : undefined; - - const displayValue = (displayValueSettings && displayValueSettings.showValueLabel) ? - { - text: displayValueText, - width: displayValueWidth, - height: fontSize || 0, - hideClippedValue, - isValueContainedInElement: displayValueSettings.isValueContainedInElement, - } + const displayValueText = + displayValueSettings && displayValueSettings.isAlternatingValueLabel + ? barGeometries.length % 2 === 0 + ? formattedDisplayValue + : undefined + : formattedDisplayValue; + + const computedDisplayValueWidth = bboxCalculator + .compute(displayValueText || '', fontSize, fontFamily) + .getOrElse({ + width: 0, + height: 0, + }).width; + const displayValueWidth = + displayValueSettings && displayValueSettings.isValueContainedInElement + ? width + : computedDisplayValueWidth; + + const hideClippedValue = displayValueSettings + ? displayValueSettings.hideClippedValue : undefined; + const displayValue = + displayValueSettings && displayValueSettings.showValueLabel + ? { + text: displayValueText, + width: displayValueWidth, + height: fontSize || 0, + hideClippedValue, + isValueContainedInElement: displayValueSettings.isValueContainedInElement, + } + : undefined; + const barGeometry: BarGeometry = { displayValue, x, @@ -429,14 +456,14 @@ export function getGeometryStyle( specOpacity?: number, individualHighlight?: { [key: string]: boolean }, ): GeometryStyle { - - const sharedStyle = specOpacity == null ? - sharedThemeStyle : - { - ...sharedThemeStyle, - highlighted: { opacity: specOpacity }, - default: { opacity: specOpacity }, - }; + const sharedStyle = + specOpacity == null + ? sharedThemeStyle + : { + ...sharedThemeStyle, + highlighted: { opacity: specOpacity }, + default: { opacity: specOpacity }, + }; if (highlightedLegendItem != null) { const isPartOfHighlightedSeries = belongsToDataSeries(geometryId, highlightedLegendItem.value); diff --git a/src/lib/series/series.ts b/src/lib/series/series.ts index 720df2f573..6e9be8a935 100644 --- a/src/lib/series/series.ts +++ b/src/lib/series/series.ts @@ -1,9 +1,9 @@ -import { findDataSeriesByColorValues } from '../../state/utils'; import { ColorConfig } from '../themes/theme'; import { Accessor } from '../utils/accessor'; import { GroupId, SpecId } from '../utils/ids'; import { splitSpecsByGroupId, YBasicSeriesSpec } from './domains/y_domain'; import { formatNonStackedDataSeriesValues } from './nonstacked_series_utils'; +import { isEqualSeriesKey } from './series_utils'; import { BasicSeriesSpec, Datum, SeriesAccessors } from './specs'; import { formatStackedDataSeriesValues } from './stacked_series_utils'; @@ -64,6 +64,19 @@ export interface DataSeriesColorsValues { specSortIndex?: number; } +export function findDataSeriesByColorValues( + series: DataSeriesColorsValues[] | null, + value: DataSeriesColorsValues, +): number { + if (!series) { + return -1; + } + + return series.findIndex((item: DataSeriesColorsValues) => { + return isEqualSeriesKey(item.colorValues, value.colorValues) && item.specId === value.specId; + }); +} + /** * Split a dataset into multiple series, each having a key with the relative * series configuration diff --git a/src/lib/themes/colors.ts b/src/lib/themes/colors.ts index 9c57731a43..49c1da4df2 100644 --- a/src/lib/themes/colors.ts +++ b/src/lib/themes/colors.ts @@ -1,38 +1,52 @@ -import { Accessor } from '../utils/accessor'; -import { ColorDomain } from '../utils/domain'; -import { ColorConfig } from './theme'; - export interface ColorScales { [key: string]: string; } -export function computeColorScales( - colorDomain: ColorDomain, - chartColors: ColorConfig, -): ColorScales { - return colorDomain.domain.reduce( - (acc, domainKey, index) => { - acc[domainKey] = chartColors.vizColors[index % chartColors.vizColors.length]; - return acc; - }, - {} as ColorScales, - ); + +interface EchPalette { + colors: string[]; } -export type GetColorFn = (datum: any, yAccessor?: Accessor) => string; +const echPaletteColorBlind: EchPalette = { + colors: [ + '#1EA593', + '#2B70F7', + '#CE0060', + '#38007E', + '#FCA5D3', + '#F37020', + '#E49E29', + '#B0916F', + '#7B000B', + '#34130C', + ], +}; -export function getColor( - chartColors: ColorConfig, - colorScales: ColorScales, - colorAccessors: Accessor[], -): GetColorFn { - return (datum: any, yAccessor?: Accessor) => { - const key = getColorKey(datum, colorAccessors, yAccessor); - return colorScales[key] || chartColors.defaultVizColor; - }; -} +const echPaletteForLightBackground: EchPalette = { + colors: ['#006BB4', '#017D73', '#F5A700', '#BD271E', '#DD0A73'], +}; -export function getColorKey(datum: any, colorAccessors: Accessor[], yAccessor?: Accessor) { - return [...colorAccessors.map((accessor) => String(datum[accessor])), yAccessor] - .filter((value) => value) - .join('--'); -} +const echPaletteForDarkBackground: EchPalette = { + colors: ['#4DA1C0', '#01B2A4', '#C06C4C', '#BF4D4D', '#F5258C'], +}; + +const echPaletteForStatus: EchPalette = { + colors: [ + '#58BA6D', + '#6ECE67', + '#A5E26A', + '#D2E26A', + '#EBDF61', + '#EBD361', + '#EBC461', + '#D99D4C', + '#D97E4C', + '#D75949', + ], +}; + +export const palettes = { + echPaletteColorBlind, + echPaletteForLightBackground, + echPaletteForDarkBackground, + echPaletteForStatus, +}; diff --git a/src/lib/themes/dark_theme.ts b/src/lib/themes/dark_theme.ts index dc317dda5a..77d5cd9d79 100644 --- a/src/lib/themes/dark_theme.ts +++ b/src/lib/themes/dark_theme.ts @@ -1,4 +1,4 @@ -import { palettes } from '@elastic/eui'; +import { palettes } from './colors'; import { Theme } from './theme'; import { @@ -63,7 +63,7 @@ export const DARK_THEME: Theme = { displayValue: { fontSize: 10, fontStyle: 'normal', - fontFamily: `'Open Sans', Helvetica, Arial, sans-serif`, + fontFamily: 'sans-serif', padding: 0, fill: 'white', offsetX: 0, @@ -78,7 +78,7 @@ export const DARK_THEME: Theme = { axisTitleStyle: { fontSize: 12, fontStyle: 'bold', - fontFamily: `'Open Sans', Helvetica, Arial, sans-serif`, + fontFamily: 'sans-serif', padding: 5, fill: 'white', }, @@ -88,7 +88,7 @@ export const DARK_THEME: Theme = { }, tickLabelStyle: { fontSize: 10, - fontFamily: `'Open Sans', Helvetica, Arial, sans-serif`, + fontFamily: 'sans-serif', fontStyle: 'normal', fill: 'white', padding: 0, @@ -99,11 +99,11 @@ export const DARK_THEME: Theme = { }, }, colors: { - vizColors: palettes.euiPaletteColorBlind.colors, + vizColors: palettes.echPaletteColorBlind.colors, defaultVizColor: DEFAULT_MISSING_COLOR, }, legend: { - verticalWidth: 160, + verticalWidth: 200, horizontalHeight: 64, }, crosshair: { diff --git a/src/lib/themes/light_theme.ts b/src/lib/themes/light_theme.ts index c42baeafe5..61d7fd22af 100644 --- a/src/lib/themes/light_theme.ts +++ b/src/lib/themes/light_theme.ts @@ -1,4 +1,4 @@ -import { palettes } from '@elastic/eui'; +import { palettes } from './colors'; import { Theme } from './theme'; import { @@ -63,7 +63,7 @@ export const LIGHT_THEME: Theme = { displayValue: { fontSize: 10, fontStyle: 'normal', - fontFamily: `'Open Sans', Helvetica, Arial, sans-serif`, + fontFamily: 'sans-serif', padding: 0, fill: 'gray', offsetX: 0, @@ -78,7 +78,7 @@ export const LIGHT_THEME: Theme = { axisTitleStyle: { fontSize: 12, fontStyle: 'bold', - fontFamily: `'Open Sans', Helvetica, Arial, sans-serif`, + fontFamily: 'sans-serif', padding: 5, fill: 'gray', }, @@ -88,7 +88,7 @@ export const LIGHT_THEME: Theme = { }, tickLabelStyle: { fontSize: 10, - fontFamily: `'Open Sans', Helvetica, Arial, sans-serif`, + fontFamily: 'sans-serif', fontStyle: 'normal', fill: 'gray', padding: 0, @@ -99,11 +99,11 @@ export const LIGHT_THEME: Theme = { }, }, colors: { - vizColors: palettes.euiPaletteColorBlind.colors, + vizColors: palettes.echPaletteColorBlind.colors, defaultVizColor: DEFAULT_MISSING_COLOR, }, legend: { - verticalWidth: 160, + verticalWidth: 200, horizontalHeight: 64, }, crosshair: { diff --git a/src/reset_dark.scss b/src/reset_dark.scss new file mode 100644 index 0000000000..4b47d4719f --- /dev/null +++ b/src/reset_dark.scss @@ -0,0 +1,6 @@ +@import '../node_modules/@elastic/eui/src/themes/eui/eui_colors_dark'; +@import 'style/themes/colors_dark'; +@import '../node_modules/@elastic/eui/src/global_styling/functions/index'; +@import '../node_modules/@elastic/eui/src/global_styling/variables/index'; +@import '../node_modules/@elastic/eui/src/global_styling/mixins/index'; +@import '../node_modules/@elastic/eui/src/global_styling/reset/index'; diff --git a/src/reset_light.scss b/src/reset_light.scss new file mode 100644 index 0000000000..1a2912d866 --- /dev/null +++ b/src/reset_light.scss @@ -0,0 +1,6 @@ +@import '../node_modules/@elastic/eui/src/themes/eui/eui_colors_light'; +@import 'style/themes/colors_light'; +@import '../node_modules/@elastic/eui/src/global_styling/functions/index'; +@import '../node_modules/@elastic/eui/src/global_styling/variables/index'; +@import '../node_modules/@elastic/eui/src/global_styling/mixins/index'; +@import '../node_modules/@elastic/eui/src/global_styling/reset/index'; diff --git a/src/state/chart_state.test.ts b/src/state/chart_state.test.ts index 125ea37ea8..960356542c 100644 --- a/src/state/chart_state.test.ts +++ b/src/state/chart_state.test.ts @@ -196,17 +196,19 @@ describe('Chart Store', () => { store.onLegendItemClickListener = undefined; store.onLegendItemClick(firstLegendItem.key); - expect(store.selectedLegendItemKey.get()).toBe(firstLegendItem.key); + // TODO reenable this after re-configuring onLegendItemClick + // expect(store.selectedLegendItemKey.get()).toBe(firstLegendItem.key); expect(legendListener).not.toBeCalled(); store.setOnLegendItemClickListener(legendListener); store.onLegendItemClick(firstLegendItem.key); - expect(store.selectedLegendItemKey.get()).toBe(null); - expect(legendListener).toBeCalledWith(null); + // TODO reenable this after re-configuring onLegendItemClick + // expect(store.selectedLegendItemKey.get()).toBe(null); + // expect(legendListener).toBeCalledWith(null); - store.setOnLegendItemClickListener(legendListener); - store.onLegendItemClick(secondLegendItem.key); - expect(store.selectedLegendItemKey.get()).toBe(secondLegendItem.key); + // store.setOnLegendItemClickListener(legendListener); + // store.onLegendItemClick(secondLegendItem.key); + // expect(store.selectedLegendItemKey.get()).toBe(secondLegendItem.key); expect(legendListener).toBeCalledWith(secondLegendItem.value); }); @@ -498,9 +500,7 @@ describe('Chart Store', () => { annotationId: getAnnotationId('rect'), groupId: GROUP_ID, annotationType: 'rectangle', - dataValues: [ - { coordinates: { x0: 1, x1: 2, y0: 3, y1: 5 } }, - ], + dataValues: [{ coordinates: { x0: 1, x1: 2, y0: 3, y1: 5 } }], }; store.addAnnotationSpec(rectAnnotation); expectedAnnotationSpecs.clear(); @@ -761,21 +761,27 @@ describe('Chart Store', () => { annotationId: getAnnotationId('rect'), groupId: GROUP_ID, annotationType: 'rectangle', - dataValues: [ - { coordinates: { x0: 1, x1: 2, y0: 3, y1: 5 } }, - ], + dataValues: [{ coordinates: { x0: 1, x1: 2, y0: 3, y1: 5 } }], }; store.annotationSpecs.set(rectAnnotationSpec.annotationId, rectAnnotationSpec); store.annotationDimensions.set(rectAnnotationSpec.annotationId, annotationDimensions); const highlightedTooltipValue = { - name: 'foo', value: 1, color: 'color', - isHighlighted: true, isXValue: false, seriesKey: 'foo', + name: 'foo', + value: 1, + color: 'color', + isHighlighted: true, + isXValue: false, + seriesKey: 'foo', }; const unhighlightedTooltipValue = { - name: 'foo', value: 1, color: 'color', - isHighlighted: false, isXValue: false, seriesKey: 'foo', + name: 'foo', + value: 1, + color: 'color', + isHighlighted: false, + isXValue: false, + seriesKey: 'foo', }; const expectedRectTooltipState = { diff --git a/src/state/chart_state.ts b/src/state/chart_state.ts index 03ffa94669..932fb223cb 100644 --- a/src/state/chart_state.ts +++ b/src/state/chart_state.ts @@ -23,6 +23,7 @@ import { import { countBarsInCluster } from '../lib/series/scales'; import { DataSeriesColorsValues, + findDataSeriesByColorValues, FormattedDataSeries, getSeriesColorMap, RawDataSeries, @@ -44,7 +45,11 @@ import { } from '../lib/series/specs'; import { formatTooltip, getSeriesTooltipValues } from '../lib/series/tooltip'; import { LIGHT_THEME } from '../lib/themes/light_theme'; -import { mergeWithDefaultAnnotationLine, mergeWithDefaultAnnotationRect, Theme } from '../lib/themes/theme'; +import { + mergeWithDefaultAnnotationLine, + mergeWithDefaultAnnotationRect, + Theme, +} from '../lib/themes/theme'; import { compareByValueAsc } from '../lib/utils/commons'; import { computeChartDimensions, Dimensions } from '../lib/utils/dimensions'; import { Domain } from '../lib/utils/domain'; @@ -76,7 +81,6 @@ import { computeChartTransform, computeSeriesDomains, computeSeriesGeometries, - findDataSeriesByColorValues, getAxesSpecForSpecId, getUpdatedCustomSeriesColors, isChartAnimatable, @@ -311,15 +315,13 @@ export class ChartStore { ); Object.assign(this.cursorLinePosition, updatedCursorLine); - const updatedTooltipPosition = getTooltipPosition( + this.tooltipPosition.transform = getTooltipPosition( this.chartDimensions, this.chartRotation, this.cursorBandPosition, this.cursorPosition, ); - this.tooltipPosition.transform = updatedTooltipPosition.transform; - // get the elements on at this cursor position const elements = this.geometriesIndex.get(xValue); @@ -382,7 +384,8 @@ export class ChartStore { // if there's an annotation rect tooltip & there isn't a single highlighted element, hide const annotationTooltip = this.annotationTooltipState.get(); - const hasRectAnnotationToolip = annotationTooltip && annotationTooltip.annotationType === AnnotationTypes.Rectangle; + const hasRectAnnotationToolip = + annotationTooltip && annotationTooltip.annotationType === AnnotationTypes.Rectangle; if (hasRectAnnotationToolip && !oneHighlighted) { this.clearTooltipAndHighlighted(); return; @@ -527,14 +530,15 @@ export class ChartStore { }); onLegendItemClick = action((legendItemKey: string) => { - if (legendItemKey !== this.selectedLegendItemKey.get()) { - this.selectedLegendItemKey.set(legendItemKey); - } else { - this.selectedLegendItemKey.set(null); - } - + // Disabling the select until we implement the right contextual menu + // with extend possibility + // if (legendItemKey !== this.selectedLegendItemKey.get()) { + // this.selectedLegendItemKey.set(legendItemKey); + // } else { + // this.selectedLegendItemKey.set(null); + // } if (this.onLegendItemClickListener) { - const currentLegendItem = this.selectedLegendItem.get(); + const currentLegendItem = legendItemKey == null ? null : this.legendItems.get(legendItemKey); const listenerData = currentLegendItem ? currentLegendItem.value : null; this.onLegendItemClickListener(listenerData); } diff --git a/src/state/crosshair_utils.ts b/src/state/crosshair_utils.ts index 126c74d8ad..ec1c564429 100644 --- a/src/state/crosshair_utils.ts +++ b/src/state/crosshair_utils.ts @@ -122,9 +122,7 @@ export function getTooltipPosition( chartRotation: Rotation, cursorBandPosition: Dimensions, cursorPosition: { x: number; y: number }, -): { - transform: string; -} { +): string { const isHorizontalRotated = isHorizontalRotation(chartRotation); const hPosition = getHorizontalTooltipPosition( cursorPosition.x, @@ -140,9 +138,7 @@ export function getTooltipPosition( ); const xTranslation = `translateX(${hPosition.position}px) translateX(-${hPosition.offset}%)`; const yTranslation = `translateY(${vPosition.position}px) translateY(-${vPosition.offset}%)`; - return { - transform: `${xTranslation} ${yTranslation}`, - }; + return `${xTranslation} ${yTranslation}`; } export function getHorizontalTooltipPosition( diff --git a/src/state/utils.test.ts b/src/state/utils.test.ts index 7d72667d39..69822514d1 100644 --- a/src/state/utils.test.ts +++ b/src/state/utils.test.ts @@ -1,6 +1,6 @@ import { mergeDomainsByGroupId } from '../lib/axes/axis_utils'; import { IndexedGeometry } from '../lib/series/rendering'; -import { DataSeriesColorsValues, getSeriesColorMap } from '../lib/series/series'; +import { DataSeriesColorsValues, findDataSeriesByColorValues, getSeriesColorMap } from '../lib/series/series'; import { AreaSeriesSpec, AxisSpec, @@ -15,7 +15,6 @@ import { ScaleType } from '../lib/utils/scales/scales'; import { computeSeriesDomains, computeSeriesGeometries, - findDataSeriesByColorValues, getUpdatedCustomSeriesColors, isChartAnimatable, isHorizontalRotation, diff --git a/src/state/utils.ts b/src/state/utils.ts index 7881250332..ec62cb07ab 100644 --- a/src/state/utils.ts +++ b/src/state/utils.ts @@ -7,6 +7,7 @@ import { BarGeometry, IndexedGeometry, LineGeometry, + mutableIndexedGeometryMapUpsert, PointGeometry, renderArea, renderBars, @@ -16,12 +17,12 @@ import { computeXScale, computeYScales, countBarsInCluster } from '../lib/series import { DataSeries, DataSeriesColorsValues, + findDataSeriesByColorValues, FormattedDataSeries, getColorValuesAsString, getFormattedDataseries, getSplittedSeries, } from '../lib/series/series'; -import { isEqualSeriesKey } from '../lib/series/series_utils'; import { AreaSeriesSpec, AxisSpec, @@ -65,19 +66,6 @@ export interface GeometriesCounts { linePoints: number; } -export function findDataSeriesByColorValues( - series: DataSeriesColorsValues[] | null, - value: DataSeriesColorsValues, -): number { - if (!series) { - return -1; - } - - return series.findIndex((item: DataSeriesColorsValues) => { - return isEqualSeriesKey(item.colorValues, value.colorValues) && item.specId === value.specId; - }); -} - export function updateDeselectedDataSeries( series: DataSeriesColorsValues[] | null, value: DataSeriesColorsValues, @@ -532,20 +520,6 @@ export function mergeGeometriesIndexes(...iterables: Array, - key: any, - geometry: IndexedGeometry | IndexedGeometry[], -) { - const existing = mutableGeometriesIndex.get(key); - const upsertGeometry: IndexedGeometry[] = Array.isArray(geometry) ? geometry : [geometry]; - if (existing === undefined) { - mutableGeometriesIndex.set(key, upsertGeometry); - } else { - mutableGeometriesIndex.set(key, [...upsertGeometry, ...existing]); - } -} - export function isHorizontalRotation(chartRotation: Rotation) { return chartRotation === 0 || chartRotation === 180; } diff --git a/src/style/themes/_colors_dark.scss b/src/style/themes/_colors_dark.scss new file mode 100644 index 0000000000..3dcc60b9c0 --- /dev/null +++ b/src/style/themes/_colors_dark.scss @@ -0,0 +1,3 @@ +// This file contains no overwrites because ElasticChart uses the default EUI dark colors +// The file exists in case you ever need to write logic to flip imports in a build process. + diff --git a/src/style/themes/_colors_light.scss b/src/style/themes/_colors_light.scss new file mode 100644 index 0000000000..f7a091e117 --- /dev/null +++ b/src/style/themes/_colors_light.scss @@ -0,0 +1,2 @@ +// This file contains no overwrites because ElasticChart by default is light. +// The file exists in case you ever need to write logic to flip imports in a build process. diff --git a/src/theme_dark.scss b/src/theme_dark.scss new file mode 100644 index 0000000000..db6cac52c8 --- /dev/null +++ b/src/theme_dark.scss @@ -0,0 +1,12 @@ +@import '../node_modules/@elastic/eui/src/themes/eui/eui_colors_dark'; + +// These are Chart variable overwrites used only for this theme. +@import 'style/themes/colors_dark'; + +@import '../node_modules/@elastic/eui/src/global_styling/functions/index'; +@import '../node_modules/@elastic/eui/src/global_styling/variables/index'; +@import '../node_modules/@elastic/eui/src/global_styling/mixins/index'; +@import '../node_modules/@elastic/eui/src/global_styling/reset/index'; + +// Components +@import 'components/index'; diff --git a/src/theme_light.scss b/src/theme_light.scss new file mode 100644 index 0000000000..3da012e20f --- /dev/null +++ b/src/theme_light.scss @@ -0,0 +1,12 @@ +@import '../node_modules/@elastic/eui/src/themes/eui/eui_colors_light'; + +// This is the default Charts theme. +@import 'style/themes/colors_light'; + +@import '../node_modules/@elastic/eui/src/global_styling/functions/index'; +@import '../node_modules/@elastic/eui/src/global_styling/variables/index'; +@import '../node_modules/@elastic/eui/src/global_styling/mixins/index'; +@import '../node_modules/@elastic/eui/src/global_styling/reset/index'; + +// Components +@import 'components/index'; diff --git a/src/theme_only_dark.scss b/src/theme_only_dark.scss new file mode 100644 index 0000000000..10db704471 --- /dev/null +++ b/src/theme_only_dark.scss @@ -0,0 +1,11 @@ +@import '../node_modules/@elastic/eui/src/themes/eui/eui_colors_dark'; + +// These are Chart variable overwrites used only for this theme. +@import 'style/themes/colors_dark'; + +@import '../node_modules/@elastic/eui/src/global_styling/functions/index'; +@import '../node_modules/@elastic/eui/src/global_styling/variables/index'; +@import '../node_modules/@elastic/eui/src/global_styling/mixins/index'; + +// Components +@import 'components/index'; diff --git a/src/theme_only_light.scss b/src/theme_only_light.scss new file mode 100644 index 0000000000..28c6e71ad3 --- /dev/null +++ b/src/theme_only_light.scss @@ -0,0 +1,11 @@ +@import '../node_modules/@elastic/eui/src/themes/eui/eui_colors_light'; + +// This is the default Charts theme. +@import 'style/themes/colors_light'; + +@import '../node_modules/@elastic/eui/src/global_styling/functions/index'; +@import '../node_modules/@elastic/eui/src/global_styling/variables/index'; +@import '../node_modules/@elastic/eui/src/global_styling/mixins/index'; + +// Components +@import 'components/index'; diff --git a/src/utils/utils.ts b/src/utils/utils.ts new file mode 100644 index 0000000000..c556a11d05 --- /dev/null +++ b/src/utils/utils.ts @@ -0,0 +1,13 @@ +import * as uuid from 'uuid'; + +/** + * This function returns a function to generate ids. + * This can be used to generate unique, but predictable ids to pair labels + * with their inputs. It takes an optional prefix as a parameter. If you don't + * specify it, it generates a random id prefix. If you specify a custom prefix + * it should begin with an letter to be HTML4 compliant. + */ +export function htmlIdGenerator(idPrefix?: string) { + const prefix = idPrefix || `i${uuid.v1()}`; + return (suffix?: string) => `${prefix}_${suffix || uuid.v1()}`; +} diff --git a/stories/annotations.tsx b/stories/annotations.tsx index 22e05d8dbd..9fe7424679 100644 --- a/stories/annotations.tsx +++ b/stories/annotations.tsx @@ -1,4 +1,3 @@ -import { EuiIcon } from '@elastic/eui'; import { array, boolean, color, number, select } from '@storybook/addon-knobs'; import { storiesOf } from '@storybook/react'; import React, { CSSProperties } from 'react'; @@ -20,6 +19,7 @@ import { Settings, timeFormatter, } from '../src'; +import { Icon } from '../src/components/icons/icon'; import { KIBANA_METRICS } from '../src/lib/series/utils/test_dataset_kibana'; const dateFormatter = timeFormatter('HH:mm:ss'); @@ -78,7 +78,7 @@ storiesOf('Annotations', module) domainType={AnnotationDomainTypes.XDomain} dataValues={dataValues} style={style} - marker={} + marker={} /> @@ -114,7 +114,7 @@ storiesOf('Annotations', module) annotationId={getAnnotationId('anno_1')} domainType={AnnotationDomainTypes.XDomain} dataValues={dataValues} - marker={} + marker={} /> } + marker={} /> @@ -202,7 +202,7 @@ storiesOf('Annotations', module) annotationId={getAnnotationId('anno_1')} domainType={AnnotationDomainTypes.XDomain} dataValues={dataValues} - marker={} + marker={} /> ( - 'marker icon (examples from EUI)', + const marker = select<'alert' | 'eye' | 'questionInCircle'>( + 'marker icon (examples from internal Icon library)', { alert: 'alert', - asterisk: 'asterisk', + eye: 'eye', questionInCircle: 'questionInCircle', }, 'alert', @@ -277,7 +277,7 @@ storiesOf('Annotations', module) domainType={AnnotationDomainTypes.XDomain} dataValues={dataValues} style={style} - marker={} + marker={} hideLines={hideLines} hideTooltips={hideTooltips} /> @@ -295,15 +295,17 @@ storiesOf('Annotations', module) ); }) .add('[rect] basic annotation (bar)', () => { - const dataValues = [{ - coordinates: { - x0: 0, - x1: 1, - y0: 0, - y1: 7, + const dataValues = [ + { + coordinates: { + x0: 0, + x1: 1, + y0: 0, + y1: 7, + }, + details: 'details about this annotation', }, - details: 'details about this annotation', - }]; + ]; const chartRotation = select( 'chartRotation', @@ -320,11 +322,7 @@ storiesOf('Annotations', module) - + { - const definedCoordinate = select('defined coordinate', { - x0: 'x0', - x1: 'x1', - y0: 'y0', - y1: 'y1', - }, 'x0'); - - const dataValues = [{ - coordinates: { - x0: 1, - x1: 1.25, - y0: 0, - y1: 7, + const definedCoordinate = select( + 'defined coordinate', + { + x0: 'x0', + x1: 'x1', + y0: 'y0', + y1: 'y1', }, - details: 'details about this annotation', - }, { - coordinates: { - x0: 2.0, - x1: 2.1, - y0: 0, - y1: 7, + 'x0', + ); + + const dataValues = [ + { + coordinates: { + x0: 1, + x1: 1.25, + y0: 0, + y1: 7, + }, + details: 'details about this annotation', }, - details: 'details about this annotation', - }, { - coordinates: { - x0: definedCoordinate === 'x0' ? 0.25 : null, - x1: definedCoordinate === 'x1' ? 2.75 : null, - y0: definedCoordinate === 'y0' ? 0.25 : null, - y1: definedCoordinate === 'y1' ? 6.75 : null, + { + coordinates: { + x0: 2.0, + x1: 2.1, + y0: 0, + y1: 7, + }, + details: 'details about this annotation', }, - details: 'can have null values', - }]; + { + coordinates: { + x0: definedCoordinate === 'x0' ? 0.25 : null, + x1: definedCoordinate === 'x1' ? 2.75 : null, + y0: definedCoordinate === 'y0' ? 0.25 : null, + y1: definedCoordinate === 'y1' ? 6.75 : null, + }, + details: 'can have null values', + }, + ]; const chartRotation = select( 'chartRotation', @@ -393,15 +399,8 @@ storiesOf('Annotations', module) return ( - - + + { - const dataValues = [{ - coordinates: { - x0: 0, - x1: 0.25, - y0: 0, - y1: 7, + const dataValues = [ + { + coordinates: { + x0: 0, + x1: 0.25, + y0: 0, + y1: 7, + }, + details: 'annotation 1', }, - details: 'annotation 1', - }, { - coordinates: { - x0: -0.1, - x1: 0, - y0: 0, - y1: 7, + { + coordinates: { + x0: -0.1, + x1: 0, + y0: 0, + y1: 7, + }, + details: 'annotation 2', }, - details: 'annotation 2', - }, { - coordinates: { - x0: 1.1, - x1: 1.3, - y0: 0, - y1: 7, + { + coordinates: { + x0: 1.1, + x1: 1.3, + y0: 0, + y1: 7, + }, + details: 'annotation 2', }, - details: 'annotation 2', - }, { - coordinates: { - x0: 2.5, - x1: 3, - y0: 0, - y1: 7, + { + coordinates: { + x0: 2.5, + x1: 3, + y0: 0, + y1: 7, + }, + details: 'annotation 3', }, - details: 'annotation 3', - }]; + ]; const zIndex = number('annotation zIndex', 0); @@ -483,9 +487,13 @@ storiesOf('Annotations', module) overflowWrap: 'break-word', width: '120px', }; - const renderTooltip = hasCustomTooltip ? - (position: { transform: string; top: number; left: number; }, details?: string) => - (
{details}
) + const renderTooltip = hasCustomTooltip + ? (position: { transform: string; top: number; left: number }, details?: string) => ( +
+ + {details} +
+ ) : undefined; const isLeft = boolean('y-domain axis is Position.Left', true); @@ -506,11 +514,7 @@ storiesOf('Annotations', module) renderTooltip={renderTooltip} zIndex={zIndex} /> - + +
diff --git a/stories/interactions.tsx b/stories/interactions.tsx index 20d6aa2942..6094ae873c 100644 --- a/stories/interactions.tsx +++ b/stories/interactions.tsx @@ -247,7 +247,13 @@ storiesOf('Interactions', module) .add('click/hovers on legend items [area chart]', () => { return ( - + { return ( - + { return ( - +
{name} {value}{value}