diff --git a/packages/charts/src/chart_types/goal_chart/state/selectors/get_screen_reader_data.ts b/packages/charts/src/chart_types/goal_chart/state/selectors/get_screen_reader_data.ts index 1ac8f3322dd..aed0203c759 100644 --- a/packages/charts/src/chart_types/goal_chart/state/selectors/get_screen_reader_data.ts +++ b/packages/charts/src/chart_types/goal_chart/state/selectors/get_screen_reader_data.ts @@ -7,11 +7,11 @@ */ import { getGoalChartDataSelector, getGoalChartLabelsSelector } from './get_goal_chart_data'; -import type { ChartSpecificScreenReaderData, ScreenReaderItem } from '../../../../state/chart_selectors'; import type { GlobalChartState } from '../../../../state/chart_state'; import { createCustomCachedSelector } from '../../../../state/create_selector'; import { getA11ySettingsSelector } from '../../../../state/selectors/get_accessibility_config'; import { getInternalChartStateSelector } from '../../../../state/selectors/get_internal_chart_state'; +import { EMPTY_SCREEN_READER_ITEMS, type ScreenReaderItem } from '../../../../state/selectors/get_screenreader_data'; /** @internal */ export const getScreenReaderDataSelector = createCustomCachedSelector( @@ -22,7 +22,7 @@ export const getScreenReaderDataSelector = createCustomCachedSelector( getA11ySettingsSelector, (state: GlobalChartState) => state, ], - (goalChartData, goalChartLabels, internalChartState, a11ySettings, state): ChartSpecificScreenReaderData => { + (goalChartData, goalChartLabels, internalChartState, a11ySettings, state): ScreenReaderItem[] => { const screenReaderItems: ScreenReaderItem[] = []; // Add chart type description first @@ -69,6 +69,6 @@ export const getScreenReaderDataSelector = createCustomCachedSelector( ); } - return { screenReaderItems }; + return screenReaderItems.length > 0 ? screenReaderItems : EMPTY_SCREEN_READER_ITEMS; }, ); diff --git a/packages/charts/src/chart_types/goal_chart/state/selectors/tooltip.ts b/packages/charts/src/chart_types/goal_chart/state/selectors/tooltip.ts index 86f840d3d00..5ae12e2f1a1 100644 --- a/packages/charts/src/chart_types/goal_chart/state/selectors/tooltip.ts +++ b/packages/charts/src/chart_types/goal_chart/state/selectors/tooltip.ts @@ -10,14 +10,10 @@ import { getGoalSpecSelector } from './get_goal_spec'; import { getPickedShapes } from './picked_shapes'; import { Colors } from '../../../../common/colors'; import type { TooltipInfo } from '../../../../components/tooltip/types'; +import { EMPTY_TOOLTIP } from '../../../../state/chart_selectors'; import { createCustomCachedSelector } from '../../../../state/create_selector'; import type { BandViewModel } from '../../layout/types/viewmodel_types'; -const EMPTY_TOOLTIP = Object.freeze({ - header: null, - values: [], -}); - const getBandColor = (value: number, bands: BandViewModel[]) => bands.find(({ value: v }) => { return v >= value; diff --git a/packages/charts/src/chart_types/heatmap/state/selectors/tooltip.ts b/packages/charts/src/chart_types/heatmap/state/selectors/tooltip.ts index eebe79b43ee..f1c55914576 100644 --- a/packages/charts/src/chart_types/heatmap/state/selectors/tooltip.ts +++ b/packages/charts/src/chart_types/heatmap/state/selectors/tooltip.ts @@ -11,14 +11,9 @@ import { getPickedShapes } from './picked_shapes'; import { RGBATupleToString } from '../../../../common/color_library_wrappers'; import { Colors } from '../../../../common/colors'; import type { TooltipInfo } from '../../../../components/tooltip/types'; +import { EMPTY_TOOLTIP } from '../../../../state/chart_selectors'; import { createCustomCachedSelector } from '../../../../state/create_selector'; -const EMPTY_TOOLTIP = Object.freeze({ - header: null, - values: [], - disableActions: false, -}); - /** @internal */ export const getTooltipInfoSelector = createCustomCachedSelector( [getHeatmapSpecSelector, getPickedShapes], diff --git a/packages/charts/src/chart_types/partition_chart/state/selectors/tooltip.ts b/packages/charts/src/chart_types/partition_chart/state/selectors/tooltip.ts index 247bdc6c2bd..6b5c3f03f50 100644 --- a/packages/charts/src/chart_types/partition_chart/state/selectors/tooltip.ts +++ b/packages/charts/src/chart_types/partition_chart/state/selectors/tooltip.ts @@ -10,11 +10,10 @@ import { partitionMultiGeometries } from './geometries'; import { getPartitionSpec } from './partition_spec'; import { getPickedShapes } from './picked_shapes'; import type { TooltipInfo } from '../../../../components/tooltip/types'; +import { EMPTY_TOOLTIP } from '../../../../state/chart_selectors'; import { createCustomCachedSelector } from '../../../../state/create_selector'; import { pickShapesTooltipValues } from '../../layout/viewmodel/picked_shapes'; -const EMPTY_TOOLTIP = Object.freeze({ header: null, values: [] }); - /** @internal */ export const getTooltipInfoSelector = createCustomCachedSelector( [getPartitionSpec, getPickedShapes, partitionMultiGeometries], diff --git a/packages/charts/src/chart_types/xy_chart/state/selectors/get_legend_item_extra_values.ts b/packages/charts/src/chart_types/xy_chart/state/selectors/get_legend_item_extra_values.ts index 5a3a00c7c5c..5ab3efcdffb 100644 --- a/packages/charts/src/chart_types/xy_chart/state/selectors/get_legend_item_extra_values.ts +++ b/packages/charts/src/chart_types/xy_chart/state/selectors/get_legend_item_extra_values.ts @@ -13,12 +13,15 @@ import { EMPTY_LEGEND_ITEM_EXTRA_VALUES } from '../../../../common/legend'; import type { SeriesKey } from '../../../../common/series_id'; import { ScaleType } from '../../../../scales/constants'; import { createCustomCachedSelector } from '../../../../state/create_selector'; +import { getLegendConfigSelector } from '../../../../state/selectors/get_legend_config_selector'; import { getLegendItemExtraValues } from '../../tooltip/tooltip'; /** @internal */ export const getLegendItemExtraValuesSelector = createCustomCachedSelector( - [getTooltipInfoAndGeomsSelector, getComputedScalesSelector], - ({ tooltip: { values } }, { xScale: { type } }): Map => + [getTooltipInfoAndGeomsSelector, getComputedScalesSelector, getLegendConfigSelector], + ({ tooltip: { values } }, { xScale: { type } }, { legendValues }): Map => // See https://github.com/elastic/elastic-charts/issues/2050 - type === ScaleType.Ordinal ? EMPTY_LEGEND_ITEM_EXTRA_VALUES : getLegendItemExtraValues(values), + type === ScaleType.Ordinal || legendValues.length === 0 + ? EMPTY_LEGEND_ITEM_EXTRA_VALUES + : getLegendItemExtraValues(values), ); diff --git a/packages/charts/src/components/accessibility/screen_reader_items.tsx b/packages/charts/src/components/accessibility/screen_reader_items.tsx index 07217a06b09..bc880998c3b 100644 --- a/packages/charts/src/components/accessibility/screen_reader_items.tsx +++ b/packages/charts/src/components/accessibility/screen_reader_items.tsx @@ -8,16 +8,16 @@ import React from 'react'; -import type { ScreenReaderItem } from '../../state/chart_selectors'; import type { A11ySettings } from '../../state/selectors/get_accessibility_config'; +import type { ScreenReaderItem } from '../../state/selectors/get_screenreader_data'; interface ScreenReaderItemsProps { - screenReaderItems?: ScreenReaderItem[]; + screenReaderItems: ScreenReaderItem[]; } /** @internal */ export function ScreenReaderItems({ screenReaderItems }: A11ySettings & ScreenReaderItemsProps) { - const hasScreenReaderItems = screenReaderItems && screenReaderItems.length > 0; + const hasScreenReaderItems = screenReaderItems.length > 0; if (!hasScreenReaderItems) { return null; diff --git a/packages/charts/src/components/accessibility/screen_reader_summary.tsx b/packages/charts/src/components/accessibility/screen_reader_summary.tsx index 93dc2bd2ac1..56b62075958 100644 --- a/packages/charts/src/components/accessibility/screen_reader_summary.tsx +++ b/packages/charts/src/components/accessibility/screen_reader_summary.tsx @@ -15,7 +15,7 @@ import type { GlobalChartState } from '../../state/chart_state'; import type { ScreenReaderSummaryData } from '../../state/selectors/get_screen_reader_summary'; import { getScreenReaderSummarySelector } from '../../state/selectors/get_screen_reader_summary'; -const ScreenReaderSummaryComponent = ({ a11ySettings, screenReaderData }: ScreenReaderSummaryData) => { +const ScreenReaderSummaryComponent = ({ a11ySettings, screenReaderItems }: ScreenReaderSummaryData) => { return (
- +
); }; diff --git a/packages/charts/src/state/chart_selectors.ts b/packages/charts/src/state/chart_selectors.ts index 9051f37d878..ad67c38e6d4 100644 --- a/packages/charts/src/state/chart_selectors.ts +++ b/packages/charts/src/state/chart_selectors.ts @@ -9,8 +9,9 @@ import type { CSSProperties } from 'react'; import type { GlobalChartState } from './chart_state'; -import { getA11ySettingsSelector } from './selectors/get_accessibility_config'; import { InitStatus } from './selectors/get_internal_is_intialized'; +import type { ScreenReaderItem } from './selectors/get_screenreader_data'; +import { getScreenReaderDataSelector } from './selectors/get_screenreader_data'; import type { TooltipVisibility } from './tooltip_visibility'; import type { DebugState } from './types'; import { DEFAULT_CSS_CURSOR } from '../common/constants'; @@ -22,31 +23,12 @@ import type { AnchorPosition } from '../components/portal/types'; import type { TooltipInfo } from '../components/tooltip/types'; import type { Dimensions } from '../utils/dimensions'; -/** @internal */ -export interface ScreenReaderItem { - /** The label for this part of the summary */ - label: string; - /** Optional ID for referencing this part */ - id?: string; - /** The value for this part of the summary */ - value: string; -} - -/** @internal */ -export interface ChartSpecificScreenReaderData { - /** Custom summary parts to include in the consolidated summary */ - screenReaderItems?: ScreenReaderItem[]; -} - /** @internal */ export interface LegendItemLabel { label: string; depth: number; } -/** @internal */ -export const EMPTY_LEGEND_ITEM_LIST: LegendItemLabel[] = []; - /** * A set of chart-type-dependant functions that are required by all chart types * @internal @@ -157,7 +139,7 @@ export interface ChartSelectors { /** * Get chart-specific data for screen reader accessibility */ - getScreenReaderData?(globalState: GlobalChartState): ChartSpecificScreenReaderData; + getScreenReaderData(globalState: GlobalChartState): ScreenReaderItem[]; /** * Get the domain of the vertical and horizontal small multiple grids @@ -170,10 +152,20 @@ export interface ChartSelectors { canDisplayChartTitles(globalState: GlobalChartState): boolean; } -/** @internal */ -export type ChartSelectorsFactory = () => ChartSelectors; +type ChartSelectorsFactory = () => ChartSelectors; -const EMPTY_TOOLTIP = Object.freeze({ header: null, values: [] }); +const EMPTY_LEGEND_ITEM_LIST: LegendItemLabel[] = []; +/** @internal */ +export const EMPTY_TOOLTIP = { header: null, values: [] }; +const EMPTY_DIMENSION = { top: 0, left: 0, width: 0, height: 0 }; +const EMPTY_SM_DOMAINS: SmallMultiplesSeriesDomains = { smVDomain: [], smHDomain: [] }; +const EMPTY_OBJ = {}; +const EMPTY_TOOLTIP_VISIBILITY: TooltipVisibility = { + visible: false, + isExternal: false, + displayOnly: false, + isPinnable: false, +}; type CallbackCreator = () => (state: GlobalChartState) => void; @@ -195,30 +187,16 @@ export const createChartSelectorsFactory = getLegendItemsLabels: () => EMPTY_LEGEND_ITEM_LIST, getLegendExtraValues: () => EMPTY_LEGEND_ITEM_EXTRA_VALUES, getPointerCursor: () => DEFAULT_CSS_CURSOR, - isTooltipVisible: () => ({ - visible: false, - isExternal: false, - displayOnly: false, - isPinnable: false, - }), + isTooltipVisible: () => EMPTY_TOOLTIP_VISIBILITY, getTooltipInfo: () => EMPTY_TOOLTIP, getTooltipAnchor: () => null, - getProjectionContainerArea: () => ({ top: 0, left: 0, width: 0, height: 0 }), - getMainProjectionArea: () => ({ top: 0, left: 0, width: 0, height: 0 }), + getProjectionContainerArea: () => EMPTY_DIMENSION, + getMainProjectionArea: () => EMPTY_DIMENSION, getBrushArea: () => null, - getDebugState: () => ({}), + getDebugState: () => EMPTY_OBJ, getChartTypeDescription: () => '', - // The default screen reader data returns just the chart type description. - getScreenReaderData: (state: GlobalChartState): ChartSpecificScreenReaderData => { - const a11ySettings = getA11ySettingsSelector(state); - const chartTypeDescription = chartSelectors.getChartTypeDescription(state); - return { - screenReaderItems: chartTypeDescription - ? [{ label: 'Chart type', id: a11ySettings.defaultSummaryId, value: chartTypeDescription }] - : [], - }; - }, - getSmallMultiplesDomains: () => ({ smVDomain: [], smHDomain: [] }), + getScreenReaderData: getScreenReaderDataSelector, + getSmallMultiplesDomains: () => EMPTY_SM_DOMAINS, canDisplayChartTitles: () => true, ...overrides, eventCallbacks: (state: GlobalChartState) => { diff --git a/packages/charts/src/state/selectors/get_internal_chart_type_desc.ts b/packages/charts/src/state/selectors/get_internal_chart_type_desc.ts new file mode 100644 index 00000000000..715b5813c4e --- /dev/null +++ b/packages/charts/src/state/selectors/get_internal_chart_type_desc.ts @@ -0,0 +1,19 @@ +/* + * 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 { getInternalChartStateSelector } from './get_internal_chart_state'; +import type { GlobalChartState } from '../chart_state'; +import { createCustomCachedSelector } from '../create_selector'; + +/** @internal */ +export const getInternalChartTypeDescSelector = createCustomCachedSelector( + [(globalChartState: GlobalChartState) => globalChartState, getInternalChartStateSelector], + (globalChartState, internalChartState): string => { + return internalChartState?.getChartTypeDescription(globalChartState) ?? ''; + }, +); diff --git a/packages/charts/src/state/selectors/get_screen_reader_summary.ts b/packages/charts/src/state/selectors/get_screen_reader_summary.ts index 5ec086c02e8..e9edec68605 100644 --- a/packages/charts/src/state/selectors/get_screen_reader_summary.ts +++ b/packages/charts/src/state/selectors/get_screen_reader_summary.ts @@ -10,19 +10,19 @@ import type { A11ySettings } from './get_accessibility_config'; import { DEFAULT_A11Y_SETTINGS, getA11ySettingsSelector } from './get_accessibility_config'; import { getInternalChartStateSelector } from './get_internal_chart_state'; import { getInternalIsInitializedSelector, InitStatus } from './get_internal_is_intialized'; -import type { ChartSpecificScreenReaderData } from '../chart_selectors'; +import type { ScreenReaderItem } from './get_screenreader_data'; import type { GlobalChartState } from '../chart_state'; import { createCustomCachedSelector } from '../create_selector'; /** @internal */ export interface ScreenReaderSummaryData { a11ySettings: A11ySettings; - screenReaderData?: ChartSpecificScreenReaderData; + screenReaderItems: ScreenReaderItem[]; } const DEFAULT_SCREEN_READER_SUMMARY: ScreenReaderSummaryData = { a11ySettings: DEFAULT_A11Y_SETTINGS, - screenReaderData: undefined, + screenReaderItems: [], }; /** @internal */ @@ -38,12 +38,12 @@ export const getScreenReaderSummarySelector = createCustomCachedSelector( return DEFAULT_SCREEN_READER_SUMMARY; } - // Get chart-specific screen reader data - const screenReaderData = internalChartState.getScreenReaderData?.(state); + // Get chart-specific screen reader items + const screenReaderItems = internalChartState.getScreenReaderData(state); return { a11ySettings, - screenReaderData, + screenReaderItems, }; }, ); diff --git a/packages/charts/src/state/selectors/get_screenreader_data.ts b/packages/charts/src/state/selectors/get_screenreader_data.ts new file mode 100644 index 00000000000..38bfd2e2809 --- /dev/null +++ b/packages/charts/src/state/selectors/get_screenreader_data.ts @@ -0,0 +1,34 @@ +/* + * 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 { getA11ySettingsSelector } from './get_accessibility_config'; +import { getInternalChartTypeDescSelector } from './get_internal_chart_type_desc'; +import { createCustomCachedSelector } from '../create_selector'; + +/** @internal */ +export interface ScreenReaderItem { + /** The label for this part of the summary */ + label: string; + /** Optional ID for referencing this part */ + id?: string; + /** The value for this part of the summary */ + value: string; +} + +/** @internal */ +export const EMPTY_SCREEN_READER_ITEMS: ScreenReaderItem[] = []; + +/** @internal */ +export const getScreenReaderDataSelector = createCustomCachedSelector( + [getA11ySettingsSelector, getInternalChartTypeDescSelector], + (a11ySettings, chartTypeDescription): ScreenReaderItem[] => { + return chartTypeDescription + ? [{ label: 'Chart type', id: a11ySettings.defaultSummaryId, value: chartTypeDescription }] + : EMPTY_SCREEN_READER_ITEMS; + }, +);