Skip to content

Conversation

@ArgoZhang
Copy link
Member

@ArgoZhang ArgoZhang commented Oct 29, 2025

Link issues

fixes #7021

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

Persist and restore table column visibility settings via localStorage and improve dynamic template event bindings

New Features:

  • Restore persisted table column visibility state from browser localStorage when ShowColumnList is enabled

Bug Fixes:

  • Prevent JSON deserialization errors in ColumnVisibleItemConverter by handling null names

Enhancements:

  • Adjust ColumnVisibleItemConverter to default null field names to empty string during deserialization
  • Use generic ValidateBase<>.OnValueChanged callback name for dynamic and edit templates
  • Add System.Text.Json import to the Checkbox component for JSON operations

Copilot AI review requested due to automatic review settings October 29, 2025 03:31
@bb-auto bb-auto bot added the enhancement New feature or request label Oct 29, 2025
@bb-auto bb-auto bot added this to the 9.11.0 milestone Oct 29, 2025
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Oct 29, 2025

Reviewer's Guide

This PR enhances the Table component by introducing localStorage-backed persistence for column visibility, refines JSON deserialization for ColumnVisibleItem, and generalizes OnValueChanged parameter handling in edit templates, with a minor import addition.

Sequence diagram for Table column visibility persistence with localStorage

sequenceDiagram
    participant TableComponent
    participant JSRuntime
    participant JsonSerializer
    TableComponent->>JSRuntime: Invoke localStorage.getItem("bb-table-column-visiable-<ClientTableName>")
    JSRuntime-->>TableComponent: Return jsonData
    alt jsonData is not empty
        TableComponent->>JsonSerializer: Deserialize jsonData to List<ColumnVisibleItem>
        JsonSerializer-->>TableComponent: Return List<ColumnVisibleItem>
        loop For each item in deserialized list
            TableComponent->>TableComponent: Update column visibility
        end
    end
Loading

Entity relationship diagram for persisted column visibility

erDiagram
    TABLE {
        string ClientTableName
        bool ShowColumnList
    }
    COLUMN_VISIBLE_ITEM {
        string Name
        bool Visible
        string DisplayName
    }
    TABLE ||--o{ COLUMN_VISIBLE_ITEM : has
    LOCAL_STORAGE {
        string key
        string value
    }
    TABLE ||--o{ LOCAL_STORAGE : persists visibility
    LOCAL_STORAGE ||--o{ COLUMN_VISIBLE_ITEM : stores list
Loading

Class diagram for updated ColumnVisibleItemConverter and Table component

classDiagram
    class ColumnVisibleItemConverter {
        +Read(ref Utf8JsonReader, Type, JsonSerializerOptions) : ColumnVisibleItem
        +Write(Utf8JsonWriter, ColumnVisibleItem, JsonSerializerOptions) : void
    }
    class ColumnVisibleItem {
        +Name : string
        +Visible : bool
        +DisplayName : string
    }
    ColumnVisibleItemConverter --> ColumnVisibleItem : uses

    class Table_TItem {
        -InternalResetVisibleColumns(columns, items)
        -SetDynamicEditTemplate()
        -SetEditTemplate()
    }
    Table_TItem --> ColumnVisibleItem : uses
    Table_TItem --> ColumnVisibleItemConverter : uses
Loading

File-Level Changes

Change Details Files
Persist and restore user-adjusted column visibility via localStorage
  • Invoke JSRuntime to read stored JSON for column visibility
  • Deserialize JSON into ColumnVisibleItem list with error handling
  • Merge persisted visibility settings into current column definitions
src/BootstrapBlazor/Components/Table/Table.razor.cs
Generalize OnValueChanged parameter in edit templates
  • Replace ValidateBase.OnValueChanged with generic ValidateBase<>.OnValueChanged in dynamic template
  • Apply same change in standard edit template setup
src/BootstrapBlazor/Components/Table/Table.razor.cs
Ensure ColumnVisibleItem converter handles null names
  • Default name parameter to empty string when deserialized name is null
src/BootstrapBlazor/Converter/ColumnVisibleItemConverter.cs
Add missing import for JSON handling
  • Add using System.Text.Json directive
src/BootstrapBlazor/Components/Table/Table.razor.Checkbox.cs

Assessment against linked issues

Issue Objective Addressed Explanation
#7021 Remove the async keyword from the ResetDynamicContext method in Table component code. The diff does not show any changes to a method named ResetDynamicContext, nor does it remove an async keyword from such a method. The changes are related to InternalResetVisibleColumns and other Table component logic, but do not revert the addition of async to ResetDynamicContext.

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

sourcery-ai[bot]
sourcery-ai bot previously approved these changes Oct 29, 2025
Copy link
Contributor

@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 there - I've reviewed your changes - here's some feedback:

  • The invocations of nameof(ValidateBase<>.OnValueChanged) won’t compile since open generics aren’t allowed in nameof; please restore a concrete type argument or use a valid type name.
  • The localStorage key is spelled "bb-table-column-visiable-" with a typo; correcting it to "bb-table-column-visible-" will prevent mismatches.
  • Silently catching all exceptions during JSON deserialization can hide issues—consider narrowing the catch clause or logging errors for easier debugging.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The invocations of nameof(ValidateBase<>.OnValueChanged) won’t compile since open generics aren’t allowed in nameof; please restore a concrete type argument or use a valid type name.
- The localStorage key is spelled "bb-table-column-visiable-" with a typo; correcting it to "bb-table-column-visible-" will prevent mismatches.
- Silently catching all exceptions during JSON deserialization can hide issues—consider narrowing the catch clause or logging errors for easier debugging.

## Individual Comments

### Comment 1
<location> `src/BootstrapBlazor/Converter/ColumnVisibleItemConverter.cs:51` </location>
<code_context>
             }
         }
-        return new ColumnVisibleItem(name, visible);
+        return new ColumnVisibleItem(name ?? "", visible);
     }

