From 7bfb714e42acd8605bec48577a3f72f3fdb4b24c Mon Sep 17 00:00:00 2001 From: JonasBa Date: Fri, 7 Nov 2025 11:48:46 -0800 Subject: [PATCH 1/7] breadcrumbs: remove styled and simplify --- static/app/components/breadcrumbs.stories.tsx | 25 +--- static/app/components/breadcrumbs.tsx | 136 ++++++++---------- 2 files changed, 64 insertions(+), 97 deletions(-) diff --git a/static/app/components/breadcrumbs.stories.tsx b/static/app/components/breadcrumbs.stories.tsx index b2516449ddd364..10eef8041e6238 100644 --- a/static/app/components/breadcrumbs.stories.tsx +++ b/static/app/components/breadcrumbs.stories.tsx @@ -16,7 +16,11 @@ export default Storybook.story('Breadcrumbs', story => { crumbs={[ {label: 'Organization', to: '/organizations/sentry/'}, {label: 'Projects', to: '/organizations/sentry/projects/'}, - {label: 'Project Settings', to: '/settings/projects/javascript/'}, + { + label: 'Project Settings', + to: '/settings/projects/javascript/', + preservePageFilters: true, + }, {label: 'General', to: null}, ]} /> @@ -24,25 +28,6 @@ export default Storybook.story('Breadcrumbs', story => { )); - story('With Last Item Linked', () => ( - -

- Set to make the last - breadcrumb clickable. -

- - - -
- )); - story('Page Filter Preservation', () => (

diff --git a/static/app/components/breadcrumbs.tsx b/static/app/components/breadcrumbs.tsx index 1f31d0f087eb84..a68f78a5ae291c 100644 --- a/static/app/components/breadcrumbs.tsx +++ b/static/app/components/breadcrumbs.tsx @@ -1,19 +1,12 @@ import {Fragment} from 'react'; -import type {Theme} from '@emotion/react'; -import {css} from '@emotion/react'; -import styled from '@emotion/styled'; + +import {Flex} from '@sentry/scraps/layout'; +import {Text} from '@sentry/scraps/text'; import {Chevron} from 'sentry/components/chevron'; import type {LinkProps} from 'sentry/components/core/link'; import {Link} from 'sentry/components/core/link'; import GlobalSelectionLink from 'sentry/components/globalSelectionLink'; -import {space} from 'sentry/styles/space'; - -const BreadcrumbList = styled('nav')` - display: flex; - align-items: center; - padding: ${space(1)} 0; -`; export interface Crumb { /** @@ -39,72 +32,72 @@ export interface Crumb { to?: LinkProps['to'] | null; } -interface Props extends React.HTMLAttributes { - /** - * Array of crumbs that will be rendered - */ +interface BreadcrumbsProps extends React.HTMLAttributes { crumbs: Crumb[]; - - /** - * As a general rule of thumb we don't want the last item to be link as it most likely - * points to the same page we are currently on. This is by default false, so that - * people don't have to check if crumb is last in the array and then manually - * assign `to: null/undefined` when passing props to this component. - */ - linkLastItem?: boolean; } /** * Page breadcrumbs used for navigation, not to be confused with sentry's event breadcrumbs */ -export function Breadcrumbs({crumbs, linkLastItem = false, ...props}: Props) { +export function Breadcrumbs({crumbs, ...props}: BreadcrumbsProps) { if (crumbs.length === 0) { return null; } - if (!linkLastItem) { + if (crumbs[crumbs.length - 1]?.to) { crumbs[crumbs.length - 1]!.to = null; } return ( - + {crumbs.map((crumb, index) => { - const {label, to, preservePageFilters, key} = crumb; - const labelKey = typeof label === 'string' ? label : ''; - const mapKey = - key ?? (typeof to === 'string' ? `${labelKey}${to}` : `${labelKey}${index}`); - return ( - - {to ? ( - - {label} - - ) : ( - {label} - )} - - {index < crumbs.length - 1 && } + + + {index < crumbs.length - 1 ? : null} ); })} - + ); } -const getBreadcrumbListItemStyles = (p: {theme: Theme}) => css` - ${p.theme.overflowEllipsis} - color: ${p.theme.subText}; - width: auto; +interface BreadCrumbItemProps { + crumb: Crumb; + variant: 'primary' | 'muted'; +} - &:last-child { - color: ${p.theme.textColor}; +function BreadCrumbItem(props: BreadCrumbItemProps) { + if (props.crumb.to) { + return ( + + + {props.crumb.label} + + + ); } -`; + return ( + + + {props.crumb.label} + + + ); +} interface BreadcrumbLinkProps { to: LinkProps['to']; @@ -112,30 +105,19 @@ interface BreadcrumbLinkProps { preservePageFilters?: boolean; } -const BreadcrumbLink = styled( - ({preservePageFilters, to, ...props}: BreadcrumbLinkProps) => - preservePageFilters ? ( - - ) : ( - - ) -)` - ${getBreadcrumbListItemStyles} - max-width: 400px; - - &:hover, - &:active { - color: ${p => p.theme.subText}; +function BreadcrumbLink(props: BreadcrumbLinkProps) { + if (props.preservePageFilters) { + return ; } -`; - -const BreadcrumbItem = styled('span')` - ${getBreadcrumbListItemStyles} - max-width: 400px; -`; + return ; +} -const BreadcrumbDividerIcon = styled(Chevron)` - color: ${p => p.theme.subText}; - margin: 0 ${space(0.5)}; - flex-shrink: 0; -`; +function BreadcumbChevron() { + return ( + + + + + + ); +} From a46d84cf75f08c8718da4258aaaa410c479f59f5 Mon Sep 17 00:00:00 2001 From: JonasBa Date: Fri, 7 Nov 2025 11:59:19 -0800 Subject: [PATCH 2/7] breadcrumbs: add breadcrumb events --- static/app/components/breadcrumbs.stories.tsx | 2 +- static/app/components/breadcrumbs.tsx | 52 +++++++------------ static/app/utils/analytics.tsx | 4 ++ .../analytics/breadcrumbsAnalyticsEvents.tsx | 14 +++++ static/app/views/discover/landing.tsx | 2 - .../header/buildCompareHeaderContent.tsx | 6 +-- .../header/replayDetailsPageBreadcrumbs.tsx | 4 +- 7 files changed, 45 insertions(+), 39 deletions(-) create mode 100644 static/app/utils/analytics/breadcrumbsAnalyticsEvents.tsx diff --git a/static/app/components/breadcrumbs.stories.tsx b/static/app/components/breadcrumbs.stories.tsx index 10eef8041e6238..63e787d07859c9 100644 --- a/static/app/components/breadcrumbs.stories.tsx +++ b/static/app/components/breadcrumbs.stories.tsx @@ -15,7 +15,7 @@ export default Storybook.story('Breadcrumbs', story => { ; /** * It will keep the page filter values (projects, environments, time) in the @@ -45,6 +40,7 @@ export function Breadcrumbs({crumbs, ...props}: BreadcrumbsProps) { } if (crumbs[crumbs.length - 1]?.to) { + // We should not be mutating the crumbs crumbs[crumbs.length - 1]!.to = null; } @@ -53,8 +49,8 @@ export function Breadcrumbs({crumbs, ...props}: BreadcrumbsProps) { gap="xs" align="center" padding="md 0" - {...props} data-test-id="breadcrumb-list" + {...props} > {crumbs.map((crumb, index) => { return ( @@ -63,7 +59,9 @@ export function Breadcrumbs({crumbs, ...props}: BreadcrumbsProps) { crumb={crumb} variant={index === crumbs.length - 1 ? 'primary' : 'muted'} /> - {index < crumbs.length - 1 ? : null} + {index < crumbs.length - 1 ? ( + + ) : null} ); })} @@ -77,30 +75,29 @@ interface BreadCrumbItemProps { } function BreadCrumbItem(props: BreadCrumbItemProps) { + function onBreadcrumbLinkClick() { + if (props.crumb.to) { + trackAnalytics('breadcrumbs.link.clicked', {organization: null}); + } + } + if (props.crumb.to) { return ( - - {props.crumb.label} - + {props.crumb.label} ); } - return ( - - - {props.crumb.label} - - - ); + + return {props.crumb.label}; } -interface BreadcrumbLinkProps { - to: LinkProps['to']; +interface BreadcrumbLinkProps extends LinkProps { children?: React.ReactNode; preservePageFilters?: boolean; } @@ -109,15 +106,6 @@ function BreadcrumbLink(props: BreadcrumbLinkProps) { if (props.preservePageFilters) { return ; } - return ; -} -function BreadcumbChevron() { - return ( - - - - - - ); + return ; } diff --git a/static/app/utils/analytics.tsx b/static/app/utils/analytics.tsx index f81190492e8e4c..9cf3b07d6c5fa6 100644 --- a/static/app/utils/analytics.tsx +++ b/static/app/utils/analytics.tsx @@ -40,6 +40,8 @@ import { import type {AgentMonitoringEventParameters} from './analytics/agentMonitoringAnalyticsEvents'; import {agentMonitoringEventMap} from './analytics/agentMonitoringAnalyticsEvents'; +import type {BreadcrumbsAnalyticsEventParameters} from './analytics/breadcrumbsAnalyticsEvents'; +import {breadcrumbsAnalyticsEventMap} from './analytics/breadcrumbsAnalyticsEvents'; import type {CoreUIEventParameters} from './analytics/coreuiAnalyticsEvents'; import {coreUIEventMap} from './analytics/coreuiAnalyticsEvents'; import type {DashboardsEventParameters} from './analytics/dashboardsAnalyticsEvents'; @@ -98,6 +100,7 @@ interface EventParameters extends GrowthEventParameters, AgentMonitoringEventParameters, AlertsEventParameters, + BreadcrumbsAnalyticsEventParameters, CoreUIEventParameters, DashboardsEventParameters, DiscoverEventParameters, @@ -135,6 +138,7 @@ interface EventParameters const allEventMap: Record = { ...agentMonitoringEventMap, ...alertsEventMap, + ...breadcrumbsAnalyticsEventMap, ...coreUIEventMap, ...dashboardsEventMap, ...discoverEventMap, diff --git a/static/app/utils/analytics/breadcrumbsAnalyticsEvents.tsx b/static/app/utils/analytics/breadcrumbsAnalyticsEvents.tsx new file mode 100644 index 00000000000000..b692fbc6db4de8 --- /dev/null +++ b/static/app/utils/analytics/breadcrumbsAnalyticsEvents.tsx @@ -0,0 +1,14 @@ +export type BreadcrumbsAnalyticsEventParameters = { + 'breadcrumbs.link.clicked': {organization: null}; + 'breadcrumbs.menu.clicked': {organization: null}; +}; + +export type BreadcrumbsAnalyticsKey = keyof BreadcrumbsAnalyticsEventParameters; + +export const breadcrumbsAnalyticsEventMap: Record< + BreadcrumbsAnalyticsKey, + string | null +> = { + 'breadcrumbs.link.clicked': 'Breadcrumbs: Link Clicked', + 'breadcrumbs.menu.clicked': 'Breadcrumbs: Menu Clicked', +}; diff --git a/static/app/views/discover/landing.tsx b/static/app/views/discover/landing.tsx index ad11f8a7e8e4b4..d7a1c713bdef90 100644 --- a/static/app/views/discover/landing.tsx +++ b/static/app/views/discover/landing.tsx @@ -187,12 +187,10 @@ function DiscoverLanding() { - ) : null, + ) : ( + t('Project') + ), }; const replayCrumb = { From a4420204add11f6230a878640c2486df21d16012 Mon Sep 17 00:00:00 2001 From: JonasBa Date: Fri, 7 Nov 2025 12:56:24 -0800 Subject: [PATCH 3/7] breadcrumbs: fix overflowing --- static/app/components/breadcrumbs.stories.tsx | 3 +- static/app/components/breadcrumbs.tsx | 44 +++++---- static/app/components/chevron.tsx | 90 ------------------- .../analytics/breadcrumbsAnalyticsEvents.tsx | 4 +- 4 files changed, 31 insertions(+), 110 deletions(-) delete mode 100644 static/app/components/chevron.tsx diff --git a/static/app/components/breadcrumbs.stories.tsx b/static/app/components/breadcrumbs.stories.tsx index 63e787d07859c9..a85acb0dfb2b89 100644 --- a/static/app/components/breadcrumbs.stories.tsx +++ b/static/app/components/breadcrumbs.stories.tsx @@ -72,7 +72,8 @@ export default Storybook.story('Breadcrumbs', story => { ], [ { - label: 'longlonglonglonglonglonglonglonglonglonglonglonglonglonglong', + label: + 'A Very Long Project Name Here That Will Be Truncated Because It Is Too Long', to: '/org/', }, {label: 'Very Long Project Name Here', to: '/project/'}, diff --git a/static/app/components/breadcrumbs.tsx b/static/app/components/breadcrumbs.tsx index 3b6d9c79e272c6..43b7ecf743d6b0 100644 --- a/static/app/components/breadcrumbs.tsx +++ b/static/app/components/breadcrumbs.tsx @@ -1,6 +1,6 @@ import {Fragment} from 'react'; -import {Flex} from '@sentry/scraps/layout'; +import {Container, Flex} from '@sentry/scraps/layout'; import {Text} from '@sentry/scraps/text'; import type {LinkProps} from 'sentry/components/core/link'; @@ -46,6 +46,7 @@ export function Breadcrumbs({crumbs, ...props}: BreadcrumbsProps) { return ( {index < crumbs.length - 1 ? ( - + + + ) : null} ); @@ -81,20 +84,29 @@ function BreadCrumbItem(props: BreadCrumbItemProps) { } } - if (props.crumb.to) { - return ( - - {props.crumb.label} - - ); - } - - return {props.crumb.label}; + return ( + + {styleProps => { + return props.crumb.to ? ( + + + {props.crumb.label} + + + ) : ( + + {props.crumb.label} + + ); + }} + + ); } interface BreadcrumbLinkProps extends LinkProps { diff --git a/static/app/components/chevron.tsx b/static/app/components/chevron.tsx deleted file mode 100644 index 57f5c0afe940d5..00000000000000 --- a/static/app/components/chevron.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import {useTheme} from '@emotion/react'; -import styled from '@emotion/styled'; -import {motion} from 'framer-motion'; - -interface ChevronProps extends React.SVGAttributes { - direction?: 'up' | 'right' | 'down' | 'left'; - /** - * Whether to lighten (by lowering the opacity) the chevron. Useful if the chevron is - * inside a dropdown trigger button. - */ - light?: boolean; - /** - * The size of the checkbox. Defaults to 'sm'. - */ - size?: 'large' | 'medium' | 'small'; - weight?: 'regular' | 'medium'; -} - -const rubikWeightFactor: Record, number> = { - regular: 1, - medium: 1.4, -}; - -function getPath(direction: NonNullable) { - // Base values for a downward chevron - const base = [ - [3.5, 5.5], - [7, 9], - [10.5, 5.5], - ] as const; - - switch (direction) { - case 'right': - // Switch X and Y axis (so `base[0][1]` goes before `base[0][1]`) - return `M${base[0][1]} ${base[0][0]}L${base[1][1]} ${base[1][0]}L${base[2][1]} ${base[2][0]}`; - case 'left': - // Switch X and Y axis, then flip X axis (so 14 - …) - return `M${14 - base[0][1]} ${base[0][0]}L${14 - base[1][1]} ${base[1][0]}L${14 - base[2][1]} ${base[2][0]}`; - case 'up': - // Flip Y axis (so 14 - …) - return `M${base[0][0]} ${14 - base[0][1]}L${base[1][0]} ${14 - base[1][1]}L${base[2][0]} ${14 - base[2][1]}`; - case 'down': - default: - return `M${base[0][0]} ${base[0][1]}L${base[1][0]} ${base[1][1]}L${base[2][0]} ${base[2][1]}`; - } -} - -function Chevron({ - size = 'medium', - weight = 'regular', - direction = 'down', - light = false, - ...props -}: ChevronProps) { - const theme = useTheme(); - return ( - - - - ); -} - -const VariableWeightIcon = styled('svg')<{size: string; weightFactor: number}>` - width: ${p => p.size}; - height: ${p => p.size}; - - fill: none; - stroke: currentColor; - stroke-linecap: round; - stroke-linejoin: round; - stroke-width: calc(${p => p.size} * 0.0875 * ${p => p.weightFactor}); -`; - -export {Chevron}; diff --git a/static/app/utils/analytics/breadcrumbsAnalyticsEvents.tsx b/static/app/utils/analytics/breadcrumbsAnalyticsEvents.tsx index b692fbc6db4de8..7aef553b5eb85c 100644 --- a/static/app/utils/analytics/breadcrumbsAnalyticsEvents.tsx +++ b/static/app/utils/analytics/breadcrumbsAnalyticsEvents.tsx @@ -3,10 +3,8 @@ export type BreadcrumbsAnalyticsEventParameters = { 'breadcrumbs.menu.clicked': {organization: null}; }; -export type BreadcrumbsAnalyticsKey = keyof BreadcrumbsAnalyticsEventParameters; - export const breadcrumbsAnalyticsEventMap: Record< - BreadcrumbsAnalyticsKey, + keyof BreadcrumbsAnalyticsEventParameters, string | null > = { 'breadcrumbs.link.clicked': 'Breadcrumbs: Link Clicked', From be5c9b8ff717a1e5490100674b418244390d6fac Mon Sep 17 00:00:00 2001 From: JonasBa Date: Fri, 7 Nov 2025 13:08:43 -0800 Subject: [PATCH 4/7] settings: add instrumentation --- .../app/utils/analytics/breadcrumbsAnalyticsEvents.tsx | 4 ++-- .../settings/components/settingsBreadcrumb/index.tsx | 10 +++++++++- .../settingsBreadcrumb/organizationCrumb.tsx | 6 ++++++ .../components/settingsBreadcrumb/projectCrumb.tsx | 6 ++++++ .../components/settingsBreadcrumb/teamCrumb.tsx | 6 ++++++ 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/static/app/utils/analytics/breadcrumbsAnalyticsEvents.tsx b/static/app/utils/analytics/breadcrumbsAnalyticsEvents.tsx index 7aef553b5eb85c..2c271357ac326d 100644 --- a/static/app/utils/analytics/breadcrumbsAnalyticsEvents.tsx +++ b/static/app/utils/analytics/breadcrumbsAnalyticsEvents.tsx @@ -1,6 +1,6 @@ export type BreadcrumbsAnalyticsEventParameters = { 'breadcrumbs.link.clicked': {organization: null}; - 'breadcrumbs.menu.clicked': {organization: null}; + 'breadcrumbs.menu.opened': {organization: null}; }; export const breadcrumbsAnalyticsEventMap: Record< @@ -8,5 +8,5 @@ export const breadcrumbsAnalyticsEventMap: Record< string | null > = { 'breadcrumbs.link.clicked': 'Breadcrumbs: Link Clicked', - 'breadcrumbs.menu.clicked': 'Breadcrumbs: Menu Clicked', + 'breadcrumbs.menu.opened': 'Breadcrumbs: Menu Opened', }; diff --git a/static/app/views/settings/components/settingsBreadcrumb/index.tsx b/static/app/views/settings/components/settingsBreadcrumb/index.tsx index 47a39973f26cc5..e7a01d373aea67 100644 --- a/static/app/views/settings/components/settingsBreadcrumb/index.tsx +++ b/static/app/views/settings/components/settingsBreadcrumb/index.tsx @@ -3,6 +3,7 @@ import styled from '@emotion/styled'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; +import {trackAnalytics} from 'sentry/utils/analytics'; import getRouteStringFromRoutes from 'sentry/utils/getRouteStringFromRoutes'; import recreateRoute from 'sentry/utils/recreateRoute'; @@ -31,6 +32,10 @@ function SettingsBreadcrumb({className, routes, params}: Props) { const lastRouteIndex = routes.map(r => !!r.name).lastIndexOf(true); + function onSettingsBreadcrumbLinkClick() { + trackAnalytics('breadcrumbs.link.clicked', {organization: null}); + } + return ( {routes.map((route, i) => { @@ -54,7 +59,10 @@ function SettingsBreadcrumb({className, routes, params}: Props) { } return ( - + {pathTitle || route.name} {isLast ? null : } diff --git a/static/app/views/settings/components/settingsBreadcrumb/organizationCrumb.tsx b/static/app/views/settings/components/settingsBreadcrumb/organizationCrumb.tsx index 2eb46fefef64dc..e96c20cb3e6ecd 100644 --- a/static/app/views/settings/components/settingsBreadcrumb/organizationCrumb.tsx +++ b/static/app/views/settings/components/settingsBreadcrumb/organizationCrumb.tsx @@ -6,6 +6,7 @@ import IdBadge from 'sentry/components/idBadge'; import {t} from 'sentry/locale'; import OrganizationsStore from 'sentry/stores/organizationsStore'; import {useLegacyStore} from 'sentry/stores/useLegacyStore'; +import {trackAnalytics} from 'sentry/utils/analytics'; import recreateRoute from 'sentry/utils/recreateRoute'; import {resolveRoute} from 'sentry/utils/resolveRoute'; import {testableWindowLocation} from 'sentry/utils/testableWindowLocation'; @@ -76,6 +77,11 @@ function OrganizationCrumb({routes, route, ...props}: SettingsBreadcrumbProps) { } onCrumbSelect={handleSelect} + onOpenChange={open => { + if (open) { + trackAnalytics('breadcrumbs.menu.opened', {organization: null}); + } + }} hasMenu={hasMenu} route={route} value={organization.slug} diff --git a/static/app/views/settings/components/settingsBreadcrumb/projectCrumb.tsx b/static/app/views/settings/components/settingsBreadcrumb/projectCrumb.tsx index 0f7689eb1ebe55..6f10e1638e6ab6 100644 --- a/static/app/views/settings/components/settingsBreadcrumb/projectCrumb.tsx +++ b/static/app/views/settings/components/settingsBreadcrumb/projectCrumb.tsx @@ -4,6 +4,7 @@ import {ProjectAvatar} from 'sentry/components/core/avatar/projectAvatar'; import IdBadge from 'sentry/components/idBadge'; import LoadingIndicator from 'sentry/components/loadingIndicator'; import {space} from 'sentry/styles/space'; +import {trackAnalytics} from 'sentry/utils/analytics'; import recreateRoute from 'sentry/utils/recreateRoute'; import replaceRouterParams from 'sentry/utils/replaceRouterParams'; import {useNavigate} from 'sentry/utils/useNavigate'; @@ -65,6 +66,11 @@ function ProjectCrumb({routes, route, ...props}: SettingsBreadcrumbProps) { } value={activeProject?.slug ?? ''} onCrumbSelect={handleSelect} + onOpenChange={open => { + if (open) { + trackAnalytics('breadcrumbs.menu.opened', {organization: null}); + } + }} onSearch={onSearch} options={projects.map(project => ({ value: project.slug, diff --git a/static/app/views/settings/components/settingsBreadcrumb/teamCrumb.tsx b/static/app/views/settings/components/settingsBreadcrumb/teamCrumb.tsx index 6c210a254a90eb..81b6f011443b55 100644 --- a/static/app/views/settings/components/settingsBreadcrumb/teamCrumb.tsx +++ b/static/app/views/settings/components/settingsBreadcrumb/teamCrumb.tsx @@ -1,6 +1,7 @@ import {TeamAvatar} from 'sentry/components/core/avatar/teamAvatar'; import IdBadge from 'sentry/components/idBadge'; import {t} from 'sentry/locale'; +import {trackAnalytics} from 'sentry/utils/analytics'; import recreateRoute from 'sentry/utils/recreateRoute'; import {useNavigate} from 'sentry/utils/useNavigate'; import {useParams} from 'sentry/utils/useParams'; @@ -38,6 +39,11 @@ function TeamCrumb({routes, route, ...props}: SettingsBreadcrumbProps) { }) ); }} + onOpenChange={open => { + if (open) { + trackAnalytics('breadcrumbs.menu.opened', {organization: null}); + } + }} hasMenu={hasMenu} route={route} value={team.slug} From 4c9059cca86f28c8384cfffd78620e5824eb8da0 Mon Sep 17 00:00:00 2001 From: JonasBa Date: Fri, 7 Nov 2025 13:36:39 -0800 Subject: [PATCH 5/7] breadcrumbs: fix prop bleed --- static/app/components/breadcrumbs.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/static/app/components/breadcrumbs.tsx b/static/app/components/breadcrumbs.tsx index 43b7ecf743d6b0..b3ed950cccedaf 100644 --- a/static/app/components/breadcrumbs.tsx +++ b/static/app/components/breadcrumbs.tsx @@ -100,7 +100,12 @@ function BreadCrumbItem(props: BreadCrumbItemProps) { ) : ( - + {props.crumb.label} ); @@ -115,9 +120,10 @@ interface BreadcrumbLinkProps extends LinkProps { } function BreadcrumbLink(props: BreadcrumbLinkProps) { - if (props.preservePageFilters) { - return ; + const {preservePageFilters, ...rest} = props; + if (preservePageFilters) { + return ; } - return ; + return ; } From 3b45509691a1d417c698a9f0cd6bb7651e859320 Mon Sep 17 00:00:00 2001 From: JonasBa Date: Fri, 7 Nov 2025 14:07:33 -0800 Subject: [PATCH 6/7] test: use role selector --- static/app/views/discover/landing.spec.tsx | 2 +- static/app/views/discover/results.spec.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/static/app/views/discover/landing.spec.tsx b/static/app/views/discover/landing.spec.tsx index 74562fcf2def8b..c05ee114b0b567 100644 --- a/static/app/views/discover/landing.spec.tsx +++ b/static/app/views/discover/landing.spec.tsx @@ -116,7 +116,7 @@ describe('Discover > Landing', () => { ); - expect(await screen.findByText('Discover')).toHaveAttribute( + expect(await screen.findByRole('link', {name: 'Discover'})).toHaveAttribute( 'href', '/organizations/org-slug/explore/discover/homepage/' ); diff --git a/static/app/views/discover/results.spec.tsx b/static/app/views/discover/results.spec.tsx index bc6f82c1fc53f3..0235ef6dbd628d 100644 --- a/static/app/views/discover/results.spec.tsx +++ b/static/app/views/discover/results.spec.tsx @@ -1204,7 +1204,7 @@ describe('Results', () => { expect(measurementsMetaMock).toHaveBeenCalled(); }); - expect(screen.getByText('Discover')).toHaveAttribute( + expect(screen.getByRole('link', {name: 'Discover'})).toHaveAttribute( 'href', expect.stringMatching( new RegExp('^/organizations/org-slug/explore/discover/homepage/') From 49bbbdd332e69bc50d3b40770d135fd853fbae73 Mon Sep 17 00:00:00 2001 From: JonasBa Date: Thu, 13 Nov 2025 10:52:56 -0800 Subject: [PATCH 7/7] copy crumb to remove link --- static/app/components/breadcrumbs.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/static/app/components/breadcrumbs.tsx b/static/app/components/breadcrumbs.tsx index b3ed950cccedaf..8458571338a012 100644 --- a/static/app/components/breadcrumbs.tsx +++ b/static/app/components/breadcrumbs.tsx @@ -39,11 +39,6 @@ export function Breadcrumbs({crumbs, ...props}: BreadcrumbsProps) { return null; } - if (crumbs[crumbs.length - 1]?.to) { - // We should not be mutating the crumbs - crumbs[crumbs.length - 1]!.to = null; - } - return ( {index < crumbs.length - 1 ? (