Skip to content

[charts-pro] Hide values outside minStart and maxEnd in zoom slider #18336

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

bernardobelchior
Copy link
Member

@bernardobelchior bernardobelchior commented Jun 11, 2025

Hide values outside minStart and maxEnd in zoom slider, as suggested by @alexfauquette here.

Screen.Recording.2025-06-11.at.17.57.35.mov

@mui-bot
Copy link

mui-bot commented Jun 11, 2025

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

Updated pages:

Bundle size report

Total Size Change: ▼-9.31KB(-0.07%) - Total Gzip Change: ▼-2.94KB(-0.08%)
Files: 122 total (0 added, 0 removed, 49 changed)

Show details for 100 more bundles (22 more not shown)

@mui/x-charts-proparsed: ▼-3.57KB(-1.05%) gzip: ▼-1.46KB(-1.40%)
@mui/x-charts-pro/FunnelChartparsed: ▼-3.47KB(-1.65%) gzip: ▼-718B(-1.06%)
@mui/x-charts-pro/BarChartProparsed: ▼-85B(-0.04%) gzip: ▼-113B(-0.16%)
@mui/x-charts-pro/LineChartProparsed: ▼-85B(-0.04%) gzip: ▼-105B(-0.14%)
@mui/x-charts-pro/ChartZoomSliderparsed: ▼-84B(-0.12%) gzip: ▼-87B(-0.36%)
@mui/x-charts-pro/ScatterChartProparsed: ▼-84B(-0.04%) gzip: ▼-80B(-0.12%)
@mui/x-date-pickers/PickersTextFieldparsed: ▼-70B(-0.25%) gzip: ▼-17B(-0.20%)
@mui/x-date-pickersparsed: ▼-67B(-0.03%) gzip: ▼-27B(-0.04%)
@mui/x-date-pickers-proparsed: ▼-67B(-0.02%) gzip: ▼-19B(-0.02%)
@mui/x-date-pickers-pro/SingleInputDateRangeFieldparsed: ▼-67B(-0.08%) gzip: ▼-25B(-0.10%)
@mui/x-date-pickers-pro/SingleInputDateTimeRangeFieldparsed: ▼-67B(-0.08%) gzip: ▼-22B(-0.09%)
@mui/x-date-pickers-pro/SingleInputTimeRangeFieldparsed: ▼-67B(-0.08%) gzip: ▼-21B(-0.09%)
@mui/x-date-pickers/DateFieldparsed: ▼-67B(-0.09%) gzip: ▼-18B(-0.08%)
@mui/x-date-pickers/DatePickerparsed: ▼-67B(-0.04%) gzip: ▼-4B(-0.01%)
@mui/x-date-pickers/DateTimeFieldparsed: ▼-67B(-0.09%) gzip: ▼-16B(-0.07%)
@mui/x-date-pickers/DesktopDatePickerparsed: ▼-67B(-0.04%) gzip: ▼-11B(-0.02%)
@mui/x-date-pickers/TimeFieldparsed: ▼-67B(-0.09%) gzip: ▼-16B(-0.07%)
@mui/x-date-pickers-pro/DesktopDateTimeRangePickerparsed: ▼-65B(-0.03%) gzip: ▼-10B(-0.02%)
@mui/x-date-pickers-pro/DesktopTimeRangePickerparsed: ▼-65B(-0.05%) gzip: ▼-6B(-0.01%)
@mui/x-date-pickers-pro/MobileDateTimeRangePickerparsed: ▼-65B(-0.03%) gzip: ▼-6B(-0.01%)
@mui/x-date-pickers-pro/MobileTimeRangePickerparsed: ▼-65B(-0.05%) gzip: ▼-13B(-0.03%)
@mui/x-date-pickers-pro/TimeRangePickerparsed: ▼-65B(-0.05%) gzip: ▼-5B(-0.01%)
@mui/x-date-pickers/MobileDatePickerparsed: ▼-65B(-0.04%) gzip: ▼-20B(-0.04%)
@mui/x-date-pickers-pro/DateRangePickerparsed: ▼-62B(-0.04%) gzip: 🔺+2B(0.00%)
@mui/x-date-pickers-pro/DateTimeRangePickerparsed: ▼-62B(-0.03%) gzip: ▼-11B(-0.02%)
@mui/x-date-pickers-pro/DesktopDateRangePickerparsed: ▼-62B(-0.04%) gzip: 🔺+4B(+0.01%)
@mui/x-date-pickers-pro/MobileDateRangePickerparsed: ▼-62B(-0.04%) gzip: 🔺+3B(+0.01%)
@mui/x-date-pickers-pro/MultiInputDateRangeFieldparsed: ▼-62B(-0.07%) gzip: ▼-20B(-0.08%)
@mui/x-date-pickers-pro/MultiInputDateTimeRangeFieldparsed: ▼-62B(-0.07%) gzip: ▼-24B(-0.09%)
@mui/x-date-pickers-pro/MultiInputTimeRangeFieldparsed: ▼-62B(-0.07%) gzip: ▼-20B(-0.08%)
@mui/x-date-pickers/DateTimePickerparsed: ▼-62B(-0.03%) gzip: ▼-15B(-0.03%)
@mui/x-date-pickers/DesktopDateTimePickerparsed: ▼-62B(-0.03%) gzip: ▼-13B(-0.02%)
@mui/x-date-pickers/DesktopTimePickerparsed: ▼-62B(-0.05%) gzip: ▼-24B(-0.06%)
@mui/x-date-pickers/MobileDateTimePickerparsed: ▼-62B(-0.03%) gzip: ▼-14B(-0.03%)
@mui/x-date-pickers/MobileTimePickerparsed: ▼-62B(-0.05%) gzip: ▼-26B(-0.07%)
@mui/x-date-pickers/TimePickerparsed: ▼-62B(-0.04%) gzip: ▼-22B(-0.05%)
@mui/x-chartsparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts-pro/ChartContainerProparsed: 0B(0.00%) gzip: 🔺+4B(+0.01%)
@mui/x-charts-pro/ChartDataProviderProparsed: 0B(0.00%) gzip: 🔺+6B(+0.01%)
@mui/x-charts-pro/ChartsToolbarProparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts-pro/Heatmapparsed: 0B(0.00%) gzip: 🔺+5B(+0.01%)
@mui/x-charts-pro/PieChartProparsed: 0B(0.00%) gzip: 🔺+5B(+0.01%)
@mui/x-charts-pro/RadarChartProparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/BarChartparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartContainerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartDataProviderparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsAxisparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsAxisHighlightparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsClipPathparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsGridparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsLabelparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsLegendparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsLocalizationProviderparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsOverlayparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsReferenceLineparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsSurfaceparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsTextparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsTooltipparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsXAxisparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsYAxisparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/Gaugeparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/LineChartparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/PieChartparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/RadarChartparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ScatterChartparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/SparkLineChartparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/Toolbarparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-data-gridparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-data-grid-premiumparsed: 0B(0.00%) gzip: 🔺+5B(0.00%)
@mui/x-data-grid-premium/DataGridPremiumparsed: 0B(0.00%) gzip: 🔺+5B(0.00%)
@mui/x-data-grid-proparsed: 0B(0.00%) gzip: 🔺+5B(0.00%)
@mui/x-data-grid-pro/DataGridProparsed: 0B(0.00%) gzip: 🔺+6B(0.00%)
@mui/x-data-grid/DataGridparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/AdapterDateFnsparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/AdapterDateFnsJalaliparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/AdapterDayjsparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/AdapterLuxonparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/AdapterMomentparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/AdapterMomentHijriparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/AdapterMomentJalaaliparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/DateRangeCalendarparsed: 0B(0.00%) gzip: 🔺+5B(+0.02%)
@mui/x-date-pickers-pro/DateRangePickerDayparsed: 0B(0.00%) gzip: 🔺+4B(+0.05%)
@mui/x-date-pickers-pro/DateRangePickerDay2parsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/LocalizationProviderparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/PickersRangeCalendarHeaderparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/StaticDateRangePickerparsed: 0B(0.00%) gzip: 🔺+4B(+0.01%)
@mui/x-date-pickers/AdapterDateFnsparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/AdapterDateFnsBaseparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/AdapterDateFnsJalaliparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/AdapterDayjsparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/AdapterLuxonparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/AdapterMomentparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/AdapterMomentHijriparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/AdapterMomentJalaaliparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/DateCalendarparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/DayCalendarSkeletonparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/DigitalClockparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/LocalizationProviderparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/MonthCalendarparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/MultiSectionDigitalClockparsed: 0B(0.00%) gzip: 0B(0.00%)

