Skip to content

feat(Table): update reset column width/order method#7850

Merged
ArgoZhang merged 5 commits intomainfrom
feat-table-js
Apr 7, 2026
Merged

feat(Table): update reset column width/order method#7850
ArgoZhang merged 5 commits intomainfrom
feat-table-js

Conversation

@ArgoZhang
Copy link
Copy Markdown
Member

@ArgoZhang ArgoZhang commented Apr 7, 2026

Link issues

fixes #7849

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

Update table column reset behavior to reapply persisted column order, width, and visibility on parameter changes and adjust default width calculation in the JavaScript table utilities.

Enhancements:

  • Refactor first-render tracking in the table component and trigger column reset logic on subsequent parameter updates.
  • Extract shared column reset logic into a dedicated method to reload column configuration from browser storage and apply default sorting.
  • Simplify table width reset and default width calculation in the JavaScript helper by computing widths directly from col elements and removing unused helpers.

Copilot AI review requested due to automatic review settings April 7, 2026 02:45
@bb-auto bb-auto bot added the enhancement New feature or request label Apr 7, 2026
@bb-auto bb-auto bot added this to the v10.5.0 milestone Apr 7, 2026
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai bot commented Apr 7, 2026

Reviewer's Guide

Refactors the table’s first-render handling and centralizes column reset logic so that column order/width/visibility are reloaded both after first render and on subsequent parameter updates, while simplifying JS table width reset calculations to rely directly on col widths.

Class diagram for updated Table component first-render and column reset logic

classDiagram
    class Table_TItem_Component {
        - bool _firstRender
        - List~ITableColumn~ Columns
        - DynamicContext DynamicContext
        - bool AutoGenerateColumns
        - Func~IEnumerable~ITableColumn~~ ColumnOrderCallback
        - Func~IEnumerable~ITableColumn~,Task~ OnColumnCreating
        - string SortName
        - SortOrder SortOrder
        - bool ShowSkeleton
        - bool IsTree
        - object SearchModel
        - IEnumerable~ITableColumn~ _searchItems
        - bool _autoQuery
        + Table_TItem_Component()
        - void OnInitParameters()
        + void OnParametersSet()
        + Task OnParametersSetAsync()
        + Task OnAfterRenderAsync(bool firstRender)
        - Task OnTableRenderAsync(bool firstRender)
        - Task ProcessFirstRender()
        - Task OnTableColumnReset()
        - void ResetDynamicContext()
        - void InternalResetVisibleColumns(List~ITableColumn~ cols)
        - Task ReloadColumnOrdersFromBrowserAsync(List~ITableColumn~ cols)
        - Task ReloadColumnWidthFromBrowserAsync(List~ITableColumn~ cols)
        - Task ReloadColumnVisibleFromBrowserAsync()
        - TItem CreateTItem()
        - object CreateSearchModel()
    }

    class ITableColumn {
        <<interface>>
        + bool Sortable
        + bool DefaultSort
        + SortOrder DefaultSortOrder
        + string GetFieldName()
    }

    class DynamicContext {
        + IEnumerable~ITableColumn~ GetColumns()
    }

    class Utility {
        + static IEnumerable~ITableColumn~ GetTableColumns~TItem~(IEnumerable~ITableColumn~ columns)
    }

    class ColumnExtensions {
        + static IEnumerable~ITableColumn~ OrderFunc(this IEnumerable~ITableColumn~ columns)
    }

    class SortOrder {
        <<enum>>
        Ascending
        Descending
        None
    }

    Table_TItem_Component --> ITableColumn : uses
    Table_TItem_Component --> DynamicContext : uses
    Table_TItem_Component --> Utility : uses
    Table_TItem_Component --> ColumnExtensions : uses
    ITableColumn --> SortOrder : uses
Loading

Flow diagram for simplified JS table width reset and default width calculation

flowchart TD
    A["resetTableWidth(table)"] --> B["Iterate table.tables"]
    B --> C["Find COLGROUP child of table element"]
    C -->|group exists| D["Compute total width from group children"]
    C -->|no group| B

    D --> E["For each col in group.children<br/>parseInt(col.style.width)"]
    E --> F{"Is width NaN?"}
    F -->|yes| G["Use default 100"]
    F -->|no| H["Use parsed width"]
    G --> I["Accumulate width"]
    H --> I["Accumulate width"]
    I --> J["Set t.style.width to total width"]
    J --> K["saveColumnWidth(table)"]

    L["setTableDefaultWidth(table)"] --> M{"table.tables.length > 0<br/>and first table is visible?"}
    M -->|no| N["Return"]
    M -->|yes| O["Get options.scrollWidth and options.columnMinWidth"]
    O --> P["Query all col elements from first table"]
    P --> Q["For each col<br/>parseFloat(col.style.width)"]
    Q --> R{"Is width NaN?"}
    R -->|yes| S["Use columnMinWidth"]
    R -->|no| T["Use parsed width"]
    S --> U["Accumulate tableWidth"]
    T --> U["Accumulate tableWidth"]
    U --> V{"tableWidth > table.el.offsetWidth?"}
    V -->|yes| W["Set table.tables[0].style.width to tableWidth"]
    V -->|no| X["Do not set explicit width"]
Loading

File-Level Changes

Change Details Files
Refactor first-render tracking and trigger column reset logic on subsequent parameter changes.
  • Replace protected FirstRender property with a private _firstRender field and update all references accordingly.
  • Invoke dynamic column reset (ResetDynamicContext and related state) only when _firstRender is false in OnParametersSet.
  • Add OnParametersSetAsync override that, when not the first render, reloads column order/width/visibility from the browser by calling the new OnTableColumnReset method.
  • Ensure the Razor markup conditionally renders skeleton content based on _firstRender instead of the removed FirstRender property.
src/BootstrapBlazor/Components/Table/Table.razor.cs
src/BootstrapBlazor/Components/Table/Table.razor
Extract and reuse table column reset pipeline previously embedded in ProcessFirstRender.
  • Introduce private async OnTableColumnReset method that builds the column list (dynamic/auto-generated/static), applies ColumnOrderCallback, reloads column order/width/visibility from browser storage, invokes OnColumnCreating, resets visible columns, reorders columns, and sets default sort field and order.
  • Replace the inline first-render column setup in ProcessFirstRender with a call to OnTableColumnReset after setting _firstRender to false, keeping loading and auto-query behavior intact.
src/BootstrapBlazor/Components/Table/Table.razor.cs
Simplify client-side table width reset and default width calculation to depend only on existing col widths, removing DOM measurement helpers.
  • Update resetTableWidth to sum explicit col widths from the existing COLGROUP instead of using getLastColgroup and getTableWidthByColumnGroup, and always set table width based on that sum before saving column widths.
  • Remove getLastColgroup, getTableWidthByColumnGroup, and getColumnRenderWidth helper functions as they are no longer used.
  • Adjust setTableDefaultWidth to compute total width by summing each col’s style.width with a columnMinWidth fallback, instead of querying header/body cell widths.
src/BootstrapBlazor/Components/Table/Table.razor.js

Assessment against linked issues

Issue Objective Addressed Explanation
#7849 Update the Table component’s column order/width reset logic so that column configuration (order, width, visibility, default sort) is reloaded not only on first render but also when parameters change.
#7849 Adjust the JavaScript table width reset behavior to compute and set table width directly from the col elements’ widths, simplifying and updating the resetTableWidth and setTableDefaultWidth logic.

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

Copy link
Copy Markdown
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 - I've left some high level feedback:

  • Changing FirstRender from a protected property to a private field (_firstRender) may be a breaking change for any derived components that referenced it; consider keeping a protected read-only property or compatibility shim if inheritance is expected.
  • In resetTableWidth you use parseInt(col.style.width) while setTableDefaultWidth uses parseFloat; for consistency and to handle non-integer widths, consider using parseFloat in both places.
  • OnParametersSetAsync now calls OnTableColumnReset on every non-first parameter set, which could be expensive; consider guarding this with a more specific condition (e.g., only when relevant parameters change) or an explicit flag to avoid unnecessary recomputation.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Changing `FirstRender` from a protected property to a private field (`_firstRender`) may be a breaking change for any derived components that referenced it; consider keeping a protected read-only property or compatibility shim if inheritance is expected.
- In `resetTableWidth` you use `parseInt(col.style.width)` while `setTableDefaultWidth` uses `parseFloat`; for consistency and to handle non-integer widths, consider using `parseFloat` in both places.
- `OnParametersSetAsync` now calls `OnTableColumnReset` on every non-first parameter set, which could be expensive; consider guarding this with a more specific condition (e.g., only when relevant parameters change) or an explicit flag to avoid unnecessary recomputation.

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.

@ArgoZhang ArgoZhang merged commit 6519f31 into main Apr 7, 2026
4 checks passed
@ArgoZhang ArgoZhang deleted the feat-table-js branch April 7, 2026 02:47
Copy link
Copy Markdown
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

Updates the Table component’s column reset behavior (width/order/visibility) to address issue #7849 by refactoring the column-reset pipeline in the Blazor component and adjusting how table widths are recalculated in the JS interop layer.

Changes:

  • Refactors column reset/reload logic into a shared OnTableColumnReset() method and invokes it on first render and subsequent parameter updates.
  • Updates JS logic for recalculating table widths (including default-width computation).
  • Bumps package version to 10.5.1-beta06.

Reviewed changes

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

File Description
src/BootstrapBlazor/Components/Table/Table.razor.js Changes table width reset/default-width calculations used by resize/reset flows.
src/BootstrapBlazor/Components/Table/Table.razor.cs Refactors first-render tracking and introduces OnTableColumnReset() + OnParametersSetAsync() reload behavior.
src/BootstrapBlazor/Components/Table/Table.razor Switches first-render UI gating from FirstRender to _firstRender.
src/BootstrapBlazor/BootstrapBlazor.csproj Version bump.

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

Comment on lines +1156 to +1164
protected override async Task OnParametersSetAsync()
{
await base.OnParametersSetAsync();

if (!_firstRender)
{
// 重新读取浏览器设置
await OnTableColumnReset();
}
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

OnParametersSetAsync calls base.OnParametersSetAsync(), and ComponentBase's base implementation invokes OnParametersSet(). Because this component already overrides OnParametersSet(), this will cause OnParametersSet() to run twice per parameter update, duplicating work (and potentially triggering side effects) after the first render. Remove the base.OnParametersSetAsync() call (or move all logic into a single override) so OnParametersSet executes only once per update.

Copilot uses AI. Check for mistakes.
protected bool FirstRender { get; set; } = true;
private bool _firstRender = true;

/// <summary>
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

The protected FirstRender property was removed and replaced with a private field. Since Table is a public component, removing a protected member is a breaking change for anyone deriving from Table and using FirstRender in subclasses. Consider keeping the protected property (possibly forwarding to _firstRender and marking it [Obsolete]) to preserve backward compatibility.

Suggested change
/// <summary>
/// <summary>
/// <para lang="zh">获得/设置 首次渲染标志。已弃用,请勿在派生类中继续使用。</para>
/// <para lang="en">Gets or sets the first-render flag. Obsolete; do not continue using this in derived classes.</para>
/// </summary>
[Obsolete("已弃用,请勿继续使用 FirstRender。Deprecated, please do not continue using FirstRender.")]
protected bool FirstRender
{
get => _firstRender;
set => _firstRender = value;
}
/// <summary>

Copilot uses AI. Check for mistakes.
Comment on lines +1290 to +1295
// set default sortName
var col = Columns.Find(i => i is { Sortable: true, DefaultSort: true });
if (col != null)
{
SortName = col.GetFieldName();
SortOrder = col.DefaultSortOrder;
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

OnTableColumnReset resets SortName/SortOrder to the default sort column every time it runs. Since this method is now also invoked from OnParametersSetAsync on subsequent parameter updates, it can unintentionally override user-driven sorting state. Only apply the default sort when sorting is currently unset (e.g., SortName is null/empty and SortOrder is Unset) or restrict this logic to first render.

Suggested change
// set default sortName
var col = Columns.Find(i => i is { Sortable: true, DefaultSort: true });
if (col != null)
{
SortName = col.GetFieldName();
SortOrder = col.DefaultSortOrder;
// set default sortName only when sort is not already initialized
if (string.IsNullOrEmpty(SortName) && SortOrder == SortOrder.Unset)
{
var col = Columns.Find(i => i is { Sortable: true, DefaultSort: true });
if (col != null)
{
SortName = col.GetFieldName();
SortOrder = col.DefaultSortOrder;
}

Copilot uses AI. Check for mistakes.
Comment on lines 538 to 552
const resetTableWidth = table => {
const { options: { scrollWidth } } = table;
table.tables.forEach(t => {
const group = [...t.children].find(i => i.nodeName === 'COLGROUP')
if (group) {
const colgroup = getLastColgroup(t, group);
if (colgroup) {
colgroup.style = null;
}
const width = getTableWidthByColumnGroup(t, 100);
if (width >= t.parentElement.offsetWidth + scrollWidth) {
t.style.width = `${width}px`;
}
else {
t.style.width = null;
}
let width = 0;
[...group.children].forEach(col => {
let colWidth = parseInt(col.style.width);
if (isNaN(colWidth)) {
colWidth = 100;
}
width += colWidth;
})
t.style.width = `${width}px`;

saveColumnWidth(table);
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

resetTableWidth now always forces each table's width to the sum of colgroup widths and no longer accounts for fixed-header mode (header vs body width needing scrollWidth compensation) or the previous conditional behavior that cleared width when not needed. This can cause header/body misalignment and unnecessary horizontal scrolling. Align this logic with setTableDefaultWidth / resize-drag behavior (e.g., subtract scrollWidth for the fixed-body table and only set explicit width when required).

Copilot uses AI. Check for mistakes.
Comment on lines 1051 to +1058
if (table.tables.length > 0 && isVisible(table.tables[0])) {
const { scrollWidth, columnMinWidth } = table.options;
const tableWidth = getTableWidthByColumnGroup(table.tables[0], columnMinWidth);
const tableWidth = [...table.tables[0].querySelectorAll('col')]
.map(i => {
const colWidth = parseFloat(i.style.width);
return isNaN(colWidth) ? columnMinWidth : colWidth;
})
.reduce((accumulator, val) => accumulator + val, 0);
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

setTableDefaultWidth now calculates tableWidth purely from col.style.width with a columnMinWidth fallback. For columns without an explicit width (style is empty), this can significantly overestimate the real rendered width and force an unnecessary fixed width + horizontal scroll. The previous implementation measured actual header/body cell widths when style width was absent. Consider restoring that measurement-based fallback (offsetWidth) rather than defaulting to columnMinWidth for unset widths.

Copilot uses AI. Check for mistakes.
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 7, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (429c484) to head (329efd7).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #7850   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          764       764           
  Lines        34165     34175   +10     
  Branches      4704      4705    +1     
=========================================
+ Hits         34165     34175   +10     
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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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(Table): update reset column width/order method

2 participants