Skip to content

Conversation

jiji-hoon96
Copy link

@jiji-hoon96 jiji-hoon96 commented Sep 2, 2025

Add comprehensive guide for Excel-like sorting behavior Include complete working example with TypeScript
Reference community solution from issue #6061
Update API docs to clarify sortUndefined limitations

Refs: #6061

Summary by CodeRabbit

  • Documentation
    • Added a new guide on Excel-like sorting to consistently place empty cells (null/undefined) at the bottom across sort directions, with a custom sorting function and full React example.
    • Clarified that sortUndefined only affects undefined values, not null, and linked to the Excel-like Sorting Guide for handling both.
    • Included step-by-step instructions, global configuration tips, and best practices for normalizing empty values and customizing cell display.

Add comprehensive guide for Excel-like sorting behavior
Include complete working example with TypeScript
Reference community solution from issue TanStack#6061
Update API docs to clarify sortUndefined limitations

Refs: TanStack#6061
Copy link

coderabbitai bot commented Sep 2, 2025

Walkthrough

Documentation updates: one API doc clarified the scope of sortUndefined (undefined-only, not null) with a link to a new guide. A new guide was added demonstrating an Excel-like sorting approach using a custom sorting function to place null/undefined values last across sort directions.

Changes

Cohort / File(s) Summary of Changes
API Docs clarification
docs/api/features/sorting.md
Added a note under sortUndefined specifying it only affects undefined, not null; linked to the Excel-like Sorting Guide.
New guide: Excel-like sorting
docs/guide/excel-like-sorting.md
Added a guide introducing a custom excelLikeSortingFn to treat null/undefined as empty and always sort them last; includes usage steps, config examples, and a full React example.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI as Table UI
  participant Core as Table Core
  participant SortFn as excelLikeSortingFn

  User->>UI: Click column header to sort
  UI->>Core: Request sorted row model
  Core->>SortFn: Compare a.value vs b.value
  alt Either value is null or undefined
    note over SortFn: Treat null/undefined as "empty"
    SortFn-->>Core: Return ordering so empties are last
  else Both values non-empty
    SortFn-->>Core: Return standard comparison result
  end
  Core-->>UI: Sorted rows (empties last)
  UI-->>User: Render updated table
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Poem

In rows I hop, through nulls I pass,
Sorting carrots, leaves, and glass—
Undefined? I nudge to last,
Nulls as well, both gently cast.
Click, click—tables dance in line;
Excel-like order, crisp and fine.
🥕✨

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
docs/guide/excel-like-sorting.md (2)

56-73: Reframe global config section

If you adopt normalization + sortUndefined: 'last', a global custom sortingFn is unnecessary. Suggest noting that Step 3 is optional only when you choose the comparator approach (asc-only behavior).


192-201: Tiny copy edit and spacing

Add a blank line before “Credits” for consistent section spacing.

 - `cell` function controls display of empty values
-
 ## Credits
docs/api/features/sorting.md (1)

142-143: Good clarification; consider adding a quick tip

The note is accurate and the link path looks correct. Consider adding a one-liner tip: “Normalize null→undefined in accessorFn and set sortUndefined: 'last' to keep empty values last for both directions.”

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9c62cf2 and ba25124.

📒 Files selected for processing (2)
  • docs/api/features/sorting.md (1 hunks)
  • docs/guide/excel-like-sorting.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
docs/guide/excel-like-sorting.md

[grammar] ~197-~197: There might be a mistake here.
Context: ...zes undefined to null - cell function controls display of empty values ## Credits Th...

(QB_NEW_EN)

Comment on lines +17 to +36
```tsx
const excelLikeSortingFn = (rowA, rowB, columnId) => {
const a = rowA.getValue(columnId);
const b = rowB.getValue(columnId);

// Check for empty values (null, undefined)
const aEmpty = a == null;
const bEmpty = b == null;

// If both are empty, they're equal
if (aEmpty && bEmpty) return 0;

// Empty values always go to bottom
if (aEmpty) return 1;
if (bEmpty) return -1;

// Normal comparison for non-empty values
return a < b ? -1 : a > b ? 1 : 0;
};
```
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Custom sortingFn cannot keep nulls “always last” in both directions