Details of bundle changes

Generated by 🚫 dangerJS against a754030

@bernardobelchior bernardobelchior added type: enhancement This is not a bug, nor a new feature scope: charts Changes or issues related to the charts product labels Jun 11, 2025
Copy link

codspeed-hq bot commented Jun 11, 2025

CodSpeed Performance Report

Merging #18336 will not alter performance

Comparing bernardobelchior:zoom-slider-hide-values-outside-range (a754030) with master (098454f)

Summary

✅ 9 untouched benchmarks

@bernardobelchior bernardobelchior marked this pull request as ready for review June 11, 2025 15:57
);
}

export function calculateZoomFromPointImpl(
Copy link
Member Author

Choose a reason for hiding this comment

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

This separation makes it easier to unit test.

Copy link
Member

Choose a reason for hiding this comment

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

Did you also consider creating calculatePointFromZoomImpl() to simplify the computation in ChartAxisZoomSliderActiveTrack.tsx and have the conversion logic zoom <-> SVG in a single place

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think I understood your question. calculatePointFromZoom, which uses calculatePointFromZoomImpl is already being used in ChartAxisZoomSliderActiveTrack. Where exactly do you think it would make sense to add it?

Copy link
Member

@JCQuintas JCQuintas left a comment

Choose a reason for hiding this comment

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

Should we handle the case where the min/max changes but the current state is over the values? In the example below you can see the bar becomes a bit fidgety 😄

Screen.Recording.2025-06-11.at.19.31.41.mov

@bernardobelchior
Copy link
Member Author

Should we handle the case where the min/max changes but the current state is over the values? In the example below you can see the bar becomes a bit fidgety 😄

Screen.Recording.2025-06-11.at.19.31.41.mov

I think we should, but I think that's a different feature because it should also apply to the zoom data itself, not just visually to the slider, right?

If the zoom is uncontrolled, it should never break any constraint. Then there's the edge case where constraints are invalid: minSpan is 100 and minStart is 10. In that case we should throw in development. In production, I'm not sure. Maybe we should ignore minSpan in that case?

If the zoom is controlled, then we need to decide. My opinion is that we should always meet the constraints. If you have minStart 10 and provide a zoom start of 5, then you'll receive an onChange where zoom start is 10. I think this might be good because we can get controlled data from a URL search param which the end user can manipulate but we don't want the developer to have to worry about out of range values.

However, we can also consider accepting what the user defines. In the URL search param case from above, the developer would need to clamp the values manually.

@alexfauquette @JCQuintas what do you think?

@alexfauquette
Copy link
Member

I tend to think that a control logic should be as dumb as possible. The dev provides something => you apply it.

Otherwise, when they try to do something custom, they must handle our internal logic (and its potential future modifications).

If the zoom constraint got modified the onZoomChange could be called with the new state, but the controlled value stay the reference. Up to the dev to know if they want to apply it or a custom modification to respect the new rules they set themself.


The zoom slider uses the same limits as the zooming options. You can set the `minStart`, `maxEnd`, `minSpan`, and `maxSpan` properties on the axis config to restrict the zoom slider range.

Values outside the `minStart` and `maxEnd` range will not be displayed in the zoom slider.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Values outside the `minStart` and `maxEnd` range will not be displayed in the zoom slider.
The `minStart` and `maxEnd` define the extremum of the zoom slider.
Suggested change
Values outside the `minStart` and `maxEnd` range will not be displayed in the zoom slider.
The zoom slider does not display values outside the `minStart` and `maxEnd` range.

);
}

export function calculateZoomFromPointImpl(
Copy link
Member

Choose a reason for hiding this comment

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

Did you also consider creating calculatePointFromZoomImpl() to simplify the computation in ChartAxisZoomSliderActiveTrack.tsx and have the conversion logic zoom <-> SVG in a single place

@JCQuintas
Copy link
Member

I agree with @alexfauquette that the user-controlled one should be fairly dumb.

On the uncontrolled mode, maybe the minStart/maxEnd should have priority over the span. User could only care about where the ends go, eg: minSpan:100,maxSpan:100 to always lock the full zoom. Then minStart:50,maxEnd:100 to lock the zoom on the second half.

Though they could do that by having the zoom controlled 🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
scope: charts Changes or issues related to the charts product type: enhancement This is not a bug, nor a new feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants