diff --git a/packages/charts/src/chart_types/xy_chart/state/selectors/compute_series_domains.ts b/packages/charts/src/chart_types/xy_chart/state/selectors/compute_series_domains.ts index 96377eea19..e2c8391d32 100644 --- a/packages/charts/src/chart_types/xy_chart/state/selectors/compute_series_domains.ts +++ b/packages/charts/src/chart_types/xy_chart/state/selectors/compute_series_domains.ts @@ -28,3 +28,26 @@ export const computeSeriesDomainsSelector = createCustomCachedSelector( ], computeSeriesDomains, ); + +/** + * Returns series domains computed over the full dataset, ignoring any deselected series. + * When nothing is deselected, reuses the already-memoized result of computeSeriesDomainsSelector + * at zero extra cost. Only triggers a second computeSeriesDomains call when deselection is active. + * Used for stable legend width calculation. + * @internal + */ +export const computeFullSeriesDomainsSelector = createCustomCachedSelector( + [ + computeSeriesDomainsSelector, + getDeselectedSeriesSelector, + getSeriesSpecsSelector, + getScaleConfigsFromSpecsSelector, + getAnnotationSpecsSelector, + getSettingsSpecSelector, + getSmallMultiplesIndexOrderSelector, + ], + (domains, deselectedSeries, seriesSpecs, scaleConfigs, annotations, settings, smallMultiples) => { + if (deselectedSeries.length === 0) return domains; + return computeSeriesDomains(seriesSpecs, scaleConfigs, annotations, settings, [], smallMultiples); + }, +); diff --git a/packages/charts/src/chart_types/xy_chart/state/selectors/get_legend_max_formatted_value.ts b/packages/charts/src/chart_types/xy_chart/state/selectors/get_legend_max_formatted_value.ts index 23d106921e..167d135fab 100644 --- a/packages/charts/src/chart_types/xy_chart/state/selectors/get_legend_max_formatted_value.ts +++ b/packages/charts/src/chart_types/xy_chart/state/selectors/get_legend_max_formatted_value.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { computeSeriesDomainsSelector } from './compute_series_domains'; +import { computeFullSeriesDomainsSelector } from './compute_series_domains'; import { getAxisSpecsSelector, getSeriesSpecsSelector } from './get_specs'; import { createCustomCachedSelector } from '../../../../state/create_selector'; import { getSettingsSpecSelector } from '../../../../state/selectors/get_settings_spec'; @@ -19,7 +19,7 @@ import { getAxesSpecForSpecId } from '../utils/spec'; * @internal */ export const getLongestLegendFormattedValueSelector = createCustomCachedSelector( - [computeSeriesDomainsSelector, getSeriesSpecsSelector, getAxisSpecsSelector, getSettingsSpecSelector], + [computeFullSeriesDomainsSelector, getSeriesSpecsSelector, getAxisSpecsSelector, getSettingsSpecSelector], ({ yDomains }, seriesSpecs, axesSpecs, settings): string | undefined => { const maxYValue = yDomains?.[0]?.domain?.[1]; if (typeof maxYValue !== 'number' || !isFinite(maxYValue)) return undefined; diff --git a/packages/charts/src/components/legend/label.tsx b/packages/charts/src/components/legend/label.tsx index 7ca7834345..327d2b55cc 100644 --- a/packages/charts/src/components/legend/label.tsx +++ b/packages/charts/src/components/legend/label.tsx @@ -23,7 +23,7 @@ interface LabelProps { options: LegendLabelOptions; hiddenSeriesCount: number; totalSeriesCount: number; - truncationMode?: TruncationMode; + truncationMode: TruncationMode; } const isAppleDevice = typeof window !== 'undefined' && /Mac|iPhone|iPad/.test(window.navigator.userAgent); @@ -92,7 +92,7 @@ export function Label({ [onToggle], ); - const title = Math.abs(options.maxLines) > 0 ? label : ''; // full text already visible + const title = truncationMode === 'px' || Math.abs(options.maxLines) > 0 ? label : ''; return isToggleable ? ( // This div is required to allow multiline text truncation, all ARIA requirements are still met @@ -139,7 +139,7 @@ function getSharedProps( const widthLimit = Math.abs(options.widthLimit); const className = classNames('echLegendItem__label', { 'echLegendItem__label--clickable': Boolean(isToggleable), - 'echLegendItem__label--singleline': maxLines === 1, + 'echLegendItem__label--singleline': truncationMode === 'px' || maxLines === 1, 'echLegendItem__label--multiline': maxLines > 1 && truncationMode === 'line', }); diff --git a/packages/charts/src/components/legend/legend_list.tsx b/packages/charts/src/components/legend/legend_list.tsx index 19dc114a4f..00351ef52e 100644 --- a/packages/charts/src/components/legend/legend_list.tsx +++ b/packages/charts/src/components/legend/legend_list.tsx @@ -89,7 +89,8 @@ export const LegendList: React.FC = (props) => { // Pre-compute value elements for both layout modes const valueElements: React.ReactNode[] = []; - if (!isSeriesHidden) { + const shouldShowValues = !isSeriesHidden || Boolean(isListLayout); + if (shouldShowValues) { const valueData = isListLayout ? // In list layout, preserve the `legendValues` order and allow placeholders for CurrentAndLastValue legendValues.map((type, index) => ({ type, legendValueItem: preparedLegendValues[index], index })) diff --git a/packages/charts/src/components/legend/legend_table/legend_table_item.tsx b/packages/charts/src/components/legend/legend_table/legend_table_item.tsx index 7f06f3619c..97de9bf855 100644 --- a/packages/charts/src/components/legend/legend_table/legend_table_item.tsx +++ b/packages/charts/src/components/legend/legend_table/legend_table_item.tsx @@ -94,6 +94,7 @@ export const LegendListItem: React.FC = (props) => { isSeriesHidden={isSeriesHidden} totalSeriesCount={totalItems} hiddenSeriesCount={hiddenItems} + truncationMode="line" /> diff --git a/storybook/stories/legend/2_legend_layout.story.tsx b/storybook/stories/legend/2_legend_layout.story.tsx index c1a1a99bea..5b8859261a 100644 --- a/storybook/stories/legend/2_legend_layout.story.tsx +++ b/storybook/stories/legend/2_legend_layout.story.tsx @@ -24,10 +24,10 @@ const getLabelOptionKnobs = (isLineLimit: boolean): LegendLabelOptions => { return isLineLimit ? { maxLines: number('max label lines', 1, { min: 0, step: 1 }, group), - widthLimit: 250, + widthLimit: 240, } : { - maxLines: 1, + maxLines: 2, widthLimit: number('width limit', 250, { min: 0, step: 1 }, group), }; };