Skip to content

[charts-premium] Replace dataIndex with name in map identifier#22891

Merged
sai6855 merged 24 commits into
mui:masterfrom
sai6855:map-data-index
Jun 26, 2026
Merged

[charts-premium] Replace dataIndex with name in map identifier#22891
sai6855 merged 24 commits into
mui:masterfrom
sai6855:map-data-index

Conversation

@sai6855

@sai6855 sai6855 commented Jun 19, 2026

Copy link
Copy Markdown
Member

Fix #22943

changelog

● Breaking change

The mapShape item identifier uses feature name instead of dataIndex.

- { type: 'mapShape', seriesId, dataIndex: 56 }
+ { type: 'mapShape', seriesId, name: 'Germany' }

If you read or build a mapShape identifier (for example in highlightedItem, or onHighlightChange), replace dataIndex by the corresponding name.

New requirement: names must be unique

Because shapes are now identified by name, each mapShape data entry must have a unique name within its series —
otherwise duplicates can't be told apart for coloring, tooltips, and highlighting. A dev-time error is thrown when a
duplicate is detected. A single name can still map to multiple shapes on the map (one logical item, several polygons);
use geoFeatureKey to give distinct features distinct keys.

@sai6855 sai6855 added plan: Premium Impact at least one Premium user. type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature. scope: charts Changes related to the charts. labels Jun 19, 2026
@code-infra-dashboard

code-infra-dashboard Bot commented Jun 19, 2026

Copy link
Copy Markdown

Deploy preview

https://deploy-preview-22891--material-ui-x.netlify.app/

Bundle size

Bundle Parsed size Gzip size
@mui/x-data-grid 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-pro 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-premium 0B(0.00%) 0B(0.00%)
@mui/x-charts 0B(0.00%) 0B(0.00%)
@mui/x-charts-pro 0B(0.00%) 0B(0.00%)
@mui/x-charts-premium 🔺+1.22KB(+0.20%) 🔺+451B(+0.24%)
@mui/x-date-pickers 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers-pro 0B(0.00%) 0B(0.00%)
@mui/x-tree-view 0B(0.00%) 0B(0.00%)
@mui/x-tree-view-pro 0B(0.00%) 0B(0.00%)
@mui/x-license 0B(0.00%) 0B(0.00%)

Details of bundle changes

Performance

Total duration: 1,833.50 ms -276.09 ms(-13.1%) | Renders: 63 (+0)

Test Duration Renders
ScatterChartPremium with big data amount (webgl renderer) 96.30 ms 🔺+16.76 ms(+21.1%) 5 (+0)
Heatmap: 100x100 grid 516.36 ms ▼-135.47 ms(-20.8%) 2 (+0)
SparkLineChart grid of 100 instances 137.89 ms ▼-53.53 ms(-28.0%) 1 (+0)
BarChartPro with big data amount 38.14 ms ▼-14.10 ms(-27.0%) 2 (+0)
LineChartPro with big data amount and zoomed in (with marks) 26.14 ms ▼-10.79 ms(-29.2%) 2 (+0)

…and 6 more (+15 within noise) — details


Check out the code infra dashboard for more information about this PR.

@sai6855

sai6855 commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

@alexfauquette I started the mapShape identifier change and hit a blocker. I'd like your call on.

Option A, keep the field named dataIndex, but make it hold the feature name (i.e. dataIndex: 'Germany', type string). This matches your example in #22876 (comment), but since the field is still called dataIndex and is now a string, it clashes with the core's numeric-dataIndex assumptions. It produces type errors in:

  • useChartHighlight/useChartHighlight.selectors.ts (the batch-rendering series helpers expect dataIndex?: number)
  • useChartKeyboardNavigation/useChartKeyboardNavigation.selectors.ts (builds an axis identifier with dataIndex: number)
  • internals/commonNextFocusItem.ts (numeric stepping)

Fixing these means adding casts / mapShape exclusions in shared core selectors.

