Skip to content

feat(Chart): keep hidden of data value when update#878

Merged
ArgoZhang merged 3 commits intomasterfrom
feat-chart
Dec 30, 2025
Merged

feat(Chart): keep hidden of data value when update#878
ArgoZhang merged 3 commits intomasterfrom
feat-chart

Conversation

@ArgoZhang
Copy link
Copy Markdown
Member

@ArgoZhang ArgoZhang commented Dec 30, 2025

Link issues

fixes #877

Summary By Copilot

Regression?

  • Yes
  • No

Risk

  • High
  • Medium
  • Low

Verification

  • Manual (required)
  • Automated

Packaging changes reviewed?

  • Yes
  • No
  • N/A

☑️ Self Check before Merge

⚠️ Please check all items below before review. ⚠️

  • Doc is updated/provided or not needed
  • Demo is updated/provided or not needed
  • Merge the latest code from the main branch

Summary by Sourcery

Enhancements:

  • Synchronize each dataset's hidden property with the current legend item visibility during chart updates to maintain user-selected visibility state.

Copilot AI review requested due to automatic review settings December 30, 2025 04:47
@bb-auto bb-auto Bot added the enhancement New feature or request label Dec 30, 2025
@bb-auto bb-auto Bot added this to the v9.2.0 milestone Dec 30, 2025
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Dec 30, 2025

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Synchronizes dataset hidden state with the existing chart legend items during updates so that previously hidden series remain hidden, specifically for non-pie/doughnut charts, without altering other chart configuration behavior.

Sequence diagram for chart update preserving hidden legend state

sequenceDiagram
    participant Caller
    participant ChartInterop as ChartUpdateFunction
    participant DataStore as DataStore
    participant Chart as ChartInstance
    participant Legend as LegendItems

    Caller->>ChartInterop: update(id, option, method, angle)
    ChartInterop->>DataStore: Data.get(id)
    DataStore-->>ChartInterop: { invoke, chart }

    loop ForEachDataset
        ChartInterop->>Legend: find legendItem where legendItem.text == dataset.label
        alt LegendItemFound
            Legend-->>ChartInterop: legendItem.hidden
            ChartInterop->>ChartInterop: dataset.hidden = legendItem.hidden
        else LegendItemNotFound
            ChartInterop->>ChartInterop: leave dataset.hidden unchanged
        end
    end

    ChartInterop->>ChartInterop: op = getChartOption(option)
    ChartInterop->>Caller: handlerClickData(invoke, op, option.options.onClickDataMethod)
    ChartInterop->>Chart: apply updated options with preserved hidden flags
    Chart-->>Caller: chart updated with consistent hidden state
Loading

File-Level Changes

Change Details Files
Preserve per-dataset hidden/visible state when updating an existing chart instance.
  • Retrieve existing chart instance and legend items in the update function
  • For each incoming dataset, find the matching legend item by label on the existing chart
  • Copy the legend item's hidden flag back onto the corresponding incoming dataset before rebuilding options
  • Pass the adjusted option into the existing getChartOption/update pipeline to maintain behavior elsewhere
src/components/BootstrapBlazor.Chart/Components/Chart/Chart.razor.js

Assessment against linked issues

Issue Objective Addressed Explanation
#877 When updating a chart, preserve the hidden/visible state of each dataset instead of resetting it.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@ArgoZhang ArgoZhang merged commit b6df265 into master Dec 30, 2025
5 of 6 checks passed
@ArgoZhang ArgoZhang deleted the feat-chart branch December 30, 2025 04:47
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • When syncing hidden states in update, guard against cases where chart.legend or chart.legend.legendItems might be undefined to avoid runtime errors during initialization or non-standard configurations.
  • Matching legend items to datasets by text === d.label can break when labels are duplicated or localized; consider using the dataset index (e.g., legend item index) or another stable identifier instead of the label string.
  • Defensively handle cases where option.data is null/undefined or not an array before calling forEach to prevent unexpected runtime exceptions during update.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- When syncing hidden states in update, guard against cases where `chart.legend` or `chart.legend.legendItems` might be undefined to avoid runtime errors during initialization or non-standard configurations.
- Matching legend items to datasets by `text === d.label` can break when labels are duplicated or localized; consider using the dataset index (e.g., legend item index) or another stable identifier instead of the label string.
- Defensively handle cases where `option.data` is null/undefined or not an array before calling `forEach` to prevent unexpected runtime exceptions during update.

## Individual Comments

