feat(#3344): add multi-column sorting to Table and TableSortHeader#3409
feat(#3344): add multi-column sorting to Table and TableSortHeader#3409chrisolsen merged 2 commits intodevfrom
Conversation
c1514d7 to
7097784
Compare
bdfranck
left a comment
There was a problem hiding this comment.
The sorting interactions are looking good! I've left some comments that can be grouped into three categories:
- Try to re-use and expand on existing variables and functions.
- Avoid using JSON because it can lead to increased errors. Use Types instead.
- Make sure that you're giving devs the data they need for their sort functions.
apps/prs/angular/src/routes/features/feat3344/feat3344.component.ts
Outdated
Show resolved
Hide resolved
libs/web-components/src/components/table/TableSortHeader.svelte
Outdated
Show resolved
Hide resolved
libs/web-components/src/components/table/TableSortHeader.svelte
Outdated
Show resolved
Hide resolved
libs/web-components/src/components/table/TableSortHeader.svelte
Outdated
Show resolved
Hide resolved
7097784 to
48f879a
Compare
✅ Deploy Preview for goa-design-2 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
91d0a01 to
fa361f7
Compare
willcodeforcoffee
left a comment
There was a problem hiding this comment.
👍 Looks good!
bdfranck
left a comment
There was a problem hiding this comment.
I looked at the latest changes...
- ✅ I see the JSON was removed
- ✅ The PR playground examples work as expected in Angular and React
- ✅ All tests pass
Looks good to me! 👍
| /** @internal Design system version for styling. */ | ||
| export let version: "1" | "2" = "1"; | ||
| /** Sort order number for multi-column sort display ("1", "2", etc). */ | ||
| export let sortOrder: number = 0; |
There was a problem hiding this comment.
Can this be any number? Or just 0 | 1 | 2?
There was a problem hiding this comment.
In practice just 0, 1, or 2. _maxSorts is 2, and the Table sets this directly on headers. 0 means not actively sorted (no sort showing). It's using a number since it's internally managed, not something a dev sets by hand.
There was a problem hiding this comment.
If if it can only be a 0, 1 or 2, then the type should reflect that.
There was a problem hiding this comment.
@chrisolsen I updated sortOrder type to 0 | 1 | 2 in the Svelte component (internal). Angular and React wrappers still need to pass 1|2.
|
|
||
| if (sortBy && sortDir !== 0) { | ||
| dispatch(heading, { sortBy, sortDir }); | ||
| function initializeSorts() { |
There was a problem hiding this comment.
Is there a reason this function name was changed? This new name is rather vague to what it does.
There was a problem hiding this comment.
Looking at what initializeSorts was actually doing:
- Reading initial direction/sortOrder from registered headers
- Attaching click handlers to headers
- Updating header attributes
- Dispatching the initial sort event
That seems like a lot for one function, and what I understand is that mixing handler attachment with state building meant listeners could stack if headers registered at different times.
It's now split into: attachClickHandlers, buildInitialSortState, with a bindAndInitialize orchestrator. Listener attachment is guarded so it only runs once even if headers register at different times. Also split handleSortClick into handleSingleSort/handleMultiSort per your earlier comment.
|
|
||
| dispatch<GoATableSortHeaderProps>( | ||
| _rootEl, | ||
| "table-sort-header:mounted", | ||
| { el: host, name, direction, sortOrder }, | ||
| { bubbles: true, timeout: 10 }, | ||
| ); | ||
| } |
There was a problem hiding this comment.
What is the reason for adding the dispatch call? Since all the props are reflected all the values are accessible via the attributes.
There was a problem hiding this comment.
That was added in Vanessa's refactor. I think it's a registration pattern so the Table doesn't need to query the DOM for headers, but @vanessatran-ddi can probably speak to the reasoning better than I can.
There was a problem hiding this comment.
Let me remove it, at first I think query all attributes will not work, but I try local and it seems working well, I will update the code.
|
|
||
| // Warn when multi-mode headers have initial direction but missing sortOrder | ||
| if (sortMode === "multi" && entries.length > 1) { | ||
| const missing = entries.filter((e) => !e.order); |
There was a problem hiding this comment.
Couple things
- The var is called
missing, which implies that the order may be null, but it's defined as a number with a default value of0, so it's not missing, but rather just not set. (This is minor, but it is a little confusing) - I am confused why a having a header with a
sort-ordervalue of0is bad.
There was a problem hiding this comment.
Two changes here:
-
Renamed missing to withoutSortOrder and changed the check to e.order === 0 to be explicit.
-
sort-order="0" isn't bad on its own. The warning only fires when multiple headers have initial direction set in multi mode but no sort-order to define priority. Without it the table falls back to DOM order, which might not match what was intended. Reworded the warning to try to explain that better.
| { | ||
| sortBy: firstSort?.column ?? "", | ||
| sortDir: firstSort ? (firstSort.direction === "asc" ? 1 : -1) : 0, | ||
| sorts: _sorts, |
There was a problem hiding this comment.
I think it would be cleaner if there was different events for the different sort types.
There was a problem hiding this comment.
The _sort event already exists on dev with { sortBy, sortDir }. This extends it with an optional sorts array for multi-mode. Single-sort consumers get the same detail they always did.
Splitting into separate events would mean separate wrapper props (onSort + onMultiSort), and if a team moves from single to multi sort they'd need to swap out their event handler too. Keeping it as one event means existing implementations don't break.
There was a problem hiding this comment.
But in the way things are done now every event has some null values and some not. So it isn't clear to them, without reading the docs, what is what.
Keeping it as one event means existing implementations don't break.
That doesn't make sense. You would keep the initial event handler and add a new one, named appropriately.
There was a problem hiding this comment.
Hi @chrisolsen , the web component now dispatches _sort({sortBy, sortDir}) and _multisort({sort:[]}). Each event has a clean and dedicated detail type with no nullable fields. Both angular and react wrappers expose onSort like now and onMultiSort accordingly.
e56b58f to
fd2d258
Compare
df20e4f to
0c4d09e
Compare
chrisolsen
left a comment
There was a problem hiding this comment.
Just one more small thing, then I am good merging it. @bdfranck @willcodeforcoffee do you guys need to re-review?
| let _isTableRoot: boolean = false; | ||
| let _sorts: SortEntry[] = []; | ||
| let _headings: NodeListOf<Element> | undefined; | ||
| const _maxSorts = 2; |
There was a problem hiding this comment.
For clarity, since this is a const value this should be renamed to MAX_SORTS
There was a problem hiding this comment.
Yes I rename this.
d94f6fd to
4fb6b81
Compare
4fb6b81 to
289510b
Compare
willcodeforcoffee
left a comment
There was a problem hiding this comment.
Testing works, looks good!
Still! 😉 😆
|
🎉 This PR is included in version 1.41.0-dev.15 🎉 The release is available on: Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version 1.11.0-dev.4 🎉 The release is available on: Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version 6.11.0-dev.11 🎉 The release is available on: Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version 4.11.0-dev.11 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Summary
Adds built-in multi-column sorting to Table and TableSortHeader components.
"single"(default) or"multi"(up to 2 columns)chevron-expand(unsorted),arrow-up/arrow-down(sorted)Multi sort
Single sort
Test plan
/features/3344/features/3344and repeat testsCloses #3344