Option B, go the Heatmap/Sankey direction: drop dataIndex entirely and give mapShape its own key field, name ({ type, seriesId, name: feature.properties.name }). Heatmap (xIndex/yIndex) and Sankey (sourceId/targetId) already do this. The cost is mapShape then needs its own highlight predicates + serializer/cleaner (which Heatmap/Sankey already provide as precedent).

I'm leaning towards B, it makes mapShape a clean custom-identifier series consistent with Heatmap/Sankey and avoids casts in shared code, whereas A leaves a dataIndex field literally holding a country name. so I wanted to confirm before committing. Which would you prefer?

@oliviertassinari oliviertassinari changed the title [charts-premium] update dataIndex to string in Map charts [charts-premium] Update dataIndex to string in Map charts Jun 19, 2026
@alexfauquette

Copy link
Copy Markdown
Member

@sai6855 Option B is effectively the one that make the more sense { type, seriesId, name }

This will probably require some modification about the keyboard navigation, but noting too complicated

@sai6855 sai6855 added the breaking change Introduces changes that are not backward compatible. label Jun 24, 2026
@sai6855
sai6855 requested a review from Copilot June 24, 2026 09:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the mapShape item identifier in x-charts-premium map charts from { dataIndex: number } to { name: string } (feature name), and wires that change through highlighting, tooltips, keyboard navigation, and color resolution.

Changes:

  • Replaced dataIndex-based map shape identifiers with name-based identifiers across map series types and interactions.
  • Added map-specific identifier serialization/cleaning and highlight/fade creators to avoid the shared dataIndex-based utilities.
  • Updated map tooltip, tooltip positioning, keyboard focus handler, and color getter logic to resolve items by name.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/createIsHighlighted.ts Excludes mapShape from the shared dataIndex-based highlight creator.
packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/createIsFaded.ts Excludes mapShape from the shared dataIndex-based fade creator.
packages/x-charts/src/internals/plugins/corePlugins/useChartSeriesConfig/types/colorProcessor.types.ts Updates mapShape color getter signature to accept name?: string.
packages/x-charts/src/internals/createCommonKeyboardFocusHandler.ts Excludes mapShape from shared dataIndex-based keyboard navigation handler.
packages/x-charts/src/internals/commonNextFocusItem.ts Excludes mapShape from shared dataIndex-based next/prev focus utilities.
packages/x-charts-premium/src/models/seriesType/mapShape.ts Changes MapShapeItemIdentifier from dataIndex to name.
packages/x-charts-premium/src/Map/seriesConfig/tooltipPosition.ts Computes tooltip position using identifier.name instead of dataIndex.
packages/x-charts-premium/src/Map/seriesConfig/tooltip.ts Resolves tooltip data/color using identifier.name.
packages/x-charts-premium/src/Map/seriesConfig/seriesProcessor.ts Applies visibility checks using { name } identifiers for map items.
packages/x-charts-premium/src/Map/seriesConfig/keyboardFocusHandler.ts Replaces common dataIndex navigation with a name-based keyboard handler.
packages/x-charts-premium/src/Map/seriesConfig/index.ts Switches to map-specific serializer/cleaner/highlight implementations.
packages/x-charts-premium/src/Map/seriesConfig/identifierSerializer.ts Adds mapShape identifier serializer based on name.
packages/x-charts-premium/src/Map/seriesConfig/identifierCleaner.ts Adds mapShape identifier cleaner returning { type, seriesId, name }.
packages/x-charts-premium/src/Map/seriesConfig/highlight.ts Adds map-specific highlight/fade creators using { name }.
packages/x-charts-premium/src/Map/seriesConfig/getColor.ts Updates map color lookup to be name-based.
packages/x-charts-premium/src/Map/seriesConfig/descriptionGetter.ts Updates description getter to resolve items by name.
packages/x-charts-premium/src/Map/MapShapePlot.tsx Passes name into MapShape and uses colorGetter(item.name).
packages/x-charts-premium/src/Map/MapShape.tsx Builds interaction identifier using { name } instead of { dataIndex }.
packages/x-charts-premium/src/Map/FocusedMapShape.tsx Resolves focused item’s data row by name.
Comments suppressed due to low confidence (1)