### Comment 1
<location> `src/components/BootstrapBlazor.Chart/Components/Chart/Chart.razor.js:493-495` </location>
<code_context>

 export function update(id, option, method, angle) {
-    const { invoke, chart } = Data.get(id)
+    const { invoke, chart } = Data.get(id);
+    option.data.forEach(d => {
+        const l = chart.legend.legendItems.find(i => i.text === d.label);
+        if(l) {
+            d.hidden = l.hidden;
</code_context>

<issue_to_address>
**issue (bug_risk):** Access to `chart.legend.legendItems` should be guarded to avoid runtime errors when legend is disabled or unavailable.

When the legend is disabled or altered by plugins, `chart.legend` / `chart.legend.legendItems` can be `undefined`, causing `update` to throw. Add a defensive check, e.g.:

```js
const { invoke, chart } = Data.get(id);
const legendItems = chart.legend && chart.legend.legendItems;

if (legendItems) {
  option.data.forEach(d => {
    const l = legendItems.find(i => i.text === d.label);
    if (l) d.hidden = l.hidden;
  });
}
```

This preserves the current behavior when a legend exists while preventing runtime errors when it does not.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Copy Markdown

Copilot AI left a comment

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 adds functionality to preserve the hidden state of chart data items when updating charts. When a user hides a dataset via the legend and then updates the chart data, the hidden state is now maintained instead of being reset. The version is bumped from 10.0.1 to 10.0.2.

Key changes:

  • Added logic in the update function to sync hidden states from existing legend items to incoming data
  • Removed a redundant Chinese comment that duplicated what the code clearly expresses

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
src/components/BootstrapBlazor.Chart/Components/Chart/Chart.razor.js Added logic to preserve hidden state of datasets during chart updates by matching legend item states; removed redundant comment
src/components/BootstrapBlazor.Chart/BootstrapBlazor.Chart.csproj Bumped version from 10.0.1 to 10.0.2

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

const { invoke, chart } = Data.get(id);
option.data.forEach(d => {
const l = chart.legend.legendItems.find(i => i.text === d.label);
if(l) {
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Missing space after 'if' keyword. JavaScript convention and most style guides recommend a space between 'if' and the opening parenthesis for better readability.

Suggested change
if(l) {
if (l) {

Copilot uses AI. Check for mistakes.
Comment on lines +494 to +499
option.data.forEach(d => {
const l = chart.legend.legendItems.find(i => i.text === d.label);
if(l) {
d.hidden = l.hidden;
}
});
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The code assumes option.data is iterable but doesn't check for null/undefined. If option.data is null or undefined, this will throw a runtime error. Add a defensive check to ensure option.data exists before calling forEach.

Suggested change
option.data.forEach(d => {
const l = chart.legend.legendItems.find(i => i.text === d.label);
if(l) {
d.hidden = l.hidden;
}
});
if (Array.isArray(option.data)) {
option.data.forEach(d => {
const l = chart.legend.legendItems.find(i => i.text === d.label);
if (l) {
d.hidden = l.hidden;
}
});
}

Copilot uses AI. Check for mistakes.
Comment on lines +494 to +496
option.data.forEach(d => {
const l = chart.legend.legendItems.find(i => i.text === d.label);
if(l) {
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The nested iteration pattern has O(n*m) time complexity where n is the number of data items and m is the number of legend items. For large datasets, consider creating a Map from legendItems indexed by text before the forEach loop to improve performance to O(n+m).

Suggested change
option.data.forEach(d => {
const l = chart.legend.legendItems.find(i => i.text === d.label);
if(l) {
const legendItems = chart && chart.legend && Array.isArray(chart.legend.legendItems)
? chart.legend.legendItems
: [];
const legendItemMap = new Map();
legendItems.forEach(item => {
legendItemMap.set(item.text, item);
});
option.data.forEach(d => {
const l = legendItemMap.get(d.label);
if (l) {

Copilot uses AI. Check for mistakes.
Comment on lines +494 to +499
option.data.forEach(d => {
const l = chart.legend.legendItems.find(i => i.text === d.label);
if(l) {
d.hidden = l.hidden;
}
});
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The code lacks proper null/undefined safety checks. If chart.legend is null/undefined or chart.legend.legendItems is null/undefined, this code will throw a runtime error. This can happen when the legend is disabled (showLegend: false) or in certain chart configurations. Add defensive checks to ensure these properties exist before attempting to access them.

Suggested change
option.data.forEach(d => {
const l = chart.legend.legendItems.find(i => i.text === d.label);
if(l) {
d.hidden = l.hidden;
}
});
const legendItems = chart && chart.legend && chart.legend.legendItems;
if (Array.isArray(option.data) && Array.isArray(legendItems)) {
option.data.forEach(d => {
const l = legendItems.find(i => i.text === d.label);
if (l) {
d.hidden = l.hidden;
}
});
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(Chart): keep hidden of data value when update

2 participants