</code_context>

<issue_to_address>
**suggestion (bug_risk):** Defaulting name to empty string may mask data issues.

Consider logging or explicitly handling cases where 'name' is missing or null to prevent silent data inconsistencies.

Suggested implementation:

```csharp
        if (name == null)
        {
            // Log a warning about missing column name
            System.Diagnostics.Debug.WriteLine("Warning: ColumnVisibleItemConverter encountered a null 'name' value during deserialization.");
            // Optionally, you could throw an exception instead:
            // throw new JsonException("ColumnVisibleItem 'name' property is missing or null.");
        }
        return new ColumnVisibleItem(name ?? "", visible);

```

If you have a preferred logging framework (e.g., ILogger), replace `System.Diagnostics.Debug.WriteLine` with your logger's warning method. If you want stricter handling, uncomment the exception line and remove the log statement.
</issue_to_address>

### Comment 2
<location> `src/BootstrapBlazor/Components/Table/Table.razor.cs:1281` </location>
<code_context>
     private async Task InternalResetVisibleColumns(List<ITableColumn> columns, IEnumerable<ColumnVisibleItem>? items = null)
     {
         var cols = columns.Select(i => new ColumnVisibleItem(i.GetFieldName(), i.GetVisible()) { DisplayName = i.GetDisplayName() }).ToList();
</code_context>

<issue_to_address>
**issue (complexity):** Consider extracting local storage access and visibility merging logic into helper methods to simplify InternalResetVisibleColumns.

Extract the local-storage lookup, deserialization and merge into two small helpers. That collapses the nested `if`/`try`/`foreach` into a straight-line call, preserves everything and makes testing/logging easier.

```csharp
private async Task<List<ColumnVisibleItem>?> FetchStoredVisibilityAsync(string tableName)
{
    var key  = $"bb-table-column-visibility-{tableName}";
    var json = await JSRuntime.InvokeAsync<string>("localStorage.getItem", key);
    if (string.IsNullOrWhiteSpace(json)) 
        return null;

    try
    {
        return JsonSerializer.Deserialize<List<ColumnVisibleItem>>(json, _serializerOption);
    }
    catch (JsonException ex)
    {
        // optional: log.Warning(...) or remove if you truly want silent failures
        return null;
    }
}

private static void ApplyStoredVisibility(
    List<ColumnVisibleItem> current,
    IEnumerable<ColumnVisibleItem> stored)
{
    foreach (var s in stored)
    {
        var c = current.FirstOrDefault(x => 
            x.Name == s.Name && x.DisplayName == s.DisplayName);
        if (c != null)
            c.Visible = s.Visible;
    }
}
```

Then simplify `InternalResetVisibleColumns` to:

```csharp
private async Task InternalResetVisibleColumns(
    List<ITableColumn> columns,
    IEnumerable<ColumnVisibleItem>? items = null)
{
    var cols = columns
        .Select(i => new ColumnVisibleItem(i.GetFieldName(), i.GetVisible())
        {
            DisplayName = i.GetDisplayName()
        })
        .ToList();

    if (ClientTableName != null && ShowColumnList)
    {
        var stored = await FetchStoredVisibilityAsync(ClientTableName);
        if (stored != null)
            ApplyStoredVisibility(cols, stored);
    }

    if (items != null)
    {
        foreach (var col in cols)
        {
            var overrideItem = items.FirstOrDefault(i => i.Name == col.Name);
            if (overrideItem != null)
            {
                col.Visible     = overrideItem.Visible;
                col.DisplayName = string.IsNullOrEmpty(overrideItem.DisplayName)
                    ? col.DisplayName
                    : overrideItem.DisplayName;
            }
        }
    }

    // ... rest of method
}
```
</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.

This reverts commit cb0f351.

# Conflicts:
#	src/BootstrapBlazor/Components/Table/Table.razor.Checkbox.cs
Copy link
Contributor

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 persist table column visibility settings to browser localStorage and makes minor improvements to code documentation and type references.

  • Added localStorage persistence for column visibility settings in Table component
  • Added XML documentation for ColumnVisibleItemConverter class
  • Updated generic type references from ValidateBase<string> to ValidateBase<> for better type flexibility

Reviewed Changes

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

File Description
src/BootstrapBlazor/Converter/ColumnVisibleItemConverter.cs Added XML documentation and null-coalescing for name parameter
src/BootstrapBlazor/Components/Table/Table.razor.cs Implemented localStorage retrieval of column visibility settings and updated ValidateBase generic type references
src/BootstrapBlazor/Components/Table/Table.razor.Checkbox.cs Added System.Text.Json using directive
Comments suppressed due to low confidence (5)

src/BootstrapBlazor/Components/Table/Table.razor.cs:1287

  • Corrected spelling of 'visiable' to 'visible' in localStorage key name.
            {

src/BootstrapBlazor/Components/Table/Table.razor.cs:1295

  • Missing space after 'if' keyword. Should be 'if (ret != null)' to follow C# coding conventions.
                    }

src/BootstrapBlazor/Components/Table/Table.razor.cs:1294

  • The new localStorage column visibility persistence feature lacks test coverage. Consider adding tests to verify that column visibility is correctly loaded from localStorage and applied to the columns.
            {
                var item = items.FirstOrDefault(i => i.Name == column.Name);
                if (item != null)
                {
                    column.Visible = item.Visible;
                    if (!string.IsNullOrEmpty(item.DisplayName))
                    {
                        column.DisplayName = item.DisplayName;

src/BootstrapBlazor/Components/Table/Table.razor.cs:1294

  • Poor error handling: empty catch block.
                        column.DisplayName = item.DisplayName;

src/BootstrapBlazor/Components/Table/Table.razor.cs:1294

  • Generic catch clause.
                        column.DisplayName = item.DisplayName;

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

@codecov
Copy link

codecov bot commented Oct 29, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (b3f199e) to head (59aaa2e).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #7022   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          741       743    +2     
  Lines        32397     32452   +55     
  Branches      4485      4498   +13     
=========================================
+ Hits         32397     32452   +55     
Flag Coverage Δ
BB 100.00% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ArgoZhang ArgoZhang merged commit f131098 into main Oct 29, 2025
7 checks passed
@ArgoZhang ArgoZhang deleted the refactor-table branch October 29, 2025 04:41
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.

refactor(Table): revert add async keyword for ResetDynamicContext

3 participants