diff --git a/.eslintrc.js b/.eslintrc.js index 94449bbb47..db6d98070b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -67,7 +67,7 @@ module.exports = { '@typescript-eslint/no-unsafe-return': 0, '@typescript-eslint/explicit-module-boundary-types': 0, '@typescript-eslint/restrict-plus-operands': 0, // rule is broken - '@typescript-eslint/no-unsafe-call': 1, + '@typescript-eslint/no-unsafe-call': 0, // seems to have issues with default import types '@typescript-eslint/unbound-method': 1, '@typescript-eslint/no-redeclare': 'off', // we use to declare enum type and object with the same name '@typescript-eslint/no-shadow': 'off', // we use shadow mostly within the canvas renderer function when we need a new context diff --git a/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-multiple-wordcloud-template-1-snap.png b/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-multiple-wordcloud-template-1-snap.png new file mode 100644 index 0000000000..c3c66703d5 Binary files /dev/null and b/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-multiple-wordcloud-template-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-right-angled-wordcloud-template-1-snap.png b/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-right-angled-wordcloud-template-1-snap.png new file mode 100644 index 0000000000..a67c969586 Binary files /dev/null and b/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-right-angled-wordcloud-template-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-single-wordcloud-template-1-snap.png b/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-single-wordcloud-template-1-snap.png new file mode 100644 index 0000000000..fa21e5635d Binary files /dev/null and b/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-single-wordcloud-template-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-small-waves-wordcloud-template-1-snap.png b/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-small-waves-wordcloud-template-1-snap.png new file mode 100644 index 0000000000..991c02b9cc Binary files /dev/null and b/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-small-waves-wordcloud-template-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-sparse-wordcloud-template-1-snap.png b/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-sparse-wordcloud-template-1-snap.png new file mode 100644 index 0000000000..85541139f7 Binary files /dev/null and b/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-sparse-wordcloud-template-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-square-words-wordcloud-template-1-snap.png b/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-square-words-wordcloud-template-1-snap.png new file mode 100644 index 0000000000..2efb2e1cac Binary files /dev/null and b/integration/tests/__image_snapshots__/wordcloud-stories-test-ts-scales-stories-should-render-square-words-wordcloud-template-1-snap.png differ diff --git a/integration/tests/wordcloud_stories.test.ts b/integration/tests/wordcloud_stories.test.ts new file mode 100644 index 0000000000..8f4ae9df3e --- /dev/null +++ b/integration/tests/wordcloud_stories.test.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { TEMPLATES } from '../../storybook/stories/wordcloud/1_wordcloud.story'; +import { common } from '../page_objects'; + +describe('Scales stories', () => { + it.each(TEMPLATES.filter((t) => t !== 'edit'))('should render %s wordcloud template', async (template) => { + await common.expectChartAtUrlToMatchScreenshot( + `http://localhost:9001/?path=/story/wordcloud-alpha--simple-wordcloud&knob-template=${template}`, + ); + }); +}); diff --git a/package.json b/package.json index 6fe17e1972..a2f9f507d9 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "@types/color": "^3.0.1", "@types/core-js": "^2.5.2", "@types/d3-array": "^1.2.6", + "@types/d3-cloud": "^1.2.5", "@types/d3-collection": "^1.0.8", "@types/d3-interpolate": "^1.3.1", "@types/d3-scale": "^2.1.1", diff --git a/packages/charts/api/charts.api.md b/packages/charts/api/charts.api.md index 4ad1580f16..6dc82ad4b5 100644 --- a/packages/charts/api/charts.api.md +++ b/packages/charts/api/charts.api.md @@ -2300,49 +2300,21 @@ export type WeightFn = $Values; export const Wordcloud: React_2.FunctionComponent; // @public (undocumented) -export interface WordcloudConfigs { - // (undocumented) - count: number; - // (undocumented) - endAngle: number; - // (undocumented) - exponent: number; - // (undocumented) - fontFamily: string; - // (undocumented) - fontStyle: FontStyle; - // (undocumented) - fontWeight: number; - // (undocumented) - height: number; - // (undocumented) - maxFontSize: number; - // (undocumented) - minFontSize: number; - // (undocumented) - padding: number; - // (undocumented) - spiral: string; - // (undocumented) - startAngle: number; +export type WordCloudElementEvent = [WordModel, SeriesIdentifier]; + +// @alpha (undocumented) +export interface WordcloudSpec extends Spec, WordcloudViewModel { // (undocumented) - weightFn: WeightFn; + chartType: typeof ChartType.Wordcloud; // (undocumented) - width: number; + specType: typeof SpecType.Series; } // @public (undocumented) -export type WordCloudElementEvent = [WordModel, SeriesIdentifier]; - -// @alpha (undocumented) -export interface WordcloudSpec extends Spec { +export interface WordcloudViewModel { // (undocumented) angleCount: number; // (undocumented) - chartType: typeof ChartType.Wordcloud; - // (undocumented) - config: RecursivePartial; - // (undocumented) data: WordModel[]; // (undocumented) endAngle: number; @@ -2363,8 +2335,6 @@ export interface WordcloudSpec extends Spec { // (undocumented) padding: number; // (undocumented) - specType: typeof SpecType.Series; - // (undocumented) spiral: string; // (undocumented) startAngle: number; diff --git a/packages/charts/src/chart_types/wordcloud/layout/config/config.ts b/packages/charts/src/chart_types/wordcloud/layout/config/config.ts deleted file mode 100644 index a039866bae..0000000000 --- a/packages/charts/src/chart_types/wordcloud/layout/config/config.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { ConfigItem, configMap } from '../../../../common/config_objects'; -import { Config } from '../types/config_types'; - -/** @internal */ -export const configMetadata: Record = { - // shape geometry - width: { dflt: 300, min: 0, max: 1024, type: 'number', reconfigurable: false }, - height: { dflt: 150, min: 0, max: 1024, type: 'number', reconfigurable: false }, - margin: { - type: 'group', - values: { - left: { dflt: 0, min: -0.25, max: 0.25, type: 'number' }, - right: { dflt: 0, min: -0.25, max: 0.25, type: 'number' }, - top: { dflt: 0, min: -0.25, max: 0.25, type: 'number' }, - bottom: { dflt: 0, min: -0.25, max: 0.25, type: 'number' }, - }, - }, - - // general text config - fontFamily: { - dflt: 'Impact', - type: 'string', - }, - - // fill text config - minFontSize: { dflt: 10, min: 10, max: 50, type: 'number', reconfigurable: true }, - maxFontSize: { dflt: 70, min: 15, max: 150, type: 'number', reconfigurable: true }, - - backgroundColor: { dflt: '#ffffff', type: 'color' }, - sectorLineWidth: { dflt: 1, min: 0, max: 4, type: 'number' }, -}; - -/** @internal */ -export const config: Config = configMap((item: ConfigItem) => item.dflt, configMetadata); diff --git a/packages/charts/src/chart_types/wordcloud/layout/types/config_types.ts b/packages/charts/src/chart_types/wordcloud/layout/types/config_types.ts deleted file mode 100644 index 5092db5187..0000000000 --- a/packages/charts/src/chart_types/wordcloud/layout/types/config_types.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { Pixels, SizeRatio } from '../../../../common/geometry'; -import { FontFamily } from '../../../../common/text_utils'; - -// todo switch to `io-ts` style, generic way of combining static and runtime type info -/** @public */ -export interface Config { - // shape geometry - width: number; - height: number; - margin: { left: SizeRatio; right: SizeRatio; top: SizeRatio; bottom: SizeRatio }; - - // general text config - fontFamily: FontFamily; - - // fill text config - minFontSize: Pixels; - maxFontSize: Pixels; -} diff --git a/packages/charts/src/chart_types/wordcloud/layout/types/viewmodel_types.ts b/packages/charts/src/chart_types/wordcloud/layout/types/viewmodel_types.ts index d6f49ce488..ab2f982549 100644 --- a/packages/charts/src/chart_types/wordcloud/layout/types/viewmodel_types.ts +++ b/packages/charts/src/chart_types/wordcloud/layout/types/viewmodel_types.ts @@ -6,13 +6,12 @@ * Side Public License, v 1. */ +import { Word as D3Word } from 'd3-cloud'; import { $Values as Values } from 'utility-types'; import { Color } from '../../../../common/colors'; -import { Pixels, PointObject, Rectangle } from '../../../../common/geometry'; +import { Pixels, PointObject } from '../../../../common/geometry'; import { FontStyle } from '../../../../common/text_utils'; -import { config } from '../config/config'; -import { Config } from './config_types'; /** @public */ export interface WordModel { @@ -32,49 +31,26 @@ export const WeightFn = Object.freeze({ /** @public */ export type WeightFn = Values; -/** @internal */ -export interface Word extends Rectangle { +/** + * Word properties extends `D3Word` except for explicitly defined values. + * + * `D3Word` values are added datum via d3TagCloud, but may be undefined + * @internal + */ +export interface Word extends D3Word { + datum: WordModel; + text: string; color: string; - font: string; fontFamily: string; - fontWeight: number; - hasText: boolean; - height: number; - padding: number; - rotate: number; - size: number; style: string; - text: string; - weight: number; - x: number; - xoff: number; - y: number; - yoff: number; - datum: WordModel; -} - -/** @public */ -export interface Configs { - count: number; - endAngle: number; - exponent: number; - fontFamily: string; - fontStyle: FontStyle; fontWeight: number; - height: number; - maxFontSize: number; - minFontSize: number; - padding: number; - spiral: string; - startAngle: number; - weightFn: WeightFn; - width: number; + size: number; } /** @public */ export type OutOfRoomCallback = (wordCount: number, renderedWordCount: number, renderedWords: string[]) => void; -/** @internal */ +/** @public */ export interface WordcloudViewModel { startAngle: number; endAngle: number; @@ -90,7 +66,6 @@ export interface WordcloudViewModel { data: WordModel[]; weightFn: WeightFn; outOfRoomCallback: OutOfRoomCallback; - // specType: string; } /** @internal */ @@ -105,7 +80,6 @@ export type PickFunction = (x: Pixels, y: Pixels) => Array; /** @internal */ export type ShapeViewModel = { - config: Config; wordcloudViewModel: WordcloudViewModel; chartCenter: PointObject; pickQuads: PickFunction; @@ -141,10 +115,9 @@ export const nullWordcloudViewModel: WordcloudViewModel = { }; /** @internal */ -export const nullShapeViewModel = (specifiedConfig?: Config, chartCenter?: PointObject): ShapeViewModel => ({ - config: specifiedConfig || config, +export const nullShapeViewModel = (): ShapeViewModel => ({ wordcloudViewModel: nullWordcloudViewModel, - chartCenter: chartCenter || { x: 0, y: 0 }, + chartCenter: { x: 0, y: 0 }, pickQuads: () => [], specId: 'empty', }); diff --git a/packages/charts/src/chart_types/wordcloud/layout/viewmodel/viewmodel.ts b/packages/charts/src/chart_types/wordcloud/layout/viewmodel/viewmodel.ts index a22047095d..1dffefb454 100644 --- a/packages/charts/src/chart_types/wordcloud/layout/viewmodel/viewmodel.ts +++ b/packages/charts/src/chart_types/wordcloud/layout/viewmodel/viewmodel.ts @@ -6,16 +6,17 @@ * Side Public License, v 1. */ +import { Dimensions } from '../../../../utils/dimensions'; +import { Theme } from '../../../../utils/themes/theme'; import { WordcloudSpec } from '../../specs'; -import { Config } from '../types/config_types'; import { WordcloudViewModel, PickFunction, ShapeViewModel } from '../types/viewmodel_types'; /** @internal */ -export function shapeViewModel(spec: WordcloudSpec, config: Config): ShapeViewModel { - const { width, height, margin } = config; - - const innerWidth = width * (1 - Math.min(1, margin.left + margin.right)); - const innerHeight = height * (1 - Math.min(1, margin.top + margin.bottom)); +export function shapeViewModel(spec: WordcloudSpec, theme: Theme, chartDimensions: Dimensions): ShapeViewModel { + const { width, height } = chartDimensions; + const { chartMargins: margin } = theme; + const innerWidth = width - margin.left - margin.right; + const innerHeight = height - margin.top - margin.bottom; const chartCenter = { x: width * margin.left + innerWidth / 2, @@ -64,7 +65,6 @@ export function shapeViewModel(spec: WordcloudSpec, config: Config): ShapeViewMo // combined viewModel return { - config, chartCenter, wordcloudViewModel, pickQuads, diff --git a/packages/charts/src/chart_types/wordcloud/renderer/svg/connected_component.tsx b/packages/charts/src/chart_types/wordcloud/renderer/svg/connected_component.tsx index 742fbe2985..2eec29fa65 100644 --- a/packages/charts/src/chart_types/wordcloud/renderer/svg/connected_component.tsx +++ b/packages/charts/src/chart_types/wordcloud/renderer/svg/connected_component.tsx @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -// @ts-ignore - remove in workcloud refactor import d3TagCloud from 'd3-cloud'; import React from 'react'; import { connect } from 'react-redux'; @@ -23,14 +22,10 @@ import { } from '../../../../state/selectors/get_accessibility_config'; import { getInternalIsInitializedSelector, InitStatus } from '../../../../state/selectors/get_internal_is_intialized'; import { getSettingsSpecSelector } from '../../../../state/selectors/get_settings_specs'; -import { Dimensions } from '../../../../utils/dimensions'; -import { Configs, Datum, nullShapeViewModel, ShapeViewModel, Word } from '../../layout/types/viewmodel_types'; +import { Size } from '../../../../utils/dimensions'; +import { nullShapeViewModel, ShapeViewModel, Word, WordcloudViewModel } from '../../layout/types/viewmodel_types'; import { geometries } from '../../state/selectors/geometries'; -function seed() { - return 0.5; -} - function getFont(d: Word) { return d.fontFamily; } @@ -43,14 +38,6 @@ function getFontWeight(d: Word) { return d.fontWeight; } -function getWidth(conf: Configs) { - return conf.width ?? 500; -} - -function getHeight(conf: Configs) { - return conf.height ?? 500; -} - function getFontSize(d: Word) { return d.size; } @@ -91,40 +78,42 @@ function log(minFontSize: number, maxFontSize: number, _exponent: number, weight const weightFnLookup = { linear, exponential, log, squareRoot }; -function layoutMaker(config: Configs, data: Datum[]) { - const words = data.map((d) => { - const weightFn = weightFnLookup[config.weightFn]; +function layoutMaker({ data, ...viewModel }: WordcloudViewModel, chartSize: Size) { + const { height, width } = chartSize; + const words = data.map((d) => { + const weightFn = weightFnLookup[viewModel.weightFn]; return { datum: d, text: d.text, color: d.color, - fontFamily: config.fontFamily, - style: config.fontStyle, - fontWeight: config.fontWeight, - size: weightFn(config.minFontSize, config.maxFontSize, config.exponent, d.weight), + fontFamily: viewModel.fontFamily, + style: viewModel.fontStyle, + fontWeight: viewModel.fontWeight, + size: weightFn(viewModel.minFontSize, viewModel.maxFontSize, viewModel.exponent, d.weight), }; }); - return d3TagCloud() - .random(seed) - .size([getWidth(config), getHeight(config)]) + + return d3TagCloud() + .random(() => 0.5) + .size([width, height]) .words(words) - .spiral(config.spiral ?? 'archimedean') - .padding(config.padding ?? 5) - .rotate((d: Word) => getRotation(config.startAngle, config.endAngle, config.count, d.text)) + .spiral(viewModel.spiral ?? 'archimedean') + .padding(viewModel.padding ?? 5) + .rotate((d) => getRotation(viewModel.startAngle, viewModel.endAngle, viewModel.angleCount, d.text)) .font(getFont) .fontStyle(getFontStyle) .fontWeight(getFontWeight) - .fontSize((d: Word) => getFontSize(d)); + .fontSize(getFontSize); } const View = ({ words, - conf, + size: { height, width }, actions: { onElementClick, onElementOver, onElementOut }, specId, }: { words: Word[]; - conf: Configs; + size: Size; actions: { onElementClick?: SettingsSpec['onElementClick']; onElementOver?: SettingsSpec['onElementOver']; @@ -133,8 +122,8 @@ const View = ({ specId: string; }) => { return ( - - + + {words.map((d, i) => { const elements: WordCloudElementEvent[] = [[d.datum, { specId, key: specId }]]; const actions = { @@ -180,7 +169,7 @@ const View = ({ interface ReactiveChartStateProps { initialized: boolean; geometries: ShapeViewModel; - chartContainerDimensions: Dimensions; + chartSize: Size; a11ySettings: A11ySettings; onElementClick?: SettingsSpec['onElementClick']; onElementOver?: SettingsSpec['onElementOver']; @@ -211,40 +200,24 @@ class Component extends React.Component { render() { const { initialized, - chartContainerDimensions: { width, height }, + chartSize, geometries: { wordcloudViewModel, specId }, a11ySettings, onElementClick, onElementOver, onElementOut, } = this.props; - if (!initialized || width === 0 || height === 0) { + + if (!initialized || chartSize.width === 0 || chartSize.height === 0) { return null; } - const conf1: Configs = { - width, - height, - startAngle: wordcloudViewModel.startAngle, - endAngle: wordcloudViewModel.endAngle, - count: wordcloudViewModel.angleCount, - padding: wordcloudViewModel.padding, - fontWeight: wordcloudViewModel.fontWeight, - fontFamily: wordcloudViewModel.fontFamily, - fontStyle: wordcloudViewModel.fontStyle, - minFontSize: wordcloudViewModel.minFontSize, - maxFontSize: wordcloudViewModel.maxFontSize, - spiral: wordcloudViewModel.spiral, - exponent: wordcloudViewModel.exponent, - weightFn: wordcloudViewModel.weightFn, - }; - const layout = layoutMaker(conf1, wordcloudViewModel.data); + const layout = layoutMaker(wordcloudViewModel, chartSize); - let ww; - layout.on('end', (w: Word[]) => (ww = w)).start(); + let renderedWordObjects: Word[] = []; + layout.on('end', (w) => (renderedWordObjects = w)).start(); const wordCount = wordcloudViewModel.data.length; - const renderedWordObjects = (ww as unknown) as Word[]; const renderedWordCount: number = renderedWordObjects.length; const notAllWordsFit = wordCount !== renderedWordCount; if (notAllWordsFit && wordcloudViewModel.outOfRoomCallback instanceof Function) { @@ -259,7 +232,7 @@ class Component extends React.Component {
@@ -280,11 +253,9 @@ const mapDispatchToProps = (dispatch: Dispatch): ReactiveChartDispatchProps => const DEFAULT_PROPS: ReactiveChartStateProps = { initialized: false, geometries: nullShapeViewModel(), - chartContainerDimensions: { + chartSize: { width: 0, height: 0, - left: 0, - top: 0, }, a11ySettings: DEFAULT_A11Y_SETTINGS, }; @@ -296,7 +267,7 @@ const mapStateToProps = (state: GlobalChartState): ReactiveChartStateProps => { return { initialized: true, geometries: geometries(state), - chartContainerDimensions: state.parentDimensions, + chartSize: state.parentDimensions, a11ySettings: getA11ySettingsSelector(state), onElementClick: getSettingsSpecSelector(state).onElementClick, onElementOver: getSettingsSpecSelector(state).onElementOver, diff --git a/packages/charts/src/chart_types/wordcloud/specs/index.ts b/packages/charts/src/chart_types/wordcloud/specs/index.ts index beeb51028d..42e286c526 100644 --- a/packages/charts/src/chart_types/wordcloud/specs/index.ts +++ b/packages/charts/src/chart_types/wordcloud/specs/index.ts @@ -9,48 +9,29 @@ import React from 'react'; import { ChartType } from '../..'; -import { FontStyle } from '../../../common/text_utils'; import { Spec } from '../../../specs'; import { SpecType } from '../../../specs/constants'; import { getConnect, specComponentFactory } from '../../../state/spec_factory'; -import { RecursivePartial } from '../../../utils/common'; -import { config } from '../layout/config/config'; import { WordModel, defaultWordcloudSpec, WeightFn, OutOfRoomCallback, - Configs as WordcloudConfigs, + WordcloudViewModel, } from '../layout/types/viewmodel_types'; const defaultProps = { chartType: ChartType.Wordcloud, specType: SpecType.Series, ...defaultWordcloudSpec, - config, }; -export { WordModel, WeightFn, OutOfRoomCallback, WordcloudConfigs }; +export { WordModel, WeightFn, OutOfRoomCallback }; /** @alpha */ -export interface WordcloudSpec extends Spec { +export interface WordcloudSpec extends Spec, WordcloudViewModel { specType: typeof SpecType.Series; chartType: typeof ChartType.Wordcloud; - config: RecursivePartial; - startAngle: number; - endAngle: number; - angleCount: number; - padding: number; - fontWeight: number; - fontFamily: string; - fontStyle: FontStyle; - minFontSize: number; - maxFontSize: number; - spiral: string; - exponent: number; - data: WordModel[]; - weightFn: WeightFn; - outOfRoomCallback: OutOfRoomCallback; } type SpecRequiredProps = Pick; @@ -62,7 +43,6 @@ export const Wordcloud: React.FunctionComponent state.parentDimensions; /** @internal */ export const geometries = createCustomCachedSelector( - [getSpecs, getParentDimensions], - (specs, parentDimensions): ShapeViewModel => { + [getSpecs, getChartThemeSelector, getParentDimensions], + (specs, theme, parentDimensions): ShapeViewModel => { const wordcloudSpecs = getSpecsFromStore(specs, ChartType.Wordcloud, SpecType.Series); - return wordcloudSpecs.length === 1 ? render(wordcloudSpecs[0], parentDimensions) : nullShapeViewModel(); + return wordcloudSpecs.length === 1 ? render(wordcloudSpecs[0], theme, parentDimensions) : nullShapeViewModel(); }, ); diff --git a/packages/charts/src/chart_types/wordcloud/state/selectors/scenegraph.ts b/packages/charts/src/chart_types/wordcloud/state/selectors/scenegraph.ts index 92fcc57c8b..3618b418d7 100644 --- a/packages/charts/src/chart_types/wordcloud/state/selectors/scenegraph.ts +++ b/packages/charts/src/chart_types/wordcloud/state/selectors/scenegraph.ts @@ -6,19 +6,13 @@ * Side Public License, v 1. */ -import { mergePartial, RecursivePartial } from '../../../../utils/common'; import { Dimensions } from '../../../../utils/dimensions'; -import { config as defaultConfig } from '../../layout/config/config'; -import { Config } from '../../layout/types/config_types'; +import { Theme } from '../../../../utils/themes/theme'; import { ShapeViewModel } from '../../layout/types/viewmodel_types'; import { shapeViewModel } from '../../layout/viewmodel/viewmodel'; import { WordcloudSpec } from '../../specs'; /** @internal */ -export function render(spec: WordcloudSpec, parentDimensions: Dimensions): ShapeViewModel { - const { width, height } = parentDimensions; - const { config } = spec; - const partialConfig: RecursivePartial = { ...config, width, height }; - const cfg: Config = mergePartial(defaultConfig, partialConfig, { mergeOptionalPartialValues: true }); - return shapeViewModel(spec, cfg); +export function render(spec: WordcloudSpec, theme: Theme, parentDimensions: Dimensions): ShapeViewModel { + return shapeViewModel(spec, theme, parentDimensions); } diff --git a/storybook/stories/wordcloud/1_wordcloud.story.tsx b/storybook/stories/wordcloud/1_wordcloud.story.tsx index 68168d94de..83958a339f 100644 --- a/storybook/stories/wordcloud/1_wordcloud.story.tsx +++ b/storybook/stories/wordcloud/1_wordcloud.story.tsx @@ -10,7 +10,7 @@ import { action } from '@storybook/addon-actions'; import { color, number, select } from '@storybook/addon-knobs'; import React from 'react'; -import { Chart, Settings, Wordcloud, FontStyle } from '@elastic/charts'; +import { Chart, Settings, Wordcloud, FontStyle, WordcloudSpec, Color } from '@elastic/charts'; import { WeightFn, WordModel } from '@elastic/charts/src/chart_types/wordcloud/layout/types/viewmodel_types'; import { getRandomNumberGenerator } from '@elastic/charts/src/mocks/utils'; import { palettes as euiPalettes } from '@elastic/charts/src/utils/themes/colors'; @@ -54,119 +54,159 @@ const palettes = { }, }; -const configs = { - edit: { - startAngle: -90, - endAngle: 90, - angleCount: 16, - padding: 0.5, - exponent: 15, - fontWeight: 900, - minFontSize: 15, - maxFontSize: 80, - fontFamily: 'Arial', - fontStyle: 'italic', - shape: 'archimedean', - palette: 'turquoise', - backgroundColor: '#1c1c24', - weightFn: WeightFn.exponential, - }, - single: { - startAngle: 0, - endAngle: 0, - angleCount: 1, - padding: 1, - exponent: 4, - fontWeight: 900, - minFontSize: 14, - maxFontSize: 92, - fontFamily: 'Arial', - fontStyle: 'normal', - shape: 'rectangular', - palette: 'greyScale', - backgroundColor: '#9fa714', - weightFn: WeightFn.exponential, - }, - rightAngled: { - startAngle: 0, - endAngle: 90, - angleCount: 2, - padding: 1, - exponent: 4, - fontWeight: 600, - minFontSize: 14, - maxFontSize: 92, - fontFamily: 'Arial Narrow', - fontStyle: 'normal', - shape: 'rectangular', - palette: 'euiLight', - backgroundColor: '#ffffff', - weightFn: WeightFn.exponential, - }, - multiple: { - startAngle: -90, - endAngle: 90, - angleCount: 16, - padding: 1, - exponent: 15, - fontWeight: 100, - minFontSize: 16, - maxFontSize: 50, - fontFamily: 'Luminari', - fontStyle: 'italic', - shape: 'archimedean', - palette: 'redBlue', - backgroundColor: '#1c1c24', - weightFn: WeightFn.exponential, - }, - squareWords: { - startAngle: -45, - endAngle: 45, - angleCount: 2, - padding: 0, - exponent: 3, - fontWeight: 100, - minFontSize: 10, - maxFontSize: 90, - fontFamily: 'Arial Narrow', - fontStyle: 'normal', - shape: 'archimedean', - palette: 'weight', - backgroundColor: '#4a6960', - weightFn: WeightFn.exponential, - }, - smallWaves: { - startAngle: -15, - endAngle: 15, - angleCount: 7, - padding: 0.5, - exponent: 5, - fontWeight: 600, - minFontSize: 17, - maxFontSize: 79, - fontFamily: 'Impact', - fontStyle: 'normal', - shape: 'rectangular', - palette: 'euiColorBlind', - backgroundColor: '#ffffff', - weightFn: WeightFn.exponential, - }, - sparse: { - startAngle: 0, - endAngle: 0, - angleCount: 1, - padding: getRandomNumber(2, 22), - exponent: 15, - fontWeight: 600, - minFontSize: 12, - maxFontSize: 60, - fontFamily: 'Courier', - fontStyle: 'normal', - shape: 'rectangular', - palette: 'vivid', - backgroundColor: '#1c1c24', - weightFn: WeightFn.exponential, - }, +type WordcloudKnobs = Omit & { + palette: keyof typeof palettes; + backgroundColor: Color; +}; + +// Used in integration testing +export const TEMPLATES = ['edit', 'single', 'rightAngled', 'multiple', 'squareWords', 'smallWaves', 'sparse']; +const getTemplate = (name: string): WordcloudKnobs => { + switch (name) { + case 'single': + return { + spiral: 'rectangular', + startAngle: 0, + endAngle: 0, + angleCount: 1, + padding: 1, + exponent: 4, + fontWeight: 900, + minFontSize: 14, + maxFontSize: 92, + fontFamily: 'Arial', + fontStyle: 'normal', + palette: 'greyScale', + weightFn: WeightFn.exponential, + backgroundColor: '#9fa714', + }; + case 'rightAngled': + return { + spiral: 'rectangular', + startAngle: 0, + endAngle: 90, + angleCount: 2, + padding: 1, + exponent: 4, + fontWeight: 600, + minFontSize: 14, + maxFontSize: 92, + fontFamily: 'Arial Narrow', + fontStyle: 'normal', + palette: 'euiLight', + weightFn: WeightFn.exponential, + backgroundColor: '#ffffff', + }; + case 'multiple': + return { + spiral: 'archimedean', + startAngle: -90, + endAngle: 90, + angleCount: 16, + padding: 1, + exponent: 15, + fontWeight: 100, + minFontSize: 16, + maxFontSize: 50, + fontFamily: 'Luminari', + fontStyle: 'italic', + palette: 'redBlue', + weightFn: WeightFn.exponential, + backgroundColor: '#1c1c24', + }; + case 'squareWords': + return { + spiral: 'archimedean', + startAngle: -45, + endAngle: 45, + angleCount: 2, + padding: 0, + exponent: 3, + fontWeight: 100, + minFontSize: 10, + maxFontSize: 90, + fontFamily: 'Arial Narrow', + fontStyle: 'normal', + palette: 'weight', + weightFn: WeightFn.exponential, + backgroundColor: '#4a6960', + }; + case 'smallWaves': + return { + spiral: 'rectangular', + startAngle: -15, + endAngle: 15, + angleCount: 7, + padding: 0.5, + exponent: 5, + fontWeight: 600, + minFontSize: 17, + maxFontSize: 79, + fontFamily: 'Impact', + fontStyle: 'normal', + palette: 'euiColorBlind', + weightFn: WeightFn.exponential, + backgroundColor: '#ffffff', + }; + case 'sparse': + return { + spiral: 'rectangular', + startAngle: 0, + endAngle: 0, + angleCount: 1, + padding: getRandomNumber(2, 22), + exponent: 15, + fontWeight: 600, + minFontSize: 12, + maxFontSize: 60, + fontFamily: 'Courier', + fontStyle: 'normal', + palette: 'vivid', + weightFn: WeightFn.exponential, + backgroundColor: '#1c1c24', + }; + case 'edit': + default: + return { + spiral: select('shape', { oval: 'archimedean', rectangular: 'rectangular' }, 'archimedean'), + startAngle: number('startAngle', -90, { range: true, min: -360, max: 360, step: 1 }), + endAngle: number('endAngle', 90, { range: true, min: -360, max: 360, step: 1 }), + angleCount: number('angleCount', 16, { range: true, min: 2, max: 360, step: 1 }), + padding: number('padding', 0.5, { range: true, min: 0, max: 10, step: 0.5 }), + exponent: number('exponent', 15, { range: true, min: 0, max: 15, step: 1 }), + fontWeight: number('fontWeight', 900, { range: true, min: 100, max: 900, step: 100 }), + minFontSize: number('minFontSize', 15, { range: true, min: 6, max: 85, step: 1 }), + maxFontSize: number('maxFontSize', 80, { range: true, min: 15, max: 150, step: 1 }), + fontFamily: select( + 'fontFamily', + { + Arial: 'Arial', + 'Arial Narrow': 'Arial Narrow', + Courier: 'Courier', + Impact: 'Impact', + Luminari: 'Luminari', + }, + 'Arial', + ), + fontStyle: select('fontStyle', { normal: 'normal', italic: 'italic' }, 'italic'), + palette: select( + 'palette', + Object.keys(palettes).reduce((p, k) => ({ ...p, [k]: k }), {}), + 'turquoise', + ), + weightFn: select( + 'weightFn', + { + linear: WeightFn.linear, + exponential: WeightFn.exponential, + squareRoot: WeightFn.squareRoot, + log: WeightFn.log, + }, + WeightFn.exponential, + ), + backgroundColor: color('background', '#1c1c24'), + }; + } }; const rawData = text @@ -186,7 +226,7 @@ interface RawDatum { weight: number; } -function sampleData(txt: string, paletteName: keyof typeof palettes): WordModel[] { +function sampleData(paletteName: keyof typeof palettes): WordModel[] { return rawData.map(function rawMapper(d, i) { return { ...d, @@ -197,76 +237,11 @@ function sampleData(txt: string, paletteName: keyof typeof palettes): WordModel[ export const Example = () => { const configName = select( - 'config', - Object.keys(configs).reduce((p, k) => ({ ...p, [k]: k }), {}), + 'template', + TEMPLATES.reduce((p, k) => ({ ...p, [k]: k }), {}), 'edit', ); - const startConfig = configs[configName]; - const template = configName !== 'edit'; - const spiral = template - ? startConfig.shape - : select('shape', { oval: 'archimedean', rectangular: 'rectangular' }, startConfig.shape); - const backgroundColor = template ? startConfig.backgroundColor : color('background', startConfig.backgroundColor); - const startAngle = template - ? startConfig.startAngle - : number('startAngle', startConfig.startAngle, { range: true, min: -360, max: 360, step: 1 }); - const endAngle = template - ? startConfig.endAngle - : number('endAngle', startConfig.endAngle, { range: true, min: -360, max: 360, step: 1 }); - const angleCount = template - ? startConfig.angleCount - : number('angleCount', startConfig.angleCount, { range: true, min: 2, max: 360, step: 1 }); - const padding = template - ? startConfig.padding - : number('padding', startConfig.padding, { range: true, min: 0, max: 10, step: 0.5 }); - const exponent = template - ? startConfig.exponent - : number('exponent', startConfig.exponent, { range: true, min: 0, max: 15, step: 1 }); - const fontWeight = template - ? startConfig.fontWeight - : number('fontWeight', startConfig.fontWeight, { range: true, min: 100, max: 900, step: 100 }); - const minFontSize = template - ? startConfig.minFontSize - : number('minFontSize', startConfig.minFontSize, { range: true, min: 6, max: 85, step: 1 }); - const maxFontSize = template - ? startConfig.maxFontSize - : number('maxFontSize', startConfig.maxFontSize, { range: true, min: 15, max: 150, step: 1 }); - const fontFamily = template - ? startConfig.fontFamily - : select( - 'fontFamily', - { - Arial: 'Arial', - 'Arial Narrow': 'Arial Narrow', - Courier: 'Courier', - Impact: 'Impact', - Luminari: 'Luminari', - }, - startConfig.fontFamily, - ); - const fontStyle = template - ? (startConfig.fontStyle as FontStyle) - : select('fontStyle', { normal: 'normal', italic: 'italic' }, startConfig.fontStyle as FontStyle); - - const palette = template - ? startConfig.palette - : select( - 'palette', - Object.keys(palettes).reduce((p, k) => ({ ...p, [k]: k }), {}), - startConfig.palette, - ); - const weightFn = template - ? startConfig.weightFn - : select( - 'weightFn', - { - linear: WeightFn.linear, - exponential: WeightFn.exponential, - squareRoot: WeightFn.squareRoot, - log: WeightFn.log, - }, - startConfig.weightFn, - ); + const { backgroundColor, palette, ...knobs } = getTemplate(configName); return ( @@ -284,19 +259,8 @@ export const Example = () => { /> { action('outOfRoomCallback')( `Managed to render ${renderedWordCount} words out of ${wordCount} words: ${renderedWords.join(', ')}`, @@ -308,5 +272,6 @@ export const Example = () => { }; Example.parameters = { - backgrounds: { disable: true }, + background: { disable: true }, + theme: { disable: true }, }; diff --git a/storybook/style.scss b/storybook/style.scss index b7b7bdd6e8..1a77e45c24 100644 --- a/storybook/style.scss +++ b/storybook/style.scss @@ -1,4 +1,5 @@ @import '~@elastic/eui/dist/eui_theme_light'; +@import '~@elastic/charts/src/theme_light'; /* atkinson-hyperlegible-regular - latin */ @font-face { diff --git a/yarn.lock b/yarn.lock index b3a847fc9d..85545f143b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5592,6 +5592,13 @@ resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-1.2.8.tgz#b852381cb68e31e46bfa23ee70a383cbc6d62146" integrity sha512-wWV0wT6oLUGprrOR5LMK7Dh8EBiondhnqINsvazv6UucYfTdb2oaFF4knlqzZV2RKB9ZC9G7G1Iojt8b/wolsw== +"@types/d3-cloud@^1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@types/d3-cloud/-/d3-cloud-1.2.5.tgz#0300bedc826aacd505ae6c41c5f8c4ab75c45135" + integrity sha512-vEIER9DsEBUOdpRiwCh3n1qE+cV6h4e1LhxhY2sLt+m8LPNAIkOOhTlqk0JDiBwD+ZPM8ynFAOU3AuPuVYBFBA== + dependencies: + "@types/d3" "^3" + "@types/d3-collection@^1.0.8": version "1.0.8" resolved "https://registry.yarnpkg.com/@types/d3-collection/-/d3-collection-1.0.8.tgz#aa9552c570a96e33c132e0fd20e331f64baa9dd5" @@ -5633,6 +5640,11 @@ resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-1.0.10.tgz#d338c7feac93a98a32aac875d1100f92c7b61f4f" integrity sha512-aKf62rRQafDQmSiv1NylKhIMmznsjRN+MnXRXTqHoqm0U/UZzVpdrtRnSIfdiLS616OuC1soYeX1dBg2n1u8Xw== +"@types/d3@^3": + version "3.5.45" + resolved "https://registry.yarnpkg.com/@types/d3/-/d3-3.5.45.tgz#cceb1cd8f468b0ed1c96546ddefff3408d7463a7" + integrity sha512-wLICfMtjDEoAJie1MF6OuksAzOapRXgJy+l5HQVpyC1yMAlvHz2QKrrasUHru8xD6cbgQNGeO+CeyjOlKtly2A== + "@types/enzyme-adapter-react-16@^1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.5.tgz#1bf30a166f49be69eeda4b81e3f24113c8b4e9d5"