Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
09bf97c
feat(issues): New stack trace component
scttcper Feb 26, 2026
9515c5d
fix(stacktrace): Improve frame a11y semantics and context rendering
scttcper Feb 26, 2026
ed31c70
ref(stacktrace): split frame UI and add lazy coverage queries
scttcper Feb 26, 2026
9cd3efe
fix(stacktrace): stabilize core stacktrace tests and exports
scttcper Feb 26, 2026
8e1a522
Merge branch 'master' into scttcper/new-stack-trace
scttcper Feb 27, 2026
70ed472
feat(stacktrace): Add shared toolbar for ChainedStackTrace
scttcper Feb 27, 2026
0bc895b
fix(stacktrace): Fix coverage colors on active exception line
scttcper Feb 27, 2026
37a6938
Merge branch 'master' into scttcper/new-stack-trace
scttcper Mar 2, 2026
6b05ad7
Merge branch 'master' into scttcper/new-stack-trace
scttcper Mar 4, 2026
1bc50c8
Merge branch 'master' into scttcper/new-stack-trace
scttcper Mar 4, 2026
6b78bc0
ref(stackTrace): Extract composable frame actions and toolbar components
scttcper Mar 4, 2026
e212bd3
ref(stackTrace): Add IssueStackTrace and remove standalone ChainedSta…
scttcper Mar 5, 2026
ca8e909
ref(stackTrace): Replace ANR context props with frameBadge render prop
scttcper Mar 5, 2026
00d2855
ref(stackTrace): Add prop docs, rename Unsymbolicated to Minified, fi…
scttcper Mar 5, 2026
26a04ab
ref(stackTrace): Simplify context state and remove fallback chains
scttcper Mar 5, 2026
c065f70
perf(stackTrace): Reduce frame rerenders and row lookup overhead
scttcper Mar 5, 2026
90fc2ec
fix(stackTrace): Restore Frame API and clean stacktrace props
scttcper Mar 5, 2026
39b0448
add anr story
scttcper Mar 5, 2026
76ac08e
ref(stackTrace): Extract frame modules and refine header text layout
scttcper Mar 5, 2026
f28b35b
ref(stackTrace): Normalize frame header metadata typography
scttcper Mar 5, 2026
5df4ba6
ref(stackTrace): Make chevron visual-only with aligned slot
scttcper Mar 5, 2026
5dd5769
mostly move files
scttcper Mar 5, 2026
4916de3
Merge branch 'master' into scttcper/new-stack-trace
scttcper Mar 9, 2026
ecff622
split out frame context
scttcper Mar 9, 2026
b317d8d
ref(stacktrace): Compose issue frames on shared StackTraceFrames
scttcper Mar 10, 2026
714a58d
add story with long line numbers
scttcper Mar 10, 2026
aae2a9a
ref(stacktrace): Refine new stack trace frame headers
scttcper Mar 10, 2026
e0e4a1e
less line wrapping
scttcper Mar 10, 2026
8c84813
ref(stacktrace): Improve frame header layout and exception display
scttcper Mar 10, 2026
702db36
remove weird prop fallbacks, remove wrapping, better number grid, sou…
scttcper Mar 10, 2026
d681e09
ref(stacktrace): Polish new stack trace component and integrate sourc…
scttcper Mar 11, 2026
dcbf66d
Merge branch 'master' into scttcper/new-stack-trace
scttcper Mar 11, 2026
81a101e
add more support for debugging source maps
scttcper Mar 11, 2026
75495c8
start cleaning up lint issues
scttcper Mar 11, 2026
199dc10
feat(stacktrace): Add exception group support to new stack trace
scttcper Mar 11, 2026
c4f57a5
feat(stacktrace): Support raw/minified view and improve story actions
scttcper Mar 11, 2026
de2d598
feat(stacktrace): Support annotated text for PII-scrubbed exception v…
scttcper Mar 11, 2026
9303595
feat(stacktrace): Add suspect commits to new stack trace component
scttcper Mar 11, 2026
2b16930
feat(stacktrace): Improve frame variables grid alignment, sorting, an…
scttcper Mar 12, 2026
be967f6
feat(stacktrace): Style frame variables with background column and fi…
scttcper Mar 12, 2026
faef2bc
revert frame header colors
scttcper Mar 12, 2026
29c6e90
feat(stacktrace): Render URLs in exception values as clickable links
scttcper Mar 12, 2026
f45d6ff
fix(stacktrace): Allow null exception value in ExceptionValue type
scttcper Mar 12, 2026
97b3556
Merge branch 'master' into scttcper/new-stack-trace
scttcper Mar 12, 2026
3ba4402
clean up knip and lint issues
scttcper Mar 12, 2026
959f711
ref(stacktrace): Add standalone stacktrace support to IssueStackTrace
scttcper Mar 12, 2026
ffb1d25
Merge branch 'master' into scttcper/new-stack-trace
scttcper Mar 12, 2026
db28830
imports
scttcper Mar 12, 2026
189ce70
ref(stacktrace): Move sentry app components out of StackTraceContext
scttcper Mar 12, 2026
a28fdad
ref(stacktrace): Add Activity support for lazy frame rendering
scttcper Mar 13, 2026
4ec8ef1
fix story jank
scttcper Mar 13, 2026
cdbae46
fix(stacktrace): Copy respects unsymbolicated toggle for chained exce…
scttcper Mar 13, 2026
f7351c8
ref(stacktrace): Deduplicate section chrome in IssueStackTrace
scttcper Mar 13, 2026
1cefd09
change frame header color, remove variable background
scttcper Mar 16, 2026
a272312
Merge branch 'master' into scttcper/new-stack-trace
scttcper Mar 16, 2026
4b0e4cd
ref(stacktrace): Clean up code reuse, quality, and efficiency issues
scttcper Mar 16, 2026
830f5e3
fix(stacktrace): Show StacktraceBanners on first visible exception
scttcper Mar 16, 2026
f17fe35
ref(stacktrace): Add borderless prop to StackTraceFrames
scttcper Mar 17, 2026
869163d
fix double border issue
scttcper Mar 17, 2026
892dc88
feat(stacktrace): Show URL link in frame location tooltip
scttcper Mar 17, 2026
29fcd78
fix(stacktrace): Prioritize filename over function name in frame header
scttcper Mar 18, 2026
28cadaa
Merge branch 'master' into scttcper/new-stack-trace
scttcper Mar 18, 2026
c8bee28
Merge branch 'master' into scttcper/new-stack-trace
scttcper Mar 19, 2026
1d89fd3
fix imports
scttcper Mar 19, 2026
250a5b7
Merge branch 'master' into scttcper/new-stack-trace
scttcper Mar 20, 2026
cbabcc0
add overrides comment
scttcper Mar 20, 2026
9865eb6
VALID_SOURCE_MAP_DEBUGGER_FILE_EXTENSIONS
scttcper Mar 20, 2026
45fd1da
remove barrel files from stackTrace frame/actions and toolbar
scttcper Mar 20, 2026
4eeb57b
unused prop, styles
scttcper Mar 20, 2026
ff69720
Merge branch 'master' into scttcper/new-stack-trace
scttcper Mar 23, 2026
9151234
add codeowner
scttcper Mar 23, 2026
067c3bb
Merge branch 'master' into scttcper/new-stack-trace
scttcper Mar 24, 2026
5b09795
ref(stacktrace): Fix type safety in IssueStackTrace props and entry map
scttcper Mar 24, 2026
79c6791
drop type safety test
scttcper Mar 24, 2026
b6439f7
Merge branch 'master' into scttcper/new-stack-trace
scttcper Mar 24, 2026
3fe1e07
await in test, import
scttcper Mar 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,7 @@ tests/sentry/api/endpoints/test_organization_attribute_mappings.py @get
/static/app/components/events/eventTags/ @getsentry/issue-workflow
/static/app/components/events/highlights/ @getsentry/issue-workflow
/static/app/components/issues/ @getsentry/issue-workflow
/static/app/components/stackTrace/ @getsentry/issue-workflow
/static/app/views/issueList/ @getsentry/issue-workflow
/static/app/views/issueList/pages/supergroups.tsx @getsentry/issue-detection-frontend
/static/app/views/issueList/supergroups/ @getsentry/issue-detection-frontend
Expand Down
121 changes: 121 additions & 0 deletions static/app/components/stackTrace/displayOptions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import {CompactSelect} from '@sentry/scraps/compactSelect';
import {OverlayTrigger} from '@sentry/scraps/overlayTrigger';

import {useStackTraceViewState} from 'sentry/components/stackTrace/stackTraceContext';
import {IconSettings} from 'sentry/icons';
import {t} from 'sentry/locale';

const VIEW_OPTION_VALUES = [
'most-relevant',
'full-stack-trace',
'raw-stack-trace',
] as const;
const SORT_OPTION_VALUES = ['newest', 'oldest'] as const;

/**
* A single dropdown that consolidates view, sort, and display toggles.
*/
export function DisplayOptions() {
const {
view,
setView,
hasMinifiedStacktrace,
isMinified,
setIsMinified,
isNewestFirst,
setIsNewestFirst,
platform,
} = useStackTraceViewState();

const isJavaScriptPlatform =
platform?.startsWith('javascript') || platform?.startsWith('node');
const minifiedLabel = isJavaScriptPlatform ? t('Minified') : t('Unsymbolicated');
const minifiedUnavailableTooltip = isJavaScriptPlatform
? t('Minified version not available')
: t('Unsymbolicated version not available');

const currentViewVal =
view === 'raw'
? 'raw-stack-trace'
: view === 'full'
? 'full-stack-trace'
: 'most-relevant';
const currentSortVal = isNewestFirst ? 'newest' : 'oldest';

const value = [currentViewVal, currentSortVal, ...(isMinified ? ['minified'] : [])];

function handleChange(opts: Array<{value: string}>) {
const vals = opts.map(o => o.value);

// Mutually exclusive view selection: pick the newly added view option
const newViewVals = vals.filter(v =>
VIEW_OPTION_VALUES.includes(v as (typeof VIEW_OPTION_VALUES)[number])
);
const newViewVal =
newViewVals.find(v => v !== currentViewVal) ?? newViewVals[0] ?? currentViewVal;
if (newViewVal === 'raw-stack-trace') {
setView('raw');
} else if (newViewVal === 'full-stack-trace') {
setView('full');
} else {
setView('app');
}

// Mutually exclusive sort selection: pick the newly added sort option
const newSortVals = vals.filter(v =>
SORT_OPTION_VALUES.includes(v as (typeof SORT_OPTION_VALUES)[number])
);
const newSortVal =
newSortVals.find(v => v !== currentSortVal) ?? newSortVals[0] ?? currentSortVal;
setIsNewestFirst(newSortVal === 'newest');

setIsMinified(vals.includes('minified'));
}

return (
<CompactSelect
trigger={triggerProps => (
<OverlayTrigger.Button
{...triggerProps}
size="xs"
icon={<IconSettings />}
aria-label={t('Display options')}
>
{t('Display')}
</OverlayTrigger.Button>
)}
multiple
position="bottom-end"
value={value}
onChange={handleChange}
options={[
{
label: t('View'),
options: [
{label: t('Most Relevant'), value: 'most-relevant'},
{label: t('Full Stack Trace'), value: 'full-stack-trace'},
{label: t('Raw Stack Trace'), value: 'raw-stack-trace'},
],
},
{
label: t('Sort'),
options: [
{label: t('Newest'), value: 'newest'},
{label: t('Oldest'), value: 'oldest'},
],
},
{
label: t('Display'),
options: [
{
label: minifiedLabel,
value: 'minified',
disabled: !hasMinifiedStacktrace,
tooltip: hasMinifiedStacktrace ? undefined : minifiedUnavailableTooltip,
},
],
},
]}
/>
);
}
140 changes: 140 additions & 0 deletions static/app/components/stackTrace/exceptionGroup.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';

