Skip to content

Commit e9f0e19

Browse files
committed
feat(ui-breadcrumb): migrate to new theming system and add new lucide icon and change separator style calculation
1 parent c4d09d5 commit e9f0e19

File tree

8 files changed

+69
-136
lines changed

8 files changed

+69
-136
lines changed

packages/ui-breadcrumb/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"@babel/runtime": "^7.27.6",
3636
"@instructure/emotion": "workspace:*",
3737
"@instructure/shared-types": "workspace:*",
38-
"@instructure/ui-icons": "workspace:*",
38+
"@instructure/ui-icons-lucide": "workspace:*",
3939
"@instructure/ui-link": "workspace:*",
4040
"@instructure/ui-react-utils": "workspace:*",
4141
"@instructure/ui-tooltip": "workspace:*",

packages/ui-breadcrumb/src/Breadcrumb/BreadcrumbLink/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ class BreadcrumbLink extends Component<
7474
iconPlacement,
7575
onClick,
7676
onMouseEnter,
77-
isCurrentPage
77+
isCurrentPage,
78+
size
7879
} = this.props
79-
8080
const { isTruncated } = this.state
8181
const props = omitProps(this.props, BreadcrumbLink.allowedProps)
8282

@@ -99,6 +99,7 @@ class BreadcrumbLink extends Component<
9999
isWithinText={false}
100100
elementRef={this.handleRef}
101101
forceButtonRole={false}
102+
size={size}
102103
{...(isCurrentPage && { 'aria-current': 'page' })}
103104
{...(isTruncated && {
104105
...(typeof children === 'string' && { 'aria-label': children }),

packages/ui-breadcrumb/src/Breadcrumb/index.tsx

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,16 @@
2222
* SOFTWARE.
2323
*/
2424

25-
import {
26-
isValidElement,
27-
cloneElement,
28-
Children,
29-
Component,
30-
ReactElement
31-
} from 'react'
25+
import { isValidElement, Children, Component, ReactElement } from 'react'
3226

3327
import { View } from '@instructure/ui-view'
28+
import { safeCloneElement } from '@instructure/ui-react-utils'
3429

35-
import { withStyleRework as withStyle } from '@instructure/emotion'
36-
import { IconArrowOpenEndSolid } from '@instructure/ui-icons'
30+
import { withStyle } from '@instructure/emotion'
31+
import { ChevronRightInstUIIcon } from '@instructure/ui-icons-lucide'
3732
import { BreadcrumbLink } from './BreadcrumbLink'
3833

3934
import generateStyle from './styles'
40-
import generateComponentTheme from './theme'
4135

4236
import { allowedProps } from './props'
4337
import type { BreadcrumbProps } from './props'
@@ -48,7 +42,7 @@ category: components
4842
---
4943
**/
5044

51-
@withStyle(generateStyle, generateComponentTheme)
45+
@withStyle(generateStyle, 'Breadcrumb')
5246
class Breadcrumb extends Component<BreadcrumbProps> {
5347
static readonly componentId = 'Breadcrumb'
5448

@@ -65,7 +59,7 @@ class Breadcrumb extends Component<BreadcrumbProps> {
6559
}
6660

6761
addAriaCurrent = (child: React.ReactNode) => {
68-
const updatedChild = cloneElement(
62+
const updatedChild = safeCloneElement(
6963
child as React.ReactElement<{ 'aria-current'?: string }>,
7064
{
7165
'aria-current': 'page'
@@ -77,14 +71,15 @@ class Breadcrumb extends Component<BreadcrumbProps> {
7771
componentDidMount() {
7872
this.props.makeStyles?.()
7973
}
74+
8075
componentDidUpdate() {
8176
this.props.makeStyles?.()
8277
}
8378

8479
static Link = BreadcrumbLink
8580

8681
renderChildren() {
87-
const { styles, children } = this.props
82+
const { styles, children, size } = this.props
8883
const numChildren = Children.count(children)
8984
const inlineStyle = {
9085
maxWidth: `${Math.floor(100 / numChildren)}%`
@@ -105,17 +100,34 @@ class Breadcrumb extends Component<BreadcrumbProps> {
105100
isAriaCurrentSet = true
106101
}
107102
}
103+
104+
// Clone child (BreadcrumbLink) and pass size prop
105+
const childWithSize = isValidElement(child)
106+
? safeCloneElement(child as ReactElement<any>, { size })
107+
: child
108+
109+
// Map Breadcrumb sizes to icon size tokens
110+
const breadcrumbSizeToIconSize = {
111+
small: 'sm',
112+
medium: 'md',
113+
large: 'lg'
114+
} as const
115+
116+
const iconSize = breadcrumbSizeToIconSize[size || 'small']
117+
108118
return (
109-
<li css={styles?.crumb} style={inlineStyle}>
110-
{!isAriaCurrentSet &&
111-
isLastElement &&
112-
(child as React.ReactElement<any>).props.isCurrentPage !== false
113-
? this.addAriaCurrent(child)
114-
: child}
119+
<>
120+
<li css={styles?.crumb} style={inlineStyle}>
121+
{!isAriaCurrentSet &&
122+
isLastElement &&
123+
(child as React.ReactElement<any>).props.isCurrentPage !== false
124+
? this.addAriaCurrent(childWithSize)
125+
: childWithSize}
126+
</li>
115127
{index < numChildren - 1 && (
116-
<IconArrowOpenEndSolid color="auto" css={styles?.separator} />
128+
<ChevronRightInstUIIcon size={iconSize} css={styles?.separator} />
117129
)}
118-
</li>
130+
</>
119131
)
120132
})
121133
}

packages/ui-breadcrumb/src/Breadcrumb/props.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import type {
2626
WithStyleProps,
2727
ComponentStyle
2828
} from '@instructure/emotion'
29-
import type { BreadcrumbTheme } from '@instructure/shared-types'
29+
import type { NewComponentTypes } from '@instructure/ui-themes'
3030

3131
type BreadcrumbOwnProps = {
3232
/**
@@ -54,9 +54,10 @@ type PropKeys = keyof BreadcrumbOwnProps
5454
type AllowedPropKeys = Readonly<Array<PropKeys>>
5555

5656
type BreadcrumbProps = BreadcrumbOwnProps &
57-
WithStyleProps<BreadcrumbTheme, BreadcrumbStyle>
57+
WithStyleProps<NewComponentTypes['Breadcrumb'], BreadcrumbStyle>
5858

5959
type BreadcrumbStyle = ComponentStyle<'breadcrumb' | 'crumb' | 'separator'>
60+
6061
const allowedProps: AllowedPropKeys = ['children', 'label', 'margin', 'size']
6162

6263
export type { BreadcrumbProps, BreadcrumbStyle }

packages/ui-breadcrumb/src/Breadcrumb/styles.ts

Lines changed: 26 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* SOFTWARE.
2323
*/
2424

25-
import type { BreadcrumbTheme } from '@instructure/shared-types'
25+
import type { NewComponentTypes, SharedTokens } from '@instructure/ui-themes'
2626
import type { BreadcrumbProps, BreadcrumbStyle } from './props'
2727

2828
/**
@@ -32,83 +32,57 @@ import type { BreadcrumbProps, BreadcrumbStyle } from './props'
3232
* Generates the style object from the theme and provided additional information
3333
* @param {Object} componentTheme The theme variable object.
3434
* @param {Object} props the props of the component, the style is applied to
35-
* @param {Object} state the state of the component, the style is applied to
35+
* @param {Object} sharedTokens Shared design tokens
3636
* @return {Object} The final style object, which will be used in the component
3737
*/
3838
const generateStyle = (
39-
componentTheme: BreadcrumbTheme,
40-
props: BreadcrumbProps
39+
componentTheme: NewComponentTypes['Breadcrumb'],
40+
props: BreadcrumbProps,
41+
_sharedTokens: SharedTokens
4142
): BreadcrumbStyle => {
4243
const { size } = props
4344

44-
const crumbSizeVariants = {
45-
small: {
46-
fontSize: componentTheme.smallFontSize,
47-
paddingInlineEnd: `calc(${componentTheme.smallSeparatorFontSize} * 2)`,
48-
paddingInlineStart: 0
49-
},
50-
medium: {
51-
fontSize: componentTheme.mediumFontSize,
52-
paddingInlineEnd: `calc(${componentTheme.mediumSeparatorFontSize} * 2)`,
53-
paddingInlineStart: 0
54-
},
55-
large: {
56-
fontSize: componentTheme.largeFontSize,
57-
paddingInlineEnd: `calc(${componentTheme.largeSeparatorFontSize} * 2)`,
58-
paddingInlineStart: 0
59-
}
60-
}
61-
62-
const separatorSizeVariants = {
63-
small: {
64-
fontSize: componentTheme.smallSeparatorFontSize,
65-
insetInlineEnd: `calc(${componentTheme.smallSeparatorFontSize} / 2)`,
66-
insetInlineStart: 'auto',
67-
marginTop: `calc(-1 * (${componentTheme.smallSeparatorFontSize} / 2))`
68-
},
69-
medium: {
70-
fontSize: componentTheme.mediumSeparatorFontSize,
71-
insetInlineEnd: `calc(${componentTheme.mediumSeparatorFontSize} / 2)`,
72-
insetInlineStart: 'auto',
73-
marginTop: `calc(-1 * (${componentTheme.mediumSeparatorFontSize} / 2))`
74-
},
75-
large: {
76-
fontSize: componentTheme.largeSeparatorFontSize,
77-
insetInlineEnd: `calc(${componentTheme.largeSeparatorFontSize} / 2)`,
78-
insetInlineStart: 'auto',
79-
marginTop: `calc(-1 * (${componentTheme.largeSeparatorFontSize} / 2))`
80-
}
45+
const gapSizeVariants = {
46+
small: componentTheme.gapSm,
47+
medium: componentTheme.gapMd,
48+
large: componentTheme.gapLg
8149
}
8250

8351
return {
8452
breadcrumb: {
8553
label: 'breadcrumb',
86-
fontFamily: componentTheme.fontFamily,
8754
margin: 0,
8855
padding: 0,
8956
listStyleType: 'none',
9057
overflow: 'visible',
9158
display: 'flex',
92-
alignItems: 'center'
59+
alignItems: 'center',
60+
gap: gapSizeVariants[size!]
9361
},
9462
crumb: {
9563
label: 'breadcrumb__crumb',
9664
boxSizing: 'border-box',
97-
position: 'relative',
98-
...crumbSizeVariants[size!],
65+
display: 'flex',
66+
alignItems: 'center',
67+
overflow: 'visible',
68+
69+
// prevent text clipping
70+
'[data-cid~="TruncateText"]': {
71+
overflow: 'visible',
9972

100-
'&:last-child': {
101-
paddingInlineEnd: 0
73+
// prevent extra spacing after the '...'
74+
'& [class*="truncateText__spacer"]': {
75+
display: 'inline'
76+
}
10277
}
10378
},
10479
separator: {
10580
label: 'breadcrumb__separator',
106-
10781
boxSizing: 'border-box',
108-
position: 'absolute',
109-
top: '50%',
110-
color: componentTheme.separatorColor,
111-
...separatorSizeVariants[size!]
82+
color: '#576773',
83+
display: 'inline-flex',
84+
alignItems: 'center',
85+
flexShrink: 0
11286
}
11387
}
11488
}

packages/ui-breadcrumb/src/Breadcrumb/theme.ts

Lines changed: 0 additions & 55 deletions
This file was deleted.

packages/ui-breadcrumb/tsconfig.build.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
{ "path": "../ui-themes/tsconfig.build.json" },
1212
{ "path": "../emotion/tsconfig.build.json" },
1313
{ "path": "../shared-types/tsconfig.build.json" },
14-
{ "path": "../ui-icons/tsconfig.build.json" },
14+
{ "path": "../ui-icons-lucide/tsconfig.build.json" },
1515
{ "path": "../ui-link/tsconfig.build.json" },
1616
{ "path": "../ui-react-utils/tsconfig.build.json" },
1717
{ "path": "../ui-truncate-text/tsconfig.build.json" },

pnpm-lock.yaml

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)