Skip to content

feat(dashboards): sorting for table widget visualization #94570

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

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

lzhao-sentry
Copy link
Contributor

@lzhao-sentry lzhao-sentry commented Jun 27, 2025

Changes

Add sorting to table widget visualization. Sorting should now work for widgets on the dashboard and widget preview when editing.

Before

Sorting was not implemented on dashboard widgets before this

After

SORTING.mov

@github-actions github-actions bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Jun 27, 2025
Copy link

codecov bot commented Jul 2, 2025

❌ 10 Tests Failed:

Tests completed Failed Passed Skipped
10589 10 10579 9
View the top 3 failed test(s) by shortest run time
Testing new onboarding ui when the first trace is processed, display an enabled button "Take me to my trace"
Stack Traces | 1.01s run time
Error: Unable to find role="button" and name "Take me to my trace"

Ignored nodes: comments, script, style
...
    at waitForWrapper (.../sentry/node_modules/.pnpm/@testing-library+dom@10.4.0/node_modules/@.../dom/dist/wait-for.js:163:27)
    at .../sentry/node_modules/.pnpm/@testing-library+dom@10.4.0/node_modules/@.../dom/dist/query-helpers.js:86:33
    at Object.<anonymous> (.../views/performance/onboarding.spec.tsx:212:46)
    at Promise.then.completed (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/utils.js:298:28)
    at new Promise (<anonymous>)
    at callAsyncCircusFn (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/utils.js:231:10)
    at _callCircusTest (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/run.js:316:40)
    at _runTest (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/run.js:252:3)
    at _runTestsForDescribeBlock (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/run.js:126:9)
    at _runTestsForDescribeBlock (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/run.js:121:9)
    at run (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/run.js:71:3)
    at runAndTransformResultsToJestFormat (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
    at jestAdapter (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
    at runTestInternal (.../sentry/node_modules/.pnpm/jest-runner@29.7..../jest-runner/build/runTest.js:367:16)
    at runTest (.../sentry/node_modules/.pnpm/jest-runner@29.7..../jest-runner/build/runTest.js:444:34)
    at Object.worker (.../sentry/node_modules/.pnpm/jest-runner@29.7..../jest-runner/build/testWorker.js:106:12)
Testing new onboarding ui Renders updated ui
Stack Traces | 1.02s run time
Error: Unable to find an element with the text: Query for Traces, Get Answers. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

Ignored nodes: comments, script, style
...
    at waitForWrapper (.../sentry/node_modules/.pnpm/@testing-library+dom@10.4.0/node_modules/@.../dom/dist/wait-for.js:163:27)
    at .../sentry/node_modules/.pnpm/@testing-library+dom@10.4.0/node_modules/@.../dom/dist/query-helpers.js:86:33
    at Object.<anonymous> (.../views/performance/onboarding.spec.tsx:70:46)
    at Promise.then.completed (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/utils.js:298:28)
    at new Promise (<anonymous>)
    at callAsyncCircusFn (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/utils.js:231:10)
    at _callCircusTest (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/run.js:316:40)
    at processTicksAndRejections (node:internal/process/task_queues:105:5)
    at _runTest (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/run.js:252:3)
    at _runTestsForDescribeBlock (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/run.js:126:9)
    at _runTestsForDescribeBlock (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/run.js:121:9)
    at run (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/run.js:71:3)
    at runAndTransformResultsToJestFormat (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
    at jestAdapter (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
    at runTestInternal (.../sentry/node_modules/.pnpm/jest-runner@29.7..../jest-runner/build/runTest.js:367:16)
    at runTest (.../sentry/node_modules/.pnpm/jest-runner@29.7..../jest-runner/build/runTest.js:444:34)
    at Object.worker (.../sentry/node_modules/.pnpm/jest-runner@29.7..../jest-runner/build/testWorker.js:106:12)
Testing new onboarding ui when the first trace is received, display a busy button "Take me to my trace"
Stack Traces | 1.02s run time
Error: Unable to find role="button" and name "Take me to my trace"

Ignored nodes: comments, script, style
...
    at waitForWrapper (.../sentry/node_modules/.pnpm/@testing-library+dom@10.4.0/node_modules/@.../dom/dist/wait-for.js:163:27)
    at .../sentry/node_modules/.pnpm/@testing-library+dom@10.4.0/node_modules/@.../dom/dist/query-helpers.js:86:33
    at Object.<anonymous> (.../views/performance/onboarding.spec.tsx:141:46)
    at Promise.then.completed (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/utils.js:298:28)
    at new Promise (<anonymous>)
    at callAsyncCircusFn (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/utils.js:231:10)
    at _callCircusTest (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/run.js:316:40)
    at _runTest (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/run.js:252:3)
    at _runTestsForDescribeBlock (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/run.js:126:9)
    at _runTestsForDescribeBlock (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/run.js:121:9)
    at run (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../jest-circus/build/run.js:71:3)
    at runAndTransformResultsToJestFormat (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
    at jestAdapter (.../sentry/node_modules/.pnpm/jest-circus@29.7.0_babel-plugin-macros@3.1..../build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
    at runTestInternal (.../sentry/node_modules/.pnpm/jest-runner@29.7..../jest-runner/build/runTest.js:367:16)
    at runTest (.../sentry/node_modules/.pnpm/jest-runner@29.7..../jest-runner/build/runTest.js:444:34)
    at Object.worker (.../sentry/node_modules/.pnpm/jest-runner@29.7..../jest-runner/build/testWorker.js:106:12)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@lzhao-sentry lzhao-sentry marked this pull request as ready for review July 2, 2025 14:49
@lzhao-sentry lzhao-sentry requested a review from a team as a code owner July 2, 2025 14:49
cursor[bot]

This comment was marked as outdated.

@lzhao-sentry
Copy link
Contributor Author

I agree with Cursor's first comment, but I think this is something that can be addressed in a separate PR since it also affects the viewer modal.

cursor[bot]

This comment was marked as outdated.

Copy link
Member

@nikkikapadia nikkikapadia left a comment

Choose a reason for hiding this comment

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

overall it's working and the code is almost there. Just a few questions and comments before I approve

} else if (locationSort?.field === sortColumn) {
direction = locationSort.kind;
}
const nextDirection = direction === 'desc' ? 'asc' : 'desc';
Copy link
Member

Choose a reason for hiding this comment

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

i don't think this is necessary to put here, this can probably be moved to inside the onClick function. This will keep calculating on every render

Comment on lines 359 to 361
const widgetCopy = cloneDeep({
...widget,
});
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const widgetCopy = cloneDeep({
...widget,
});
const widgetCopy = cloneDeep(widget);

});
if (widgetCopy.queries[0]) {
const direction =
sort.kind === 'desc' && widget.widgetType !== WidgetType.ISSUE ? '-' : '';
Copy link
Member

Choose a reason for hiding this comment

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

is this gonna be an issue with some of the issue widget fields? (referencing one of your previous prs) Just curious, not sure if that needs to be taken into account here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should be fine, issues fields omit the '-' regardless of order that it is sorted by. So when updating the widget query, we want to make sure it's never included

// This is to match the widget viewer modal, which always displays the arrow pointing down
const sort: Sort = {
field: decodeSorts(widget.queries[0]?.orderby)[0]?.field || '',
kind: 'desc',
Copy link
Member

Choose a reason for hiding this comment

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

same comment as above about the issues widget default sort stuff.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This would be purely visual since that widget.widgetType !== WidgetType.ISSUE always removes the '-'.

I'm honestly thinking it might be better to omit sorting for issue widgets for now/make a separate PR to address it. Might want to get design's opinion as well since the arrow direction lowkey makes no sense

Copy link
Member

@gggritso gggritso left a comment

Choose a reason for hiding this comment

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

I know I keep hammering on you about splitting PRs but this PR would be even better if it was two PRs! One to introduce the new functionality, and a separate one to integrate into Dashboards, once the functionality is all approved and merged.

Otherwise, I might forget to ask that the new sorting functionality has specs, for example, and/or you'll need to make more changes if I ask for a prop to be renamed, or whatever!

In any case, left a few comments about how it works, even though I think overall it LGTM 👍🏻

* A callback function that is invoked after a user clicks a sortable column header and overrides default behaviour of navigating
* @param sort `Sort` object contain the `field` and `kind` ('asc' or 'desc')
*/
onColumnSortChange?: (sort: Sort) => void;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
onColumnSortChange?: (sort: Sort) => void;
onSortChange?: (sort: Sort) => void;

onSortChange is nicer here, it's shorter and matches sort. Only columns can be sorted, right?

@@ -73,10 +75,19 @@ interface TableWidgetVisualizationProps {
* @param meta The full table metadata
*/
makeBaggage?: BaggageMaker;
/**
* A callback function that is invoked after a user clicks a sortable column header and overrides default behaviour of navigating
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* A callback function that is invoked after a user clicks a sortable column header and overrides default behaviour of navigating
* A callback function that is invoked after a user clicks a sortable column header. If omitted, clicking a column header updates the sort in the URL

@@ -136,6 +149,7 @@ export function TableWidgetVisualization(props: TableWidgetVisualizationProps) {
}));

const {data, meta} = tableData;
const locationSort = decodeSorts(location?.query?.sort)[0];
Copy link
Member

Choose a reason for hiding this comment

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

👀 I don't mind that it only supports a single sort for now, but it'd be good to document this limitation in the stories!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh interesting, is there any use case for multiple sorts right now? But will update the stories.

Copy link
Member

Choose a reason for hiding this comment

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

I don't know offhand if any of our UIs support it! I don't even know how you'd specific which sort is first and which one is second.

<p>
By default, column fields are assumed to be not sortable. To enable sorting,
pass the
<code>columns</code> prop with the field <code>sortable</code> set to true. Ex.
Copy link
Member

Choose a reason for hiding this comment

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

This is the nittest of nits but most of the documentation uses Chicago-style "e.g.,"instead of"Ex."

Comment on lines 152 to 161
// The Sort type
export type Sort = {
field: string;
kind: 'asc' | 'desc';
};

// Basic Example
function onColumnSortChange(sort: Sort) {
setSort(sort)
}
Copy link
Member

Choose a reason for hiding this comment

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

No need to explain the Sort type here! IMO would be better to give a more illustrative example. e.g.,

Suggested change
// The Sort type
export type Sort = {
field: string;
kind: 'asc' | 'desc';
};
// Basic Example
function onColumnSortChange(sort: Sort) {
setSort(sort)
}
<TableWidgetVisualization
columns={[{field: 'count(
sort={{field: 'count(
tableData=...

As in, have the sample be a whole snippet that explains how the data, the columns, and the sort are specified together

Comment on lines 164 to 172
<p>
The table will try to automatically parse out the direction from the location
query parameter and apply the sort direction arrow to the sorted column.
However, if sorting does not rely on this, or custom sort needs to be used, then
pass the <code>sort</code> prop to correcly display the sort arrow direction:
</p>
<CodeSnippet>
{`sort={{field: 'count(span.duration)', kind: 'desc'}}`}
</CodeSnippet>
Copy link
Member

Choose a reason for hiding this comment

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

This is a cool default, but would be good to also explain with a fuller example, IMO!

cursor[bot]

This comment was marked as outdated.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Bug: Visual Sort Indicator Mismatch

The sort.kind property is hardcoded to 'desc' in IssueWidgetCard, even though the sort field is correctly derived from the widget's orderby query. This causes the visual sort indicator in the UI to always show a descending arrow, regardless of the actual sort direction of the data, misleading users about the current sort state.

static/app/views/dashboards/widgetCard/issueWidgetCard.tsx#L86-L91

};
// This is to match the widget viewer modal, which always displays the arrow pointing down
const sort: Sort = {
field: decodeSorts(widget.queries[0]?.orderby)[0]?.field || '',
kind: 'desc',
};

Fix in Cursor


Was this report helpful? Give feedback by reacting with 👍 or 👎

@lzhao-sentry lzhao-sentry marked this pull request as draft July 3, 2025 13:56
lzhao-sentry added a commit that referenced this pull request Jul 3, 2025
### Changes

Added sorting functionality to the table widget visualization component.
Part one of this splitting this PR:
#94570

Will open another PR to integrate it into dashboards.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Scope: Frontend Automatically applied to PRs that change frontend components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants