11import { Fragment } from 'react' ;
2- import type { Theme } from '@emotion/react' ;
3- import { css } from '@emotion/react ' ;
4- import styled from '@emotion/styled ' ;
2+
3+ import { Flex } from '@sentry/scraps/layout ' ;
4+ import { Text } from '@sentry/scraps/text ' ;
55
66import { Chevron } from 'sentry/components/chevron' ;
77import type { LinkProps } from 'sentry/components/core/link' ;
88import { Link } from 'sentry/components/core/link' ;
99import GlobalSelectionLink from 'sentry/components/globalSelectionLink' ;
10- import { space } from 'sentry/styles/space' ;
11-
12- const BreadcrumbList = styled ( 'nav' ) `
13- display: flex;
14- align-items: center;
15- padding: ${ space ( 1 ) } 0;
16- ` ;
1710
1811export interface Crumb {
1912 /**
@@ -39,103 +32,92 @@ export interface Crumb {
3932 to ?: LinkProps [ 'to' ] | null ;
4033}
4134
42- interface Props extends React . HTMLAttributes < HTMLDivElement > {
43- /**
44- * Array of crumbs that will be rendered
45- */
35+ interface BreadcrumbsProps extends React . HTMLAttributes < HTMLDivElement > {
4636 crumbs : Crumb [ ] ;
47-
48- /**
49- * As a general rule of thumb we don't want the last item to be link as it most likely
50- * points to the same page we are currently on. This is by default false, so that
51- * people don't have to check if crumb is last in the array and then manually
52- * assign `to: null/undefined` when passing props to this component.
53- */
54- linkLastItem ?: boolean ;
5537}
5638
5739/**
5840 * Page breadcrumbs used for navigation, not to be confused with sentry's event breadcrumbs
5941 */
60- export function Breadcrumbs ( { crumbs, linkLastItem = false , ...props } : Props ) {
42+ export function Breadcrumbs ( { crumbs, ...props } : BreadcrumbsProps ) {
6143 if ( crumbs . length === 0 ) {
6244 return null ;
6345 }
6446
65- if ( ! linkLastItem ) {
47+ if ( crumbs [ crumbs . length - 1 ] ?. to ) {
6648 crumbs [ crumbs . length - 1 ] ! . to = null ;
6749 }
6850
6951 return (
70- < BreadcrumbList { ...props } data-test-id = "breadcrumb-list" >
52+ < Flex
53+ gap = "xs"
54+ align = "center"
55+ padding = "md 0"
56+ { ...props }
57+ data-test-id = "breadcrumb-list"
58+ >
7159 { crumbs . map ( ( crumb , index ) => {
72- const { label, to, preservePageFilters, key} = crumb ;
73- const labelKey = typeof label === 'string' ? label : '' ;
74- const mapKey =
75- key ?? ( typeof to === 'string' ? `${ labelKey } ${ to } ` : `${ labelKey } ${ index } ` ) ;
76-
7760 return (
78- < Fragment key = { mapKey } >
79- { to ? (
80- < BreadcrumbLink
81- to = { to }
82- preservePageFilters = { preservePageFilters }
83- data-test-id = "breadcrumb-link"
84- >
85- { label }
86- </ BreadcrumbLink >
87- ) : (
88- < BreadcrumbItem data-test-id = "breadcrumb-item" > { label } </ BreadcrumbItem >
89- ) }
90-
91- { index < crumbs . length - 1 && < BreadcrumbDividerIcon direction = "right" /> }
61+ < Fragment key = { index } >
62+ < BreadCrumbItem
63+ crumb = { crumb }
64+ variant = { index === crumbs . length - 1 ? 'primary' : 'muted' }
65+ />
66+ { index < crumbs . length - 1 ? < BreadcumbChevron /> : null }
9267 </ Fragment >
9368 ) ;
9469 } ) }
95- </ BreadcrumbList >
70+ </ Flex >
9671 ) ;
9772}
9873
99- const getBreadcrumbListItemStyles = ( p : { theme : Theme } ) => css `
100- ${ p . theme . overflowEllipsis }
101- color : ${ p . theme . subText } ;
102- width : auto;
74+ interface BreadCrumbItemProps {
75+ crumb : Crumb ;
76+ variant : 'primary' | 'muted' ;
77+ }
10378
104- & : last-child {
105- color : ${ p . theme . textColor } ;
79+ function BreadCrumbItem ( props : BreadCrumbItemProps ) {
80+ if ( props . crumb . to ) {
81+ return (
82+ < BreadcrumbLink
83+ to = { props . crumb . to }
84+ preservePageFilters = { props . crumb . preservePageFilters }
85+ data-test-id = "breadcrumb-link"
86+ >
87+ < Text as = "span" variant = "muted" >
88+ { props . crumb . label }
89+ </ Text >
90+ </ BreadcrumbLink >
91+ ) ;
10692 }
107- ` ;
93+ return (
94+ < Flex data-test-id = "breadcrumb-item" maxWidth = "400px" align = "center" >
95+ < Text as = "span" ellipsis variant = { props . variant } >
96+ { props . crumb . label }
97+ </ Text >
98+ </ Flex >
99+ ) ;
100+ }
108101
109102interface BreadcrumbLinkProps {
110103 to : LinkProps [ 'to' ] ;
111104 children ?: React . ReactNode ;
112105 preservePageFilters ?: boolean ;
113106}
114107
115- const BreadcrumbLink = styled (
116- ( { preservePageFilters, to, ...props } : BreadcrumbLinkProps ) =>
117- preservePageFilters ? (
118- < GlobalSelectionLink to = { to } { ...props } />
119- ) : (
120- < Link to = { to } { ...props } />
121- )
122- ) `
123- ${ getBreadcrumbListItemStyles }
124- max-width: 400px;
125-
126- &:hover,
127- &:active {
128- color: ${ p => p . theme . subText } ;
108+ function BreadcrumbLink ( props : BreadcrumbLinkProps ) {
109+ if ( props . preservePageFilters ) {
110+ return < GlobalSelectionLink { ...props } /> ;
129111 }
130- ` ;
131-
132- const BreadcrumbItem = styled ( 'span' ) `
133- ${ getBreadcrumbListItemStyles }
134- max-width: 400px;
135- ` ;
112+ return < Link { ...props } /> ;
113+ }
136114
137- const BreadcrumbDividerIcon = styled ( Chevron ) `
138- color: ${ p => p . theme . subText } ;
139- margin: 0 ${ space ( 0.5 ) } ;
140- flex-shrink: 0;
141- ` ;
115+ function BreadcumbChevron ( ) {
116+ return (
117+ < Text variant = "muted" >
118+ < Flex flexShrink = { 0 } align = "center" >
119+ < Chevron direction = "right" color = "subText" />
120+ </ Flex >
121+ </ Text >
122+ ) ;
123+ }
0 commit comments