packages/x-charts-premium/src/Map/seriesConfig/tooltipPosition.ts:11

  • itemSeries is assigned but never used after switching mapShape identifiers to name. This will trip linting and is unnecessary—just validate the series exists (if needed) without storing it.
  if (!identifier || identifier.name === undefined) {
    return null;
  }
  const itemSeries = series.mapShape?.series[identifier.seriesId];


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 11 to 15
const item = series.data.find((d) => d.name === name);

if (item.color !== undefined) {
if (item?.color !== undefined) {
return item.color;
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed here 9729df8

Comment on lines +4 to +8
if (!identifier || identifier.name === undefined) {
return null;
}

const point = series.data[identifier.dataIndex];
const point = series.data.find((d) => d.name === identifier.name);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed here 9729df8

Comment on lines +77 to +79
const order = mapSeries.seriesOrder;
const position = order.indexOf(currentItem.seriesId);
for (let i = position + direction; i >= 0 && i < order.length; i += direction) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed here 9729df8

@github-actions github-actions Bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label Jun 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@github-actions github-actions Bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged. label Jun 25, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 7 comments.

Comment on lines +11 to +15
const item = series.data.find((d) => d.name === name);

if (item.color !== undefined) {
if (!item) {
return series.color;
}
Comment on lines +28 to 34
return (name?: string) => {
if (name == null) {
return series.color;
}
return series.data[dataIndex].color ?? series.color;
const item = series.data.find((d) => d.name === name);
return item?.color ?? series.color;
};
Comment on lines 9 to 15
export type MapShapeProps = Omit<React.SVGProps<SVGPathElement>, 'ref'> & {
seriesId: SeriesId;
dataIndex: number;
name: string;
d: string;
color: string;
};
Comment on lines +37 to +43
for (const seriesId of mapSeries.seriesOrder) {
const data = mapSeries.series[seriesId]?.data;
if (data && data.length > 0) {
return toFocusedItem(seriesId, data[0].name);
}
}
return null;
Comment on lines +52 to +62
const data = getSeriesData(state, currentItem.seriesId);
const current = data.findIndex((d) => d.name === currentItem.name);
if (current === -1) {
return getFirstItem(state);
}
const next = current + direction;
if (next < 0 || next >= data.length) {
return currentItem;
}
return toFocusedItem(currentItem.seriesId, data[next].name);
}
Comment on lines +79 to +85
for (let i = position + direction; i >= 0 && i < order.length; i += direction) {
const data = mapSeries.series[order[i]]?.data;
if (data && data.length > 0) {
return toFocusedItem(order[i], data[0].name);
}
}
return currentItem;
Comment on lines +15 to +16
* Note: hidden shapes/series are not skipped here — the defaultized series read
* from the state does not carry visibility. Focus may land on a hidden shape.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 3 comments.

Comment on lines +1 to +8
import { typeSerializer, seriesIdSerializer } from '@mui/x-charts/internals';
import type { IdentifierSerializer } from '@mui/x-charts/internals';

const identifierSerializer: IdentifierSerializer<'mapShape'> = (identifier) => {
return `${typeSerializer(identifier.type)}${seriesIdSerializer(identifier.seriesId)}(${identifier.name})`;
};

export default identifierSerializer;
Comment on lines +90 to +105
function stepWithinSeries(
currentItem: FocusedItemIdentifier<'mapShape'>,
state: MapState,
direction: 1 | -1,
): FocusedItemIdentifier<'mapShape'> | null {
const series = getMapSeries(state)?.series[currentItem.seriesId];
if (!series) {
return currentItem;
}
const current = series.data.findIndex((d) => d.name === currentItem.name);
if (current === -1) {
return getFirstItem(state);
}
const next = nextVisibleIndex(series.data, current + direction, direction);
return next === null ? currentItem : toFocusedItem(currentItem.seriesId, series.data[next].name);
}
Comment on lines 9 to 14
export type MapShapeProps = Omit<React.SVGProps<SVGPathElement>, 'ref'> & {
seriesId: SeriesId;
dataIndex: number;
name: string;
d: string;
color: string;
@sai6855
sai6855 requested a review from Copilot June 25, 2026 08:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@sai6855

sai6855 commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

@sai6855 Option B is effectively the one that make the more sense { type, seriesId, name }

This will probably require some modification about the keyboard navigation, but noting too complicated

@alexfauquette Updated identifier structure, Now PR is ready for review

@sai6855
sai6855 marked this pull request as ready for review June 25, 2026 10:05

const descriptionGetter: DescriptionGetter<'mapShape'> = ({ identifier, series }) => {
const item = series.data[identifier.dataIndex];
const item = series.data.find((d) => d.name === identifier.name);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How should this work when joining shapes? Like, should we provide an array of items instead?

If I understand correctly, in the current api, a name could be related to multiple shapes/items 🤔

@sai6855 sai6855 Jun 25, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The map joins data to features through a resolved key (geoFeatureKey, default feature.properties.name), and one key can intentionally map to several shapes for example, the Somalia/Somaliland demo,

If two different features share a same name, the user can give them distinct keys via geoFeatureKey (when the features carry a unique field like an id or ISO code)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly. Giving the same name to multiple shapes is a way to indicate they should be treated the same way.

Btw this aspect changes nothing, because the dataIndex we replace in this PR is the series data index, and not the feature data index :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean if we merge the below

[ { name: 'land', population: 3000, }, { name: 'land_disputed', population: 1000, }, ]

we will ignore the population data of land_disputed once merged?

@alexfauquette

alexfauquette commented Jun 25, 2026

Copy link
Copy Markdown
Member

I've fixed what I could find. Mostly sharing a lookup name => index when creating the series to avoid the .find() at each interaction, and make sure there is no duplicate issues.

I replaced name prop by featureName because name is already a valid attribute.

For the keyboard navigation, I'm wondering if it would be possible to modify a bit the shared function to pass adapters to the existing keyboard navigation

image

If you want I can give it a look tomorow

@sai6855

sai6855 commented Jun 26, 2026

Copy link
Copy Markdown
Member Author

For the keyboard navigation, I'm wondering if it would be possible to modify a bit the shared function to pass adapters ?>to the existing keyboard navigation

Turns out we can re-use existing keyboard utils, just needed slight tweaks in types. Updated code here dbc3813

@sai6855 sai6855 changed the title [charts-premium] Update dataIndex to string in Map charts [charts-premium] Replace dataIndex with name in map identifier Jun 26, 2026

@alexfauquette alexfauquette left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍 @JCQuintas are you ok with the new API?

Comment on lines +35 to +37
const getMapSeries = (state: MapState) =>
selectorChartSeriesProcessed(state as ChartState<[UseChartKeyboardNavigationSignature], []>)
.mapShape;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is only used once

Suggested change
const getMapSeries = (state: MapState) =>
selectorChartSeriesProcessed(state as ChartState<[UseChartKeyboardNavigationSignature], []>)
.mapShape;

}

return (currentItem, state) => {
const mapSeries = getMapSeries(state);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const mapSeries = getMapSeries(state);
const mapSeries = selectorChartSeriesProcessed(state as ChartState<[UseChartKeyboardNavigationSignature], []>).mapShape;

* reuse these helpers by translating to a `dataIndex` at their boundary.
*/
type WorkingItem = {
type: Exclude<ChartSeriesType, 'sankey' | 'heatmap'>;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we need to exclude sankey/heatmap now?

}

export function createGetNextIndexFocusedItem<
InSeriesType extends Exclude<ChartSeriesType, 'sankey' | 'heatmap'>,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JCQuintas Exclusion is actually not a new change, We just moved this type to WorkingItem[type]

@sai6855
sai6855 enabled auto-merge (squash) June 26, 2026 10:57
@sai6855
sai6855 merged commit 90255b1 into mui:master Jun 26, 2026
21 checks passed
mbrookes pushed a commit to mbrookes/mui-x that referenced this pull request Jun 27, 2026
…#22891)

Signed-off-by: sai chand <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
Co-authored-by: alex <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change Introduces changes that are not backward compatible. plan: Premium Impact at least one Premium user. scope: charts Changes related to the charts. type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[charts-premium] Replace data-index by data-name

4 participants