import type {ExceptionValue} from 'sentry/types/event';

import {
RelatedExceptionsTree,
ToggleRelatedExceptionsButton,
useHiddenExceptions,
} from './exceptionGroup';

/**
* Tree structure:
* ExceptionGroup (id=0, root)
* ├── ValueError (id=1)
* └── ExceptionGroup (id=2, nested)
* ├── TypeError (id=3)
* └── KeyError (id=4)
*/
function makeValues(): ExceptionValue[] {
const stub = {
stacktrace: null,
module: null,
threadId: null,
rawStacktrace: null,
};
return [
{
...stub,
type: 'ExceptionGroup',
value: 'root',
mechanism: {
handled: true,
type: '',
exception_id: 0,
is_exception_group: true,
},
},
{
...stub,
type: 'ValueError',
value: 'bad value',
mechanism: {handled: true, type: '', exception_id: 1, parent_id: 0},
},
{
...stub,
type: 'ExceptionGroup',
value: 'nested',
mechanism: {
handled: true,
type: '',
exception_id: 2,
parent_id: 0,
is_exception_group: true,
},
},
{
...stub,
type: 'TypeError',
value: 'type err',
mechanism: {handled: true, type: '', exception_id: 3, parent_id: 2},
},
{
...stub,
type: 'KeyError',
value: 'key err',
mechanism: {handled: true, type: '', exception_id: 4, parent_id: 2},
},
];
}

function TestHarness({values}: {values: ExceptionValue[]}) {
const {hiddenExceptions, toggleRelatedExceptions, expandException} =
useHiddenExceptions(values);

return (
<div>
{values.map(exc => {
const id = exc.mechanism?.exception_id;
const parentId = exc.mechanism?.parent_id;

if (parentId !== undefined && hiddenExceptions[parentId]) {
return null;
}

return (
<div key={id} data-test-id={`exc-${id}`}>
<span>{exc.type}</span>
<ToggleRelatedExceptionsButton
exception={exc}
hiddenExceptions={hiddenExceptions}
toggleRelatedExceptions={toggleRelatedExceptions}
values={values}
/>
<RelatedExceptionsTree
exception={exc}
allExceptions={values}
newestFirst={false}
onExceptionClick={expandException}
/>
</div>
);
})}
</div>
);
}

describe('exceptionGroup', () => {
it('hides nested group children by default, reveals on toggle, and expands via tree link', async () => {
render(<TestHarness values={makeValues()} />);

// Root group and its direct children are visible
expect(screen.getByTestId('exc-0')).toBeInTheDocument();
expect(screen.getByTestId('exc-1')).toBeInTheDocument();
expect(screen.getByTestId('exc-2')).toBeInTheDocument();

// Nested group's children are hidden
expect(screen.queryByTestId('exc-3')).not.toBeInTheDocument();
expect(screen.queryByTestId('exc-4')).not.toBeInTheDocument();

// Toggle reveals nested group's children
await userEvent.click(
screen.getByRole('button', {name: 'Show 2 related exceptions'})
);
expect(screen.getByTestId('exc-3')).toBeInTheDocument();
expect(screen.getByTestId('exc-4')).toBeInTheDocument();

// Toggle hides them again
await userEvent.click(
screen.getByRole('button', {name: 'Hide 2 related exceptions'})
);
expect(screen.queryByTestId('exc-3')).not.toBeInTheDocument();
expect(screen.queryByTestId('exc-4')).not.toBeInTheDocument();

// Clicking a child link in the nested group's tree calls expandException,
// which un-hides the parent group's children
await userEvent.click(screen.getByRole('button', {name: 'TypeError: type err'}));
expect(screen.getByTestId('exc-3')).toBeInTheDocument();
expect(screen.getByTestId('exc-4')).toBeInTheDocument();
});
});
Loading
Loading