From 3d8d2a337d59e6f1e194318a49d4d8a6a71c258b Mon Sep 17 00:00:00 2001 From: Adrian Bonilla Date: Thu, 15 Oct 2026 18:12:05 -0700 Subject: [PATCH 1/5] feat(droppedData): Replace Learn more button with an ellipsis menu --- .../drawer/droppedDataOutcomeList.spec.tsx | 21 +++++++++---------- .../drawer/droppedDataOutcomeList.tsx | 14 ++++++------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/static/app/components/droppedData/drawer/droppedDataOutcomeList.spec.tsx b/static/app/components/droppedData/drawer/droppedDataOutcomeList.spec.tsx index bf7265f2ee60..a2966676795e 100644 --- a/static/app/components/droppedData/drawer/droppedDataOutcomeList.spec.tsx +++ b/static/app/components/droppedData/drawer/droppedDataOutcomeList.spec.tsx @@ -209,21 +209,20 @@ describe('DroppedDataOutcomeList', () => { /> ); - // The fix-this affordance is hidden until the reason chevron is clicked. - expect(screen.queryByRole('button', {name: 'Learn more'})).not.toBeInTheDocument(); + expect( + screen.queryByRole('button', {name: 'Reason actions'}) + ).not.toBeInTheDocument(); await userEvent.click(screen.getByRole('button', {name: 'Toggle fix options'})); - expect(screen.getByRole('button', {name: 'Learn more'})).toBeInTheDocument(); - - // Collapsing hides it again. + expect(screen.getByRole('button', {name: 'Reason actions'})).toBeInTheDocument(); await userEvent.click(screen.getByRole('button', {name: 'Toggle fix options'})); - expect(screen.queryByRole('button', {name: 'Learn more'})).not.toBeInTheDocument(); - - // The toggle is reachable from the keyboard. + expect( + screen.queryByRole('button', {name: 'Reason actions'}) + ).not.toBeInTheDocument(); act(() => screen.getByRole('button', {name: 'Toggle fix options'}).focus()); await userEvent.keyboard('{Enter}'); - expect(screen.getByRole('button', {name: 'Learn more'})).toBeInTheDocument(); + expect(screen.getByRole('button', {name: 'Reason actions'})).toBeInTheDocument(); }); it('shows the long description when a reason row is expanded', async () => { @@ -271,7 +270,7 @@ describe('DroppedDataOutcomeList', () => { ); await userEvent.click(screen.getByRole('button', {name: 'Toggle fix options'})); - await userEvent.click(screen.getByRole('button', {name: 'Learn more'})); + await userEvent.click(screen.getByRole('button', {name: 'Reason actions'})); // Project settings deep-links to the usage stats page scoped to the // reason's data category. @@ -309,7 +308,7 @@ describe('DroppedDataOutcomeList', () => { ); await userEvent.click(screen.getByRole('button', {name: 'Toggle fix options'})); - await userEvent.click(screen.getByRole('button', {name: 'Learn more'})); + await userEvent.click(screen.getByRole('button', {name: 'Reason actions'})); expect( screen.getByRole('menuitemradio', {name: 'Project Settings'}) diff --git a/static/app/components/droppedData/drawer/droppedDataOutcomeList.tsx b/static/app/components/droppedData/drawer/droppedDataOutcomeList.tsx index dac291468223..50afe4131b8b 100644 --- a/static/app/components/droppedData/drawer/droppedDataOutcomeList.tsx +++ b/static/app/components/droppedData/drawer/droppedDataOutcomeList.tsx @@ -1,5 +1,6 @@ import {Fragment, useId, useState} from 'react'; import {IconChevron} from '@sentry/icons/chevron'; +import {IconEllipsis} from '@sentry/icons/ellipsis'; import {Button} from '@sentry/scraps/button'; import {useDrawer, useDrawerContentContext} from '@sentry/scraps/drawer'; @@ -86,15 +87,14 @@ function FixThisMenu({ usePortal portalContainerRef={panelRef} trigger={(triggerProps, isOpen) => ( - } + aria-label={t('Reason actions')} aria-expanded={isOpen} - > - {t('Learn more')} - + /> )} /> ); From c299ca396a07dff1685a0d610755124609cc546b Mon Sep 17 00:00:00 2001 From: Adrian Bonilla Date: Thu, 15 Oct 2026 18:15:39 -0700 Subject: [PATCH 2/5] feat(droppedData): Remove expandable reason rows and long descriptions --- .../drawer/droppedDataOutcomeList.spec.tsx | 63 ------------- .../drawer/droppedDataOutcomeList.tsx | 87 +++++------------- .../components/droppedData/outcomes.spec.tsx | 25 +---- .../app/components/droppedData/outcomes.tsx | 92 ------------------- 4 files changed, 26 insertions(+), 241 deletions(-) diff --git a/static/app/components/droppedData/drawer/droppedDataOutcomeList.spec.tsx b/static/app/components/droppedData/drawer/droppedDataOutcomeList.spec.tsx index a2966676795e..5b077a9aa2fd 100644 --- a/static/app/components/droppedData/drawer/droppedDataOutcomeList.spec.tsx +++ b/static/app/components/droppedData/drawer/droppedDataOutcomeList.spec.tsx @@ -192,64 +192,6 @@ describe('DroppedDataOutcomeList', () => { expect(screen.queryByRole('table')).not.toBeInTheDocument(); }); - it('reveals the fix options under a reason row via its chevron', async () => { - render( - - ); - - expect( - screen.queryByRole('button', {name: 'Reason actions'}) - ).not.toBeInTheDocument(); - - await userEvent.click(screen.getByRole('button', {name: 'Toggle fix options'})); - - expect(screen.getByRole('button', {name: 'Reason actions'})).toBeInTheDocument(); - await userEvent.click(screen.getByRole('button', {name: 'Toggle fix options'})); - expect( - screen.queryByRole('button', {name: 'Reason actions'}) - ).not.toBeInTheDocument(); - act(() => screen.getByRole('button', {name: 'Toggle fix options'}).focus()); - await userEvent.keyboard('{Enter}'); - expect(screen.getByRole('button', {name: 'Reason actions'})).toBeInTheDocument(); - }); - - it('shows the long description when a reason row is expanded', async () => { - render( - - ); - - const longDescription = - 'The event reached Sentry but was rejected with an error code.'; - expect(screen.queryByText(longDescription)).not.toBeInTheDocument(); - - await userEvent.click(screen.getByRole('button', {name: 'Toggle fix options'})); - - expect(screen.getByText(longDescription)).toBeInTheDocument(); - }); - it('opens the fix-this menu with investigate, settings, and docs entries', async () => { const onInvestigate = jest.fn(); render( @@ -269,11 +211,7 @@ describe('DroppedDataOutcomeList', () => { /> ); - await userEvent.click(screen.getByRole('button', {name: 'Toggle fix options'})); await userEvent.click(screen.getByRole('button', {name: 'Reason actions'})); - - // Project settings deep-links to the usage stats page scoped to the - // reason's data category. expect(screen.getByRole('menuitemradio', {name: 'Project Settings'})).toHaveAttribute( 'href', expect.stringContaining('/stats/?dataCategory=spans') @@ -307,7 +245,6 @@ describe('DroppedDataOutcomeList', () => { /> ); - await userEvent.click(screen.getByRole('button', {name: 'Toggle fix options'})); await userEvent.click(screen.getByRole('button', {name: 'Reason actions'})); expect( diff --git a/static/app/components/droppedData/drawer/droppedDataOutcomeList.tsx b/static/app/components/droppedData/drawer/droppedDataOutcomeList.tsx index 50afe4131b8b..3cc1550121f9 100644 --- a/static/app/components/droppedData/drawer/droppedDataOutcomeList.tsx +++ b/static/app/components/droppedData/drawer/droppedDataOutcomeList.tsx @@ -1,4 +1,4 @@ -import {Fragment, useId, useState} from 'react'; +import {useId, useState} from 'react'; import {IconChevron} from '@sentry/icons/chevron'; import {IconEllipsis} from '@sentry/icons/ellipsis'; @@ -19,7 +19,6 @@ import { dataCategoryInfo, formatDroppedShare, reasonDescription, - reasonLongDescription, reasonTitle, type OutcomeColors, } from 'sentry/components/droppedData/outcomes'; @@ -28,7 +27,7 @@ import {t} from 'sentry/locale'; import {normalizeUrl} from 'sentry/utils/url/normalizeUrl'; import {useOrganization} from 'sentry/utils/useOrganization'; -const COLUMN_WIDTHS = {reason: '1fr', dropped: '160px', share: '96px', toggle: '48px'}; +const COLUMN_WIDTHS = {reason: '1fr', dropped: '160px', share: '96px', actions: '48px'}; const TABLE_COLUMNS: TableColumnConfig[] = Object.entries(COLUMN_WIDTHS).map( ([key, width]) => ({key, width}) ); @@ -100,31 +99,6 @@ function FixThisMenu({ ); } -function ReasonExpansion({ - row, - onInvestigate, -}: { - row: ReasonRow; - onInvestigate?: (row: ReasonRow) => void; -}) { - const longDescription = reasonLongDescription(row.reason); - - return ( - - - {longDescription && {longDescription}} - - - - ); -} - function ReasonTableRow({ row, onInvestigate, @@ -132,43 +106,32 @@ function ReasonTableRow({ row: ReasonRow; onInvestigate?: (row: ReasonRow) => void; }) { - const [expanded, setExpanded] = useState(false); const description = reasonDescription(row.reason, row.category); return ( - - - - {reasonTitle(row.reason)} - {description && ( - - {description} - - )} - - - - {row.events.toLocaleString()} - - - - - {formatDroppedShare(row.shareRatio)} + + + {reasonTitle(row.reason)} + {description && ( + + {description} - - -