Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/guides/upgrade-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ The following variant values have been **deprecated** and will be removed in a f

### Theme variable changes

- theme variable `unstyledTextColor` is newly added for non-interactive Links
- theme variable `fontSize` is now removed
- theme variable `fontSizeSmall` is now removed
- theme variable `lineHeight` is now removed
Expand Down Expand Up @@ -104,6 +105,24 @@ type: example
<TextInput renderLabel="Name" placeholder="Doe, John Doe"/>
</InstUISettingsProvider>
```
### Breadcrumb

#### New tokens

- gapSm - Gap spacing for small size breadcrumbs
- gapMd - Gap spacing for medium size breadcrumbs
- gapLg - Gap spacing for large size breadcrumbs

#### Theme variable changes

- theme variable `fontFamily` is now removed (handled in link component)
- theme variable `separatorColor` is now removed (handled in link component)
- theme variable `smallSeparatorFontSize` is now removed (handled in link component)
- theme variable `smallFontSize` is now removed (handled in link component)
- theme variable `mediumSeparatorFontSize` is now removed (handled in link component)
- theme variable `mediumFontSize` is now removed (handled in link component)
- theme variable `largeSeparatorFontSize` is now removed (handled in link component)
- theme variable `largeFontSize` is now removed (handled in link component)

### RadioInput

Expand Down
2 changes: 1 addition & 1 deletion packages/ui-breadcrumb/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"@babel/runtime": "^7.27.6",
"@instructure/emotion": "workspace:*",
"@instructure/shared-types": "workspace:*",
"@instructure/ui-icons": "workspace:*",
"@instructure/ui-icons-lucide": "workspace:*",
"@instructure/ui-link": "workspace:*",
"@instructure/ui-react-utils": "workspace:*",
"@instructure/ui-tooltip": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ class BreadcrumbLink extends Component<
iconPlacement,
onClick,
onMouseEnter,
isCurrentPage
isCurrentPage,
size
} = this.props

const { isTruncated } = this.state
const props = omitProps(this.props, BreadcrumbLink.allowedProps)

Expand All @@ -99,6 +99,7 @@ class BreadcrumbLink extends Component<
isWithinText={false}
elementRef={this.handleRef}
forceButtonRole={false}
size={size}
{...(isCurrentPage && { 'aria-current': 'page' })}
{...(isTruncated && {
...(typeof children === 'string' && { 'aria-label': children }),
Expand Down
6 changes: 3 additions & 3 deletions packages/ui-breadcrumb/src/Breadcrumb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@ You can include icons in `Breadcrumb.Link`:
type: example
---
<Breadcrumb label="Breadcrumb with icons">
<Breadcrumb.Link renderIcon={<IconBankLine size="small" />} href="#Breadcrumb">Item Bank</Breadcrumb.Link>
<Breadcrumb.Link renderIcon={<IconClockLine size="small" />} onClick={() => {}}>History</Breadcrumb.Link>
<Breadcrumb.Link renderIcon={IconPlusLine} iconPlacement="end">New Question</Breadcrumb.Link>
<Breadcrumb.Link renderIcon={<FolderOpenInstUIIcon />} href="#Breadcrumb">Item Bank</Breadcrumb.Link>
<Breadcrumb.Link renderIcon={<Clock4InstUIIcon />} onClick={() => {}}>History</Breadcrumb.Link>
<Breadcrumb.Link renderIcon={<PlusInstUIIcon />} iconPlacement="end">New Question</Breadcrumb.Link>
</Breadcrumb>
```

Expand Down
56 changes: 35 additions & 21 deletions packages/ui-breadcrumb/src/Breadcrumb/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,16 @@
* SOFTWARE.
*/

import {
isValidElement,
cloneElement,
Children,
Component,
ReactElement
} from 'react'
import { isValidElement, Children, Component, ReactElement } from 'react'

import { View } from '@instructure/ui-view'
import { safeCloneElement } from '@instructure/ui-react-utils'

import { withStyleRework as withStyle } from '@instructure/emotion'
import { IconArrowOpenEndSolid } from '@instructure/ui-icons'
import { withStyle } from '@instructure/emotion'
import { ChevronRightInstUIIcon } from '@instructure/ui-icons-lucide'
import { BreadcrumbLink } from './BreadcrumbLink'

import generateStyle from './styles'
import generateComponentTheme from './theme'