Table desc sorting inverts the comparator result, so this function will place null/undefined at the top when sorting desc. It contradicts the stated “always at the bottom regardless of sort direction.” Use sortUndefined: 'last' and normalize null→undefined instead of relying on a comparator to enforce direction-agnostic placement.

Apply this diff to mark the function as optional and clarify intent:

-### Step 1: Create a Custom Sorting Function
+### Step 1 (Optional): Custom Sorting Function
@@
-  // Empty values always go to bottom
+  // NOTE: This keeps empties last for ascending only.
+  // For "always last" in both directions, prefer normalizing to `undefined`
+  // and using `sortUndefined: 'last'` (see next step).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```tsx
const excelLikeSortingFn = (rowA, rowB, columnId) => {
const a = rowA.getValue(columnId);
const b = rowB.getValue(columnId);
// Check for empty values (null, undefined)
const aEmpty = a == null;
const bEmpty = b == null;
// If both are empty, they're equal
if (aEmpty && bEmpty) return 0;
// Empty values always go to bottom
if (aEmpty) return 1;
if (bEmpty) return -1;
// Normal comparison for non-empty values
return a < b ? -1 : a > b ? 1 : 0;
};
```
### Step 1 (Optional): Custom Sorting Function
🤖 Prompt for AI Agents
In docs/guide/excel-like-sorting.md around lines 17 to 36, the provided
comparator attempts to force null/undefined always last but table descending
sort inverts comparator results so it fails; update the docs to mark the custom
sortingFn as optional, remove the claim that the comparator alone will keep
nulls last in both directions, and instead show/describe normalizing
null→undefined and using the built-in option sortUndefined: 'last' for
direction-agnostic placement; keep the comparator example only as an optional
fallback for custom value comparison (not for controlling undefined ordering).

Comment on lines +41 to +53
const columns = [
{
id: 'price',
accessorFn: row => row.price ?? null,
header: 'Price',
cell: ({ getValue }) => {
const value = getValue();
return value == null ? '-' : `$${value}`;
},
sortingFn: excelLikeSortingFn,
sortUndefined: 'last'
}
];
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Normalize null→undefined and drop sortingFn

To keep empty values at the bottom for both asc/desc, normalize null to undefined and rely on sortUndefined: 'last'. Current code does the opposite (undefined→null), making sortUndefined ineffective for those values.

 const columns = [
   {
     id: 'price',
-    accessorFn: row => row.price ?? null,
+    accessorFn: row => (row.price == null ? undefined : row.price),
     header: 'Price',
     cell: ({ getValue }) => {
       const value = getValue();
-      return value == null ? '-' : `$${value}`;
+      return value == null ? '-' : `$${value}`;
     },
-    sortingFn: excelLikeSortingFn,
     sortUndefined: 'last'
   }
 ];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const columns = [
{
id: 'price',
accessorFn: row => row.price ?? null,
header: 'Price',
cell: ({ getValue }) => {
const value = getValue();
return value == null ? '-' : `$${value}`;
},
sortingFn: excelLikeSortingFn,
sortUndefined: 'last'
}
];
const columns = [
{
id: 'price',
accessorFn: row => (row.price == null ? undefined : row.price),
header: 'Price',
cell: ({ getValue }) => {
const value = getValue();
return value == null ? '-' : `$${value}`;
},
sortUndefined: 'last'
}
];
🤖 Prompt for AI Agents
In docs/guide/excel-like-sorting.md around lines 41 to 53, the accessor
currently converts undefined to null and also sets a custom sortingFn, which
prevents the built-in sortUndefined: 'last' behavior from working; change the
accessorFn to normalize empty values to undefined (e.g., return row.price ??
undefined) and remove the sortingFn property so the column relies on
sortUndefined: 'last' to keep empty values at the bottom for both ascending and
descending sorts.

Comment on lines +95 to +107
function ExcelSortingTable() {
// Excel-like sorting function
const excelLikeSortingFn = (rowA, rowB, columnId) => {
const a = rowA.getValue(columnId);
const b = rowB.getValue(columnId);

if (a == null && b == null) return 0;
if (a == null) return 1;
if (b == null) return -1;

return a < b ? -1 : a > b ? 1 : 0;
};

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove the per-example sortingFn; rely on normalization + sortUndefined

The inline excelLikeSortingFn still fails “always last” for nulls on desc. Simplify by removing it.

 function ExcelSortingTable() {
-  // Excel-like sorting function
-  const excelLikeSortingFn = (rowA, rowB, columnId) => {
-    const a = rowA.getValue(columnId);
-    const b = rowB.getValue(columnId);
-    
-    if (a == null && b == null) return 0;
-    if (a == null) return 1;
-    if (b == null) return -1;
-    
-    return a < b ? -1 : a > b ? 1 : 0;
-  };
+  // Normalize null to undefined and use sortUndefined: 'last' per column.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In docs/guide/excel-like-sorting.md around lines 95 to 107, the inline
excelLikeSortingFn forces nulls to behave incorrectly (they remain "always last"
on descending sorts); remove this per-example sortingFn entirely and rely on the
existing value normalization plus the table/sort utility's sortUndefined
handling instead—delete the custom comparator and update the example to
demonstrate sorting using normalized values and sortUndefined configuration so
null/undefined are handled correctly by the shared sort logic.

Comment on lines +115 to +135
id: 'price',
accessorFn: row => row.price ?? null,
header: 'Price',
cell: ({ getValue }) => {
const value = getValue();
return value == null ? '-' : `$${value}`;
},
sortingFn: excelLikeSortingFn,
sortUndefined: 'last'
},
{
id: 'stock',
accessorFn: row => row.stock ?? null,
header: 'Stock',
cell: ({ getValue }) => {
const value = getValue();
return value == null ? 'N/A' : value;
},
sortingFn: excelLikeSortingFn,
sortUndefined: 'last'
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix column definitions to achieve “always last”

Normalize null→undefined and remove sortingFn from both columns. Keep sortUndefined: 'last'.

       {
         id: 'price',
-        accessorFn: row => row.price ?? null,
+        accessorFn: row => (row.price == null ? undefined : row.price),
         header: 'Price',
         cell: ({ getValue }) => {
           const value = getValue();
           return value == null ? '-' : `$${value}`;
         },
-        sortingFn: excelLikeSortingFn,
         sortUndefined: 'last'
       },
       {
         id: 'stock',
-        accessorFn: row => row.stock ?? null,
+        accessorFn: row => (row.stock == null ? undefined : row.stock),
         header: 'Stock',
         cell: ({ getValue }) => {
           const value = getValue();
           return value == null ? 'N/A' : value;
         },
-        sortingFn: excelLikeSortingFn,
         sortUndefined: 'last'
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
id: 'price',
accessorFn: row => row.price ?? null,
header: 'Price',
cell: ({ getValue }) => {
const value = getValue();
return value == null ? '-' : `$${value}`;
},
sortingFn: excelLikeSortingFn,
sortUndefined: 'last'
},
{
id: 'stock',
accessorFn: row => row.stock ?? null,
header: 'Stock',
cell: ({ getValue }) => {
const value = getValue();
return value == null ? 'N/A' : value;
},
sortingFn: excelLikeSortingFn,
sortUndefined: 'last'
}
{
id: 'price',
accessorFn: row => (row.price == null ? undefined : row.price),
header: 'Price',
cell: ({ getValue }) => {
const value = getValue();
return value == null ? '-' : `$${value}`;
},
sortUndefined: 'last'
},
{
id: 'stock',
accessorFn: row => (row.stock == null ? undefined : row.stock),
header: 'Stock',
cell: ({ getValue }) => {
const value = getValue();
return value == null ? 'N/A' : value;
},
sortUndefined: 'last'
}
🤖 Prompt for AI Agents
In docs/guide/excel-like-sorting.md around lines 115 to 135, the price and stock
column defs currently normalize missing values to null and explicitly set
sortingFn; update both columns to normalize missing values to undefined (e.g.
change accessorFn from row => row.price ?? null to row => row.price ?? undefined
and similarly for stock) and remove the sortingFn property from both column
objects while keeping sortUndefined: 'last'.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant