From 838d6e340e2b21f41539a0d0de48346bf0b9cbef Mon Sep 17 00:00:00 2001 From: nikkikapadia Date: Thu, 2 Apr 2026 14:44:56 -0400 Subject: [PATCH 1/6] add body elements to errors page content --- static/app/views/explore/errors/body.tsx | 52 +++++++++++++++++++++ static/app/views/explore/errors/content.tsx | 44 ++++++++++++++++- static/app/views/explore/spans/spansTab.tsx | 2 +- 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 static/app/views/explore/errors/body.tsx diff --git a/static/app/views/explore/errors/body.tsx b/static/app/views/explore/errors/body.tsx new file mode 100644 index 00000000000000..23f7076862e9e5 --- /dev/null +++ b/static/app/views/explore/errors/body.tsx @@ -0,0 +1,52 @@ +import {IconChevron} from 'sentry/icons/iconChevron'; +import {t} from 'sentry/locale'; +import {OverChartButtonGroup} from 'sentry/views/explore/components/overChartButtonGroup'; +import { + ExploreContentSection, + ExploreControlSection, +} from 'sentry/views/explore/components/styles'; +import {ChevronButton} from 'sentry/views/explore/spans/spansTab'; + +interface ErrorsControlSectionProps { + controlSectionExpanded: boolean; +} + +export function ErrorsControlSection({ + controlSectionExpanded, +}: ErrorsControlSectionProps) { + return ; +} + +interface ErrorsContentSectionProps { + controlSectionExpanded: boolean; + setControlSectionExpanded: (expanded: boolean) => void; +} + +export function ErrorsContentSection({ + controlSectionExpanded, + setControlSectionExpanded, +}: ErrorsContentSectionProps) { + return ( + + + + } + onClick={() => setControlSectionExpanded(!controlSectionExpanded)} + > + {controlSectionExpanded ? null : t('Advanced')} + + + + ); +} diff --git a/static/app/views/explore/errors/content.tsx b/static/app/views/explore/errors/content.tsx index 2d3fb49ad0fb3a..7c0e3549593af2 100644 --- a/static/app/views/explore/errors/content.tsx +++ b/static/app/views/explore/errors/content.tsx @@ -1,3 +1,5 @@ +import {useCallback} from 'react'; + import {FeatureBadge} from '@sentry/scraps/badge'; import {FeedbackButton} from 'sentry/components/feedbackButton/feedbackButton'; @@ -5,8 +7,16 @@ import * as Layout from 'sentry/components/layouts/thirds'; import {PageFiltersContainer} from 'sentry/components/pageFilters/container'; import {SentryDocumentTitle} from 'sentry/components/sentryDocumentTitle'; import {t} from 'sentry/locale'; +import {useLocalStorageState} from 'sentry/utils/useLocalStorageState'; import {useOrganization} from 'sentry/utils/useOrganization'; -import {ExploreBodySearch} from 'sentry/views/explore/components/styles'; +import { + ExploreBodyContent, + ExploreBodySearch, +} from 'sentry/views/explore/components/styles'; +import { + ErrorsContentSection, + ErrorsControlSection, +} from 'sentry/views/explore/errors/body'; import {ErrorsFilterSection} from 'sentry/views/explore/errors/filterContent'; export default function ErrorsContent() { @@ -22,6 +32,7 @@ export default function ErrorsContent() { + ); @@ -41,3 +52,34 @@ function ErrorsHeader() { ); } + +export function ErrorsBody() { + const [controlSectionExpanded, setControlSectionExpanded] = + useErrorsControlSectionExpanded(); + + return ( + + + + + ); +} + +function useErrorsControlSectionExpanded() { + const [controlSectionExpanded, _setControlSectionExpanded] = useLocalStorageState( + 'explore-errors-toolbar', + 'expanded' + ); + + const setControlSectionExpanded = useCallback( + (expanded: boolean) => { + _setControlSectionExpanded(expanded ? 'expanded' : ''); + }, + [_setControlSectionExpanded] + ); + + return [controlSectionExpanded === 'expanded', setControlSectionExpanded] as const; +} diff --git a/static/app/views/explore/spans/spansTab.tsx b/static/app/views/explore/spans/spansTab.tsx index 61b7c5c78cfbbd..1aa501e0ccf00c 100644 --- a/static/app/views/explore/spans/spansTab.tsx +++ b/static/app/views/explore/spans/spansTab.tsx @@ -365,7 +365,7 @@ const OnboardingContentSection = styled('section')` grid-column: 1/3; `; -const ChevronButton = styled(Button)<{expanded: boolean}>` +export const ChevronButton = styled(Button)<{expanded: boolean}>` display: none; @media (min-width: ${p => p.theme.breakpoints.md}) { From 6dd207993b181393bf5747b8998c0375b4caf779 Mon Sep 17 00:00:00 2001 From: nikkikapadia Date: Thu, 2 Apr 2026 14:47:59 -0400 Subject: [PATCH 2/6] add schema hints section to the filter content (so i don't forget) + add todos --- .../components/schemaHints/schemaHintsList.tsx | 2 ++ .../components/schemaHints/schemaHintsUtils.tsx | 2 ++ static/app/views/explore/errors/filterContent.tsx | 14 ++++++++++++++ 3 files changed, 18 insertions(+) diff --git a/static/app/views/explore/components/schemaHints/schemaHintsList.tsx b/static/app/views/explore/components/schemaHints/schemaHintsList.tsx index dddab9649b3e83..bb80f0884f31c4 100644 --- a/static/app/views/explore/components/schemaHints/schemaHintsList.tsx +++ b/static/app/views/explore/components/schemaHints/schemaHintsList.tsx @@ -118,6 +118,8 @@ const FILTER_KEY_SECTIONS: Record = { [SchemaHintsSources.EXPLORE]: SPANS_FILTER_KEY_SECTIONS, [SchemaHintsSources.LOGS]: LOGS_FILTER_KEY_SECTIONS, [SchemaHintsSources.CONVERSATIONS]: SPANS_FILTER_KEY_SECTIONS, + // TODO: add error filter key sections when they are implemented + [SchemaHintsSources.ERRORS]: [], }; function getFilterKeySections(source: SchemaHintsSources) { diff --git a/static/app/views/explore/components/schemaHints/schemaHintsUtils.tsx b/static/app/views/explore/components/schemaHints/schemaHintsUtils.tsx index 74dbbe618a239a..c6ac1a7a7d2caf 100644 --- a/static/app/views/explore/components/schemaHints/schemaHintsUtils.tsx +++ b/static/app/views/explore/components/schemaHints/schemaHintsUtils.tsx @@ -63,9 +63,11 @@ const SCHEMA_HINTS_HIDDEN_KEYS: string[] = [ ]; export enum SchemaHintsSources { + // TODO: change Explore to Spans because Explore is too broad and confusing here EXPLORE = 'explore', LOGS = 'logs', CONVERSATIONS = 'conversations', + ERRORS = 'errors', } export const getSchemaHintsListOrder = (source: SchemaHintsSources) => { diff --git a/static/app/views/explore/errors/filterContent.tsx b/static/app/views/explore/errors/filterContent.tsx index 2e0240d87e6a6d..836a9dc37e3fcc 100644 --- a/static/app/views/explore/errors/filterContent.tsx +++ b/static/app/views/explore/errors/filterContent.tsx @@ -6,6 +6,9 @@ import {EnvironmentPageFilter} from 'sentry/components/pageFilters/environment/e import {ProjectPageFilter} from 'sentry/components/pageFilters/project/projectPageFilter'; import {SearchQueryBuilderProvider} from 'sentry/components/searchQueryBuilder/context'; import {t} from 'sentry/locale'; +import {SchemaHintsList} from 'sentry/views/explore/components/schemaHints/schemaHintsList'; +import {SchemaHintsSources} from 'sentry/views/explore/components/schemaHints/schemaHintsUtils'; +import {ExploreSchemaHintsSection} from 'sentry/views/explore/components/styles'; import {TraceItemSearchQueryBuilder} from 'sentry/views/explore/components/traceItemSearchQueryBuilder'; import {StyledPageFilterBar} from 'sentry/views/explore/spans/spansTabSearchSection'; import {TraceItemDataset} from 'sentry/views/explore/types'; @@ -40,6 +43,17 @@ export function ErrorsFilterSection() { stringSecondaryAliases={{}} /> + + + ); From cc595b710b33e43b9d6bf07b6a30a33fbcca75a0 Mon Sep 17 00:00:00 2001 From: nikkikapadia Date: Thu, 2 Apr 2026 14:48:31 -0400 Subject: [PATCH 3/6] add tests for new page content --- static/app/views/explore/errors/body.spec.tsx | 57 +++++++++++++++++++ .../app/views/explore/errors/content.spec.tsx | 28 ++++++++- 2 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 static/app/views/explore/errors/body.spec.tsx diff --git a/static/app/views/explore/errors/body.spec.tsx b/static/app/views/explore/errors/body.spec.tsx new file mode 100644 index 00000000000000..0150fc02ec34c2 --- /dev/null +++ b/static/app/views/explore/errors/body.spec.tsx @@ -0,0 +1,57 @@ +import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary'; + +import {ErrorsContentSection, ErrorsControlSection} from './body'; + +describe('ErrorsControlSection', () => { + beforeEach(() => { + localStorage.clear(); + }); + + it('renders as an aside element', () => { + render(); + expect(screen.getByRole('complementary')).toBeInTheDocument(); + }); +}); + +describe('ErrorsContentSection', () => { + it('renders collapse sidebar button when expanded', () => { + const setControlSectionExpanded = jest.fn(); + render( + + ); + + const collapseButton = screen.getByRole('button', {name: 'Collapse sidebar'}); + expect(collapseButton).toBeInTheDocument(); + expect(collapseButton).not.toHaveTextContent('Advanced'); + }); + + it('renders expand sidebar button when collapsed', () => { + const setControlSectionExpanded = jest.fn(); + render( + + ); + + const expandButton = screen.getByRole('button', {name: 'Expand sidebar'}); + expect(expandButton).toBeInTheDocument(); + expect(expandButton).toHaveTextContent('Advanced'); + }); + + it('calls setControlSectionExpanded when clicking collapse button', async () => { + const setControlSectionExpanded = jest.fn(); + render( + + ); + + await userEvent.click(screen.getByRole('button', {name: 'Collapse sidebar'})); + expect(setControlSectionExpanded).toHaveBeenCalledWith(false); + }); +}); diff --git a/static/app/views/explore/errors/content.spec.tsx b/static/app/views/explore/errors/content.spec.tsx index d4198cf7f3ad4f..19dde6ac6f698f 100644 --- a/static/app/views/explore/errors/content.spec.tsx +++ b/static/app/views/explore/errors/content.spec.tsx @@ -1,11 +1,11 @@ import {OrganizationFixture} from 'sentry-fixture/organization'; import {ProjectFixture} from 'sentry-fixture/project'; -import {render, screen} from 'sentry-test/reactTestingLibrary'; +import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary'; import {PageFiltersStore} from 'sentry/components/pageFilters/store'; -import ErrorsContent from './content'; +import ErrorsContent, {ErrorsBody} from './content'; describe('ErrorsContent', () => { beforeEach(() => { @@ -59,3 +59,27 @@ describe('ErrorsContent', () => { ).toBeInTheDocument(); }); }); + +describe('ErrorsBody', () => { + beforeEach(() => { + localStorage.clear(); + }); + + it('renders with sidebar expanded by default', () => { + render(); + const collapseButton = screen.getByRole('button', {name: 'Collapse sidebar'}); + expect(collapseButton).toBeInTheDocument(); + expect(collapseButton).not.toHaveTextContent('Advanced'); + }); + + it('collapses sidebar when chevron button is clicked', async () => { + render(); + + const collapseButton = screen.getByRole('button', {name: 'Collapse sidebar'}); + await userEvent.click(collapseButton); + + const expandButton = screen.getByRole('button', {name: 'Expand sidebar'}); + expect(expandButton).toBeInTheDocument(); + expect(expandButton).toHaveTextContent('Advanced'); + }); +}); From 7360ea8d8881ff6de10f922323fdb4ac1ad5dc92 Mon Sep 17 00:00:00 2001 From: nikkikapadia Date: Thu, 2 Apr 2026 15:46:27 -0400 Subject: [PATCH 4/6] make general hook for expandable control section --- static/app/views/explore/errors/content.tsx | 25 +++---------------- .../hooks/useControlSectionExpanded.tsx | 19 ++++++++++++++ static/app/views/explore/spans/spansTab.tsx | 24 ++++-------------- 3 files changed, 28 insertions(+), 40 deletions(-) create mode 100644 static/app/views/explore/hooks/useControlSectionExpanded.tsx diff --git a/static/app/views/explore/errors/content.tsx b/static/app/views/explore/errors/content.tsx index 7c0e3549593af2..31b51986fb7339 100644 --- a/static/app/views/explore/errors/content.tsx +++ b/static/app/views/explore/errors/content.tsx @@ -1,5 +1,3 @@ -import {useCallback} from 'react'; - import {FeatureBadge} from '@sentry/scraps/badge'; import {FeedbackButton} from 'sentry/components/feedbackButton/feedbackButton'; @@ -7,7 +5,6 @@ import * as Layout from 'sentry/components/layouts/thirds'; import {PageFiltersContainer} from 'sentry/components/pageFilters/container'; import {SentryDocumentTitle} from 'sentry/components/sentryDocumentTitle'; import {t} from 'sentry/locale'; -import {useLocalStorageState} from 'sentry/utils/useLocalStorageState'; import {useOrganization} from 'sentry/utils/useOrganization'; import { ExploreBodyContent, @@ -18,6 +15,7 @@ import { ErrorsControlSection, } from 'sentry/views/explore/errors/body'; import {ErrorsFilterSection} from 'sentry/views/explore/errors/filterContent'; +import {useControlSectionExpanded} from 'sentry/views/explore/hooks/useControlSectionExpanded'; export default function ErrorsContent() { const organization = useOrganization(); @@ -54,8 +52,9 @@ function ErrorsHeader() { } export function ErrorsBody() { - const [controlSectionExpanded, setControlSectionExpanded] = - useErrorsControlSectionExpanded(); + const [controlSectionExpanded, setControlSectionExpanded] = useControlSectionExpanded( + 'explore-errors-toolbar' + ); return ( @@ -67,19 +66,3 @@ export function ErrorsBody() { ); } - -function useErrorsControlSectionExpanded() { - const [controlSectionExpanded, _setControlSectionExpanded] = useLocalStorageState( - 'explore-errors-toolbar', - 'expanded' - ); - - const setControlSectionExpanded = useCallback( - (expanded: boolean) => { - _setControlSectionExpanded(expanded ? 'expanded' : ''); - }, - [_setControlSectionExpanded] - ); - - return [controlSectionExpanded === 'expanded', setControlSectionExpanded] as const; -} diff --git a/static/app/views/explore/hooks/useControlSectionExpanded.tsx b/static/app/views/explore/hooks/useControlSectionExpanded.tsx new file mode 100644 index 00000000000000..9eb607cc50b0bd --- /dev/null +++ b/static/app/views/explore/hooks/useControlSectionExpanded.tsx @@ -0,0 +1,19 @@ +import {useCallback} from 'react'; + +import {useLocalStorageState} from 'sentry/utils/useLocalStorageState'; + +export function useControlSectionExpanded(localStorageKey: string) { + const [controlSectionExpanded, _setControlSectionExpanded] = useLocalStorageState( + localStorageKey, + 'expanded' + ); + + const setControlSectionExpanded = useCallback( + (expanded: boolean) => { + _setControlSectionExpanded(expanded ? 'expanded' : ''); + }, + [_setControlSectionExpanded] + ); + + return [controlSectionExpanded === 'expanded', setControlSectionExpanded] as const; +} diff --git a/static/app/views/explore/spans/spansTab.tsx b/static/app/views/explore/spans/spansTab.tsx index 1aa501e0ccf00c..a2df2bafb8ee92 100644 --- a/static/app/views/explore/spans/spansTab.tsx +++ b/static/app/views/explore/spans/spansTab.tsx @@ -1,4 +1,4 @@ -import {Fragment, useCallback, useEffect, useMemo} from 'react'; +import {Fragment, useEffect, useMemo} from 'react'; import {css} from '@emotion/react'; import styled from '@emotion/styled'; @@ -22,7 +22,6 @@ import type {Project} from 'sentry/types/project'; import {defined} from 'sentry/utils'; import {DiscoverDatasets} from 'sentry/utils/discover/types'; import {useChartInterval} from 'sentry/utils/useChartInterval'; -import {useLocalStorageState} from 'sentry/utils/useLocalStorageState'; import {useOrganization} from 'sentry/utils/useOrganization'; import {ChartSelectionProvider} from 'sentry/views/explore/components/attributeBreakdowns/chartSelectionContext'; import {OverChartButtonGroup} from 'sentry/views/explore/components/overChartButtonGroup'; @@ -34,6 +33,7 @@ import { } from 'sentry/views/explore/components/styles'; import {Mode} from 'sentry/views/explore/contexts/pageParamsContext/mode'; import {useAnalytics} from 'sentry/views/explore/hooks/useAnalytics'; +import {useControlSectionExpanded} from 'sentry/views/explore/hooks/useControlSectionExpanded'; import {useCrossEventQueries} from 'sentry/views/explore/hooks/useCrossEventQueries'; import {useExploreAggregatesTable} from 'sentry/views/explore/hooks/useExploreAggregatesTable'; import {useExploreSpansTable} from 'sentry/views/explore/hooks/useExploreSpansTable'; @@ -90,22 +90,6 @@ export function SpansTabOnboarding({ ); } -function useControlSectionExpanded() { - const [controlSectionExpanded, _setControlSectionExpanded] = useLocalStorageState( - 'explore-spans-toolbar', - 'expanded' - ); - - const setControlSectionExpanded = useCallback( - (expanded: boolean) => { - _setControlSectionExpanded(expanded ? 'expanded' : ''); - }, - [_setControlSectionExpanded] - ); - - return [controlSectionExpanded === 'expanded', setControlSectionExpanded] as const; -} - interface SpanTabProps { datePageFilterProps: DatePageFilterProps; } @@ -113,7 +97,9 @@ interface SpanTabProps { export function SpansTabContent({datePageFilterProps}: SpanTabProps) { useVisitExplore(); - const [controlSectionExpanded, setControlSectionExpanded] = useControlSectionExpanded(); + const [controlSectionExpanded, setControlSectionExpanded] = useControlSectionExpanded( + 'explore-spans-toolbar' + ); return ( From df461c107410c173a83cb1af7f5369d500f1a270 Mon Sep 17 00:00:00 2001 From: nikkikapadia Date: Thu, 2 Apr 2026 15:53:31 -0400 Subject: [PATCH 5/6] add in errors schema hints options --- .../explore/components/schemaHints/schemaHintsUtils.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/static/app/views/explore/components/schemaHints/schemaHintsUtils.tsx b/static/app/views/explore/components/schemaHints/schemaHintsUtils.tsx index c6ac1a7a7d2caf..dccb123996a0c0 100644 --- a/static/app/views/explore/components/schemaHints/schemaHintsUtils.tsx +++ b/static/app/views/explore/components/schemaHints/schemaHintsUtils.tsx @@ -78,6 +78,11 @@ export const getSchemaHintsListOrder = (source: SchemaHintsSources) => { return SCHEMA_HINTS_LIST_ORDER_KEYS_CONVERSATIONS; } + if (source === SchemaHintsSources.ERRORS) { + // TODO: check to see which keys we want to display for errors + return []; + } + return SCHEMA_HINTS_LIST_ORDER_KEYS_EXPLORE; }; From a1263cd6bdac870a55b84dccdc53e5f40035c43c Mon Sep 17 00:00:00 2001 From: nikkikapadia Date: Thu, 2 Apr 2026 15:56:35 -0400 Subject: [PATCH 6/6] clean up code around new hook --- static/app/views/explore/errors/content.tsx | 4 +++- .../app/views/explore/metrics/metricsTab.tsx | 24 ++++--------------- static/app/views/explore/spans/spansTab.tsx | 4 +++- 3 files changed, 10 insertions(+), 22 deletions(-) diff --git a/static/app/views/explore/errors/content.tsx b/static/app/views/explore/errors/content.tsx index 31b51986fb7339..c90ebe193d5ce4 100644 --- a/static/app/views/explore/errors/content.tsx +++ b/static/app/views/explore/errors/content.tsx @@ -51,9 +51,11 @@ function ErrorsHeader() { ); } +const ERRORS_TOOLBAR_STORAGE_KEY = 'explore-errors-toolbar'; + export function ErrorsBody() { const [controlSectionExpanded, setControlSectionExpanded] = useControlSectionExpanded( - 'explore-errors-toolbar' + ERRORS_TOOLBAR_STORAGE_KEY ); return ( diff --git a/static/app/views/explore/metrics/metricsTab.tsx b/static/app/views/explore/metrics/metricsTab.tsx index a10923b340c4e5..14a248c3ff8767 100644 --- a/static/app/views/explore/metrics/metricsTab.tsx +++ b/static/app/views/explore/metrics/metricsTab.tsx @@ -1,4 +1,3 @@ -import {useCallback} from 'react'; import {css} from '@emotion/react'; import styled from '@emotion/styled'; @@ -13,7 +12,6 @@ import {ProjectPageFilter} from 'sentry/components/pageFilters/project/projectPa import {IconChevron} from 'sentry/icons/iconChevron'; import {t} from 'sentry/locale'; import {useChartInterval} from 'sentry/utils/useChartInterval'; -import {useLocalStorageState} from 'sentry/utils/useLocalStorageState'; import {useOrganization} from 'sentry/utils/useOrganization'; import {WidgetSyncContextProvider} from 'sentry/views/dashboards/contexts/widgetSyncContext'; import {OverChartButtonGroup} from 'sentry/views/explore/components/overChartButtonGroup'; @@ -25,6 +23,7 @@ import { } from 'sentry/views/explore/components/styles'; import {ToolbarVisualizeAddChart} from 'sentry/views/explore/components/toolbar/toolbarVisualize'; import {useMetricsAnalytics} from 'sentry/views/explore/hooks/useAnalytics'; +import {useControlSectionExpanded} from 'sentry/views/explore/hooks/useControlSectionExpanded'; import {useMetricOptions} from 'sentry/views/explore/hooks/useMetricOptions'; import {MetricPanel} from 'sentry/views/explore/metrics/metricPanel'; import {canUseMetricsUIRefresh} from 'sentry/views/explore/metrics/metricsFlags'; @@ -50,25 +49,10 @@ type MetricsTabProps = { const METRICS_TOOLBAR_STORAGE_KEY = 'explore-metrics-toolbar'; -function useMetricsControlSectionExpanded() { - const [controlSectionExpanded, _setControlSectionExpanded] = useLocalStorageState( - METRICS_TOOLBAR_STORAGE_KEY, - 'expanded' - ); - - const setControlSectionExpanded = useCallback( - (expanded: boolean) => { - _setControlSectionExpanded(expanded ? 'expanded' : ''); - }, - [_setControlSectionExpanded] - ); - - return [controlSectionExpanded === 'expanded', setControlSectionExpanded] as const; -} - function MetricsTabContentRefreshLayout({datePageFilterProps}: MetricsTabProps) { - const [controlSectionExpanded, setControlSectionExpanded] = - useMetricsControlSectionExpanded(); + const [controlSectionExpanded, setControlSectionExpanded] = useControlSectionExpanded( + METRICS_TOOLBAR_STORAGE_KEY + ); return ( diff --git a/static/app/views/explore/spans/spansTab.tsx b/static/app/views/explore/spans/spansTab.tsx index a2df2bafb8ee92..18d6128cd9af06 100644 --- a/static/app/views/explore/spans/spansTab.tsx +++ b/static/app/views/explore/spans/spansTab.tsx @@ -94,11 +94,13 @@ interface SpanTabProps { datePageFilterProps: DatePageFilterProps; } +const SPANS_TOOLBAR_STORAGE_KEY = 'explore-spans-toolbar'; + export function SpansTabContent({datePageFilterProps}: SpanTabProps) { useVisitExplore(); const [controlSectionExpanded, setControlSectionExpanded] = useControlSectionExpanded( - 'explore-spans-toolbar' + SPANS_TOOLBAR_STORAGE_KEY ); return (