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
7 changes: 5 additions & 2 deletions packages/__docs__/buildScripts/DataTypes.mts
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,14 @@ type ResolvedColors = {
semantic: Record<string, string>
}

// At runtime, build-docs.mts also attaches `resolvedComponents` (and on
// canvas / canvas-high-contrast: `key`, `description`) to the new-system
// entries. Not declared per-branch; surfaced as optional on `MainDocsData.themes`.
type ThemeResource =
| (BaseTheme & { resolvedComponents: Record<string, any> }) // legacy-canvas, legacy-canvas-high-contrast
| (NewBaseTheme & { resolvedColors: ResolvedColors }) // canvas, canvas-high-contrast
| (LightTheme & { resolvedColors: ResolvedColors })
| (DarkTheme & { resolvedColors: ResolvedColors })
| (LightTheme & { resolvedColors: ResolvedColors }) // light
| (DarkTheme & { resolvedColors: ResolvedColors }) // dark
| SharedTokens

type MainDocsData = {
Expand Down
35 changes: 31 additions & 4 deletions packages/__docs__/buildScripts/build-docs.mts
Original file line number Diff line number Diff line change
Expand Up @@ -420,17 +420,44 @@ function parseThemes() {
parsed['legacy-canvas-high-contrast'] = {
resource: { ...canvasHighContrast, resolvedComponents: resolveComponents(legacyCanvasHighContrast) }
}
// `key` is read by Document.tsx's `componentDidUpdate` to detect theme
// changes and refetch the Default Theme Variables. `legacyCanvas` /
// `legacyCanvasHighContrast` from `newThemeTokens` do not include a `key`
// field (unlike `light` / `dark`, which come through wrappers that set it).
// Without it, switching e.g. canvas (legacy) → canvas-high-contrast (legacy)
// on v11_7 leaves `themeVariables.key` `undefined` on both sides, so
// `undefined !== undefined` is false and the refetch never fires.
parsed['canvas'] = {
resource: { ...legacyCanvas, resolvedColors: resolveNewThemeColors(legacyCanvas), description: canvas.description }
resource: {
...legacyCanvas,
key: 'canvas',
resolvedColors: resolveNewThemeColors(legacyCanvas),
resolvedComponents: resolveComponents(legacyCanvas),
description: canvas.description
}
}
parsed['canvas-high-contrast'] = {
resource: { ...legacyCanvasHighContrast, resolvedColors: resolveNewThemeColors(legacyCanvasHighContrast), description: canvasHighContrast.description }
resource: {
...legacyCanvasHighContrast,
key: 'canvas-high-contrast',
resolvedColors: resolveNewThemeColors(legacyCanvasHighContrast),
resolvedComponents: resolveComponents(legacyCanvasHighContrast),
description: canvasHighContrast.description
}
}
parsed[light.key] = {
resource: { ...light, resolvedColors: resolveNewThemeColors(light.newTheme as typeof legacyCanvas) }
resource: {
...light,
resolvedColors: resolveNewThemeColors(light.newTheme as typeof legacyCanvas),
resolvedComponents: resolveComponents(light.newTheme as typeof legacyCanvas)
}
}
parsed[dark.key] = {
resource: { ...dark, resolvedColors: resolveNewThemeColors(dark.newTheme as typeof legacyCanvas) }
resource: {
...dark,
resolvedColors: resolveNewThemeColors(dark.newTheme as typeof legacyCanvas),
resolvedComponents: resolveComponents(dark.newTheme as typeof legacyCanvas)
}
}
const canvasSemantics = legacyCanvas.semantics(legacyCanvas.primitives)
parsed['shared-tokens'] = { resource: legacyCanvas.sharedTokens(canvasSemantics) }
Expand Down
13 changes: 12 additions & 1 deletion packages/__docs__/src/App/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,13 @@ class App extends Component<AppProps, AppState> {
const allThemeKeys = Object.keys(this.state.docsData!.themes)
const showNewThemes = selectedMinorVersion !== 'v11_6'

// The `parsed.themes` map in build-docs.mts contains both:
// - `canvas` / `canvas-high-contrast` → new-system resources (primitives/semantics/sharedTokens/components`)
// - `legacy-canvas` / `legacy-canvas-high-contrast` → legacy wrappers (full theme object)
// v11_6 components' `generateComponentTheme(theme)` reads `theme.colors`,
// `theme.spacing`, etc., so v11_6 MUST be backed by the legacy wrappers.
// We pick `legacy-*` as the actual selected keys, and strip the prefix for
// display so the user still sees "canvas" / "canvas-high-contrast".
const themeKeys = showNewThemes
? allThemeKeys.filter(
(k) =>
Expand All @@ -610,7 +617,7 @@ class App extends Component<AppProps, AppState> {
k !== 'legacy-canvas-high-contrast'
)
: allThemeKeys.filter(
(k) => k === 'canvas' || k === 'canvas-high-contrast'
(k) => k === 'legacy-canvas' || k === 'legacy-canvas-high-contrast'
)

const displayThemeName = (themeKey: string) => {
Expand All @@ -620,6 +627,10 @@ class App extends Component<AppProps, AppState> {
) {
return `${themeKey} (legacy)`
}
// On v11_6 strip the `legacy-` prefix so the user sees the plain names.
if (!showNewThemes) {
return themeKey.replace(/^legacy-/, '')
}
return themeKey
}

Expand Down
65 changes: 43 additions & 22 deletions packages/__docs__/src/Document/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { View } from '@instructure/ui-view'
import { Tabs } from '@instructure/ui-tabs'
import type { TabsProps } from '@instructure/ui-tabs'
import type { NewBaseTheme } from '@instructure/ui-themes'
import type { ComponentTheme as ComponentThemeData } from '@instructure/shared-types'
import { SourceCodeEditor } from '@instructure/ui-source-code-editor'
import { withStyleForDocs as withStyleNew } from '../withStyleForDocs'

Expand Down Expand Up @@ -78,47 +79,48 @@ class Document extends Component<DocumentProps, DocumentState> {

fetchGenerateComponentTheme = async () => {
const { doc, themeVariables } = this.props
let generateTheme
// Doc IDs use dot notation (e.g. "Menu.Item") but theme component keys
// use PascalCase without dots (e.g. "MenuItem").
// New-theme entries are in themeVariables.newTheme.components.
const selectedId = this.state.selectedDetailsTabId
type ComponentKey = keyof NewBaseTheme['components']
// When a child tab is selected, work from the child's doc/component;
// otherwise from the main doc's.
const childDoc =
selectedId !== doc.id
? doc?.children?.find((value) => value.id === selectedId)
: null
// in case of some components, we need to display the theme variables of other components based on themeId (like displaying the theme variables of Options in Drillsdown.Group)
const currentComponentInstance =
selectedId === doc.id
? doc?.componentInstance
: childDoc?.componentInstance
// Resolve the theme registry key. Default: doc id with dots stripped
// (e.g. "Menu.Item" → "MenuItem"). Two ways to override the default:
// - `themeId:` in YAML frontmatter (read from childDoc.themeId) — used
// when a doc page wants to show another component's tokens
// (e.g. Drilldown.Group → 'Options').
// - `static themeId` on the component class — used when a component
// borrows another's tokens via `useTokensFrom` at runtime
// (e.g. Button → 'BaseButton', ColorMixer.Slider → 'Slider').
const themeKey: ComponentKey = (childDoc?.themeId ||
currentComponentInstance?.themeId ||
selectedId?.replace(/\./g, '')) as ComponentKey
const isLegacyTheme = this.context?.componentVersion == 'v11_6'
// new theme
// New theme: tokens are pre-resolved into plain objects at build time
// (the build emits JSON, so generator functions can't be carried through).
if (!isLegacyTheme) {
// resolvedComponents contains pre-computed plain objects (built at build time)
const resolvedComponents = (themeVariables as Record<string, unknown>)
?.resolvedComponents as Record<string, unknown> | undefined
const newThemeEntry = resolvedComponents?.[themeKey as string]
const componentInstance =
selectedId === doc.id
? doc?.componentInstance
: childDoc?.componentInstance
if (
newThemeEntry &&
typeof componentInstance?.generateComponentTheme !== 'function'
typeof currentComponentInstance?.generateComponentTheme !== 'function'
) {
// new theme - use pre-computed theme object directly
this.setState({
componentTheme: newThemeEntry as DocumentState['componentTheme']
})
return
}
}
// old theme - use generateComponentTheme function
if (selectedId === doc.id) {
generateTheme = doc?.componentInstance?.generateComponentTheme
} else {
generateTheme = childDoc?.componentInstance?.generateComponentTheme
}
// Legacy theme: call the component's generateComponentTheme directly.
const generateTheme = currentComponentInstance?.generateComponentTheme
if (typeof generateTheme === 'function' && themeVariables) {
this.setState({ componentTheme: generateTheme(themeVariables) })
} else {
Expand Down Expand Up @@ -156,6 +158,19 @@ class Document extends Component<DocumentProps, DocumentState> {

const themeVariableKeys = componentTheme && Object.keys(componentTheme)

// The displayed token list comes from another component's registry when
// `themeId` (YAML frontmatter) or the component class's `static themeId`
// points at a different key than the doc's own id. In that case some of
// the listed tokens may not actually be used by this component.
const isLegacyTheme = this.context?.componentVersion === 'v11_6'
const dotStrippedId = doc.id?.replace(/\./g, '')
const borrowedThemeId =
doc.themeId || doc.componentInstance?.themeId
const borrowsTokens =
!isLegacyTheme &&
borrowedThemeId &&
borrowedThemeId !== dotStrippedId

return themeVariables &&
componentTheme &&
themeVariableKeys &&
Expand All @@ -164,6 +179,14 @@ class Document extends Component<DocumentProps, DocumentState> {
<Heading level="h2" as="h3" id={`${doc.id}Theme`} margin="0 0 small 0">
Default Theme Variables
</Heading>
{borrowsTokens ? (
<View as="div" margin="0 0 small 0">
Note: <code>{doc.id}</code> shares its theme tokens with{' '}
<code>{borrowedThemeId}</code>, so the table below lists every
token of <code>{borrowedThemeId}</code>. Some of these may not
actually be used by <code>{doc.id}</code>.
</View>
) : null}
{doc.themePath ? (
<View as="div" margin="0 0 x-small 0">
See which global theme variables are mapped to the component here:{' '}
Expand All @@ -179,9 +202,7 @@ class Document extends Component<DocumentProps, DocumentState> {
</code>
</View>
) : null}
<ComponentTheme
componentTheme={componentTheme as any /* TODO-theme-types check */}
/>
<ComponentTheme componentTheme={componentTheme as ComponentThemeData} />

<View margin="x-large 0 0" display="block">
<Heading
Expand Down
6 changes: 5 additions & 1 deletion packages/__docs__/src/Document/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,16 @@ type DocumentProps = DocumentOwnProps & WithStyleProps<null, DocumentStyle>

type DocumentStyle = ComponentStyle<'githubCornerOctoArm' | 'githubCorner'>

type ResolvedNewComponentTheme = ReturnType<
NewBaseTheme['components'][keyof NewBaseTheme['components']]
>

type DocumentState = {
selectedDetailsTabId: string | undefined
pageRef: HTMLDivElement | null
componentTheme:
| ThemeVariables[keyof ThemeVariables]
| NewBaseTheme['components'][keyof NewBaseTheme['components']]
| ResolvedNewComponentTheme
| undefined
}

Expand Down
2 changes: 2 additions & 0 deletions packages/ui-buttons/src/Button/v2/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ category: components
@withStyleNew(null, 'BaseButton')
class Button extends Component<ButtonProps> {
static readonly componentId = 'Button'
// Button v2 uses BaseButton's tokens; tell Document where to look for theme variables
static readonly themeId = 'BaseButton'

static allowedProps = allowedProps
static defaultProps = {
Expand Down
2 changes: 2 additions & 0 deletions packages/ui-buttons/src/CloseButton/v2/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ category: components
@withStyleNew(generateStyle, 'BaseButton')
class CloseButton extends Component<CloseButtonProps> {
static readonly componentId = 'CloseButton'
// Uses BaseButton's tokens; tell Document where to look for theme variables
static readonly themeId = 'BaseButton'

static allowedProps = allowedProps
static defaultProps = {
Expand Down
2 changes: 2 additions & 0 deletions packages/ui-buttons/src/CondensedButton/v2/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ category: components
@withStyleNew(null, 'BaseButton')
class CondensedButton extends Component<CondensedButtonProps> {
static readonly componentId = 'CondensedButton'
// Uses BaseButton's tokens; tell Document where to look for theme variables
static readonly themeId = 'BaseButton'

static allowedProps = allowedProps
static defaultProps = {
Expand Down
2 changes: 2 additions & 0 deletions packages/ui-buttons/src/IconButton/v2/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ category: components
@withStyleNew(null, 'BaseButton')
class IconButton extends Component<IconButtonProps> {
static readonly componentId = 'IconButton'
// Uses BaseButton's tokens; tell Document where to look for theme variables
static readonly themeId = 'BaseButton'

static allowedProps = allowedProps
static defaultProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ parent: Checkbox
@withStyleNew(generateStyle, 'Checkbox')
class CheckboxFacade extends Component<CheckboxFacadeProps> {
static readonly componentId = 'CheckboxFacade'
static readonly themeId = 'Checkbox'

static allowedProps = allowedProps
static defaultProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ parent: Checkbox
@withStyleNew(generateStyle, 'Toggle')
class ToggleFacade extends Component<ToggleFacadeProps> {
static readonly componentId = 'ToggleFacade'
static readonly themeId = 'Toggle'

static allowedProps = allowedProps
static defaultProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ private: true
class ColorPalette extends Component<ColorPaletteProps, ColorPaletteState> {
static allowedProps = allowedProps
static readonly componentId = 'ColorMixer.Palette'
static readonly themeId = 'Palette'

constructor(props: ColorPaletteProps) {
super(props)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ private: true
class RGBAInput extends Component<RGBAInputProps, RGBAInputState> {
static allowedProps = allowedProps
static readonly componentId = 'ColorMixer.RGBAInput'
static readonly themeId = 'RgbaInput'

static defaultProps = {
withAlpha: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ private: true
class Slider extends Component<SliderProps> {
static allowedProps = allowedProps
static readonly componentId = 'ColorMixer.Slider'
static readonly themeId = 'Slider'

static defaultProps = {
isColorSlider: false
Expand Down
1 change: 1 addition & 0 deletions packages/ui-modal/src/Modal/v2/ModalBody/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ id: Modal.Body
@withStyleNew(generateStyle, 'ModalBody')
class ModalBody extends Component<ModalBodyProps> {
static readonly componentId = 'Modal.Body'
static readonly themeId = 'ModalBody'

static allowedProps = allowedProps
static defaultProps = {
Expand Down
1 change: 1 addition & 0 deletions packages/ui-modal/src/Modal/v2/ModalFooter/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ id: Modal.Footer
@withStyleNew(generateStyle, 'ModalFooter')
class ModalFooter extends Component<ModalFooterProps> {
static readonly componentId = 'Modal.Footer'
static readonly themeId = 'ModalFooter'

static allowedProps = allowedProps
static defaultProps = {
Expand Down
1 change: 1 addition & 0 deletions packages/ui-modal/src/Modal/v2/ModalHeader/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ id: Modal.Header
@withStyleNew(generateStyle, 'ModalHeader')
class ModalHeader extends Component<ModalHeaderProps> {
static readonly componentId = 'Modal.Header'
static readonly themeId = 'ModalHeader'

static allowedProps = allowedProps
static defaultProps = {
Expand Down
2 changes: 2 additions & 0 deletions packages/ui-number-input/src/NumberInput/v2/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,8 @@ const NumberInput = forwardRef<NumberInputHandle, NumberInputProps>(
)

NumberInput.displayName = 'NumberInput'
// Uses TextInput's tokens; tell Document where to look for theme variables
;(NumberInput as typeof NumberInput & { themeId: string }).themeId = 'TextInput'

export interface NumberInputHandle {
focus: () => void
Expand Down
1 change: 1 addition & 0 deletions packages/ui-pagination/src/Pagination/v1/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ class Pagination extends Component<PaginationProps> {

static Page = PaginationButton
static Navigation = PaginationArrowButton
static PageInput = PaginationPageInput

private readonly _labelId: string

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class PaginationPageInput extends Component<
PaginationPageInputState
> {
static readonly componentId = 'Pagination.PageInput'
static readonly themeId = 'PaginationPageInput'

static allowedProps = allowedProps
static defaultProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ id: SideNavBar.Item
@withStyleNew(generateStyle, 'SideNavBarItem')
class SideNavBarItem extends Component<SideNavBarItemProps> {
static readonly componentId = 'SideNavBar.Item'
static readonly themeId = 'SideNavBarItem'

static allowedProps = allowedProps

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class TopNavBarActionItems extends Component<
TopNavBarActionItemsState
> {
static readonly componentId = 'TopNavBar.ActionItems'
static readonly themeId = 'TopNavBarActionItems'

static allowedProps = allowedProps
static defaultProps = {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ id: TopNavBar.Brand
@withStyleNew(generateStyle, 'TopNavBarBrand')
class TopNavBarBrand extends Component<TopNavBarBrandProps> {
static readonly componentId = 'TopNavBar.Brand'
static readonly themeId = 'TopNavBarBrand'
// TODO: add to the docs: making it static on parent and jsdocs parent/module settings, dont export child on its own

static allowedProps = allowedProps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ id: TopNavBar.Item
@withStyleNew(generateStyle, 'TopNavBarItem')
class TopNavBarItem extends Component<TopNavBarItemProps, TopNavBarItemState> {
static readonly componentId = 'TopNavBar.Item'
static readonly themeId = 'TopNavBarItem'

static allowedProps = allowedProps
static defaultProps = {
Expand Down
Loading
Loading