-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
docs: add Excel-like sorting guide for null/undefined handling #6085
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
base: main
Are you sure you want to change the base?
docs: add Excel-like sorting guide for null/undefined handling #6085
Conversation
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
WalkthroughDocumentation 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
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
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related issues
Poem
✨ Finishing Touches🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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 sectionIf 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 spacingAdd 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 tipThe note is accurate and the link path looks correct. Consider adding a one-liner tip: “Normalize null→undefined in
accessorFn
and setsortUndefined: '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.
📒 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)
```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; | ||
}; | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
```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).
const columns = [ | ||
{ | ||
id: 'price', | ||
accessorFn: row => row.price ?? null, | ||
header: 'Price', | ||
cell: ({ getValue }) => { | ||
const value = getValue(); | ||
return value == null ? '-' : `$${value}`; | ||
}, | ||
sortingFn: excelLikeSortingFn, | ||
sortUndefined: 'last' | ||
} | ||
]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
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; | ||
}; | ||
|
There was a problem hiding this comment.
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.
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' | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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'.
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