import { allowedProps } from './props'
import type { BreadcrumbProps } from './props'
Expand All @@ -48,7 +42,7 @@ category: components
---
**/

@withStyle(generateStyle, generateComponentTheme)
@withStyle(generateStyle)
class Breadcrumb extends Component<BreadcrumbProps> {
static readonly componentId = 'Breadcrumb'

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

addAriaCurrent = (child: React.ReactNode) => {
const updatedChild = cloneElement(
const updatedChild = safeCloneElement(
child as React.ReactElement<{ 'aria-current'?: string }>,
{
'aria-current': 'page'
Expand All @@ -77,14 +71,15 @@ class Breadcrumb extends Component<BreadcrumbProps> {
componentDidMount() {
this.props.makeStyles?.()
}

componentDidUpdate() {
this.props.makeStyles?.()
}

static Link = BreadcrumbLink

renderChildren() {
const { styles, children } = this.props
const { styles, children, size } = this.props
const numChildren = Children.count(children)
const inlineStyle = {
maxWidth: `${Math.floor(100 / numChildren)}%`
Expand All @@ -105,17 +100,36 @@ class Breadcrumb extends Component<BreadcrumbProps> {
isAriaCurrentSet = true
}
}

// Clone child (BreadcrumbLink) and pass size prop
const childWithSize = isValidElement(child)
? safeCloneElement(child as ReactElement<any>, { size })
: child

// Map Breadcrumb sizes to icon size tokens
const breadcrumbSizeToIconSize = {
small: 'sm',
medium: 'md',
large: 'lg'
} as const

const iconSize = breadcrumbSizeToIconSize[size || 'small']

return (
<li css={styles?.crumb} style={inlineStyle}>
{!isAriaCurrentSet &&
isLastElement &&
(child as React.ReactElement<any>).props.isCurrentPage !== false
? this.addAriaCurrent(child)
: child}
<>
<li css={styles?.crumb} style={inlineStyle}>
{!isAriaCurrentSet &&
isLastElement &&
(child as React.ReactElement<any>).props.isCurrentPage !== false
? this.addAriaCurrent(childWithSize)
: childWithSize}
</li>
{index < numChildren - 1 && (
<IconArrowOpenEndSolid color="auto" css={styles?.separator} />
<li aria-hidden="true" css={styles?.separator}>
<ChevronRightInstUIIcon size={iconSize} color={'mutedColor'} />
</li>
)}
</li>
</>
)
})
}
Expand Down
5 changes: 3 additions & 2 deletions packages/ui-breadcrumb/src/Breadcrumb/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import type {
WithStyleProps,
ComponentStyle
} from '@instructure/emotion'
import type { BreadcrumbTheme } from '@instructure/shared-types'
import type { NewComponentTypes } from '@instructure/ui-themes'

type BreadcrumbOwnProps = {
/**
Expand Down Expand Up @@ -54,9 +54,10 @@ type PropKeys = keyof BreadcrumbOwnProps
type AllowedPropKeys = Readonly<Array<PropKeys>>

type BreadcrumbProps = BreadcrumbOwnProps &
WithStyleProps<BreadcrumbTheme, BreadcrumbStyle>
WithStyleProps<NewComponentTypes['Breadcrumb'], BreadcrumbStyle>

type BreadcrumbStyle = ComponentStyle<'breadcrumb' | 'crumb' | 'separator'>

const allowedProps: AllowedPropKeys = ['children', 'label', 'margin', 'size']

export type { BreadcrumbProps, BreadcrumbStyle }
Expand Down
74 changes: 22 additions & 52 deletions packages/ui-breadcrumb/src/Breadcrumb/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
* SOFTWARE.
*/

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

/**
Expand All @@ -32,83 +32,53 @@ import type { BreadcrumbProps, BreadcrumbStyle } from './props'
* Generates the style object from the theme and provided additional information
* @param {Object} componentTheme The theme variable object.
* @param {Object} props the props of the component, the style is applied to
* @param {Object} state the state of the component, the style is applied to
* @return {Object} The final style object, which will be used in the component
*/
const generateStyle = (
componentTheme: BreadcrumbTheme,
props: BreadcrumbProps
componentTheme: NewComponentTypes['Breadcrumb'],
props: BreadcrumbProps,
): BreadcrumbStyle => {
const { size } = props

const crumbSizeVariants = {
small: {
fontSize: componentTheme.smallFontSize,
paddingInlineEnd: `calc(${componentTheme.smallSeparatorFontSize} * 2)`,
paddingInlineStart: 0
},
medium: {
fontSize: componentTheme.mediumFontSize,
paddingInlineEnd: `calc(${componentTheme.mediumSeparatorFontSize} * 2)`,
paddingInlineStart: 0
},
large: {
fontSize: componentTheme.largeFontSize,
paddingInlineEnd: `calc(${componentTheme.largeSeparatorFontSize} * 2)`,
paddingInlineStart: 0
}
}

const separatorSizeVariants = {
small: {
fontSize: componentTheme.smallSeparatorFontSize,
insetInlineEnd: `calc(${componentTheme.smallSeparatorFontSize} / 2)`,
insetInlineStart: 'auto',
marginTop: `calc(-1 * (${componentTheme.smallSeparatorFontSize} / 2))`
},
medium: {
fontSize: componentTheme.mediumSeparatorFontSize,
insetInlineEnd: `calc(${componentTheme.mediumSeparatorFontSize} / 2)`,
insetInlineStart: 'auto',
marginTop: `calc(-1 * (${componentTheme.mediumSeparatorFontSize} / 2))`
},
large: {
fontSize: componentTheme.largeSeparatorFontSize,
insetInlineEnd: `calc(${componentTheme.largeSeparatorFontSize} / 2)`,
insetInlineStart: 'auto',
marginTop: `calc(-1 * (${componentTheme.largeSeparatorFontSize} / 2))`
}
const gapSizeVariants = {
small: componentTheme.gapSm,
medium: componentTheme.gapMd,
large: componentTheme.gapLg
}

return {
breadcrumb: {
label: 'breadcrumb',
fontFamily: componentTheme.fontFamily,
margin: 0,
padding: 0,
listStyleType: 'none',
overflow: 'visible',
display: 'flex',
alignItems: 'center'
alignItems: 'center',
gap: gapSizeVariants[size!]
},
crumb: {
label: 'breadcrumb__crumb',
boxSizing: 'border-box',
position: 'relative',
...crumbSizeVariants[size!],
display: 'flex',
alignItems: 'center',

'&:last-child': {
paddingInlineEnd: 0
// prevent text clipping
'[data-cid~="TruncateText"]': {
overflow: 'visible',

// prevent extra spacing after the '...'
'& [class*="truncateText__spacer"]': {
display: 'inline'
}
}
},
separator: {
label: 'breadcrumb__separator',

boxSizing: 'border-box',
position: 'absolute',
top: '50%',
color: componentTheme.separatorColor,
...separatorSizeVariants[size!]
display: 'inline-flex',
alignItems: 'center',
flexShrink: 0
}
}
}
Expand Down
55 changes: 0 additions & 55 deletions packages/ui-breadcrumb/src/Breadcrumb/theme.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/ui-breadcrumb/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{ "path": "../ui-themes/tsconfig.build.json" },
{ "path": "../emotion/tsconfig.build.json" },
{ "path": "../shared-types/tsconfig.build.json" },
{ "path": "../ui-icons/tsconfig.build.json" },
{ "path": "../ui-icons-lucide/tsconfig.build.json" },
{ "path": "../ui-link/tsconfig.build.json" },
{ "path": "../ui-react-utils/tsconfig.build.json" },
{ "path": "../ui-truncate-text/tsconfig.build.json" },
Expand Down
1 change: 1 addition & 0 deletions packages/ui-link/src/Link/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ const generateStyle = (
verticalAlign: 'baseline',
fontSize: variantStyles.fontSize,
lineHeight: variantStyles.lineHeight,
color: componentTheme.unstyledTextColor,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The unstyledTextColor token was added to the Link component to provide a theme-aware text color for non-interactive Link elements. This resolves the color issue not only in Link itself, but also for the last items in the Breadcrumb.


// set up focus styles
borderRadius: '0.125rem',
Expand Down
2 changes: 2 additions & 0 deletions packages/ui-truncate-text/src/TruncateText/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ class TruncateText extends Component<TruncateTextProps, TruncateTextState> {
// this spacer element is set to the max width the full text could
// potentially be without this, text in `width: auto` elements won't expand
// to accommodate more text, once truncated
// Breadcrumb is modifying this element's display to inline to prevent layout issues
// TODO: find a better way to handle this
childElements.push(
<span
css={this.props.styles?.spacer}
Expand Down
Loading