@@ -29,6 +29,7 @@ import { View } from '@instructure/ui-view'
2929import { Tabs } from '@instructure/ui-tabs'
3030import type { TabsProps } from '@instructure/ui-tabs'
3131import type { NewBaseTheme } from '@instructure/ui-themes'
32+ import type { ComponentTheme as ComponentThemeData } from '@instructure/shared-types'
3233import { SourceCodeEditor } from '@instructure/ui-source-code-editor'
3334import { withStyleForDocs as withStyleNew } from '../withStyleForDocs'
3435
@@ -80,47 +81,48 @@ class Document extends Component<DocumentProps, DocumentState> {
8081
8182 fetchGenerateComponentTheme = async ( ) => {
8283 const { doc, themeVariables } = this . props
83- let generateTheme
84- // Doc IDs use dot notation (e.g. "Menu.Item") but theme component keys
85- // use PascalCase without dots (e.g. "MenuItem").
86- // New-theme entries are in themeVariables.newTheme.components.
8784 const selectedId = this . state . selectedDetailsTabId
8885 type ComponentKey = keyof NewBaseTheme [ 'components' ]
86+ // When a child tab is selected, work from the child's doc/component;
87+ // otherwise from the main doc's.
8988 const childDoc =
9089 selectedId !== doc . id
9190 ? doc ?. children ?. find ( ( value ) => value . id === selectedId )
9291 : null
93- // 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)
92+ const currentComponentInstance =
93+ selectedId === doc . id
94+ ? doc ?. componentInstance
95+ : childDoc ?. componentInstance
96+ // Resolve the theme registry key. Default: doc id with dots stripped
97+ // (e.g. "Menu.Item" → "MenuItem"). Two ways to override the default:
98+ // - `themeId:` in YAML frontmatter (read from childDoc.themeId) — used
99+ // when a doc page wants to show another component's tokens
100+ // (e.g. Drilldown.Group → 'Options').
101+ // - `static themeId` on the component class — used when a component
102+ // borrows another's tokens via `useTokensFrom` at runtime
103+ // (e.g. Button → 'BaseButton', ColorMixer.Slider → 'Slider').
94104 const themeKey : ComponentKey = ( childDoc ?. themeId ||
105+ currentComponentInstance ?. themeId ||
95106 selectedId ?. replace ( / \. / g, '' ) ) as ComponentKey
96107 const isLegacyTheme = this . context ?. componentVersion == 'v11_6'
97- // new theme
108+ // New theme: tokens are pre-resolved into plain objects at build time
109+ // (the build emits JSON, so generator functions can't be carried through).
98110 if ( ! isLegacyTheme ) {
99- // resolvedComponents contains pre-computed plain objects (built at build time)
100111 const resolvedComponents = ( themeVariables as Record < string , unknown > )
101112 ?. resolvedComponents as Record < string , unknown > | undefined
102113 const newThemeEntry = resolvedComponents ?. [ themeKey as string ]
103- const componentInstance =
104- selectedId === doc . id
105- ? doc ?. componentInstance
106- : childDoc ?. componentInstance
107114 if (
108115 newThemeEntry &&
109- typeof componentInstance ?. generateComponentTheme !== 'function'
116+ typeof currentComponentInstance ?. generateComponentTheme !== 'function'
110117 ) {
111- // new theme - use pre-computed theme object directly
112118 this . setState ( {
113119 componentTheme : newThemeEntry as DocumentState [ 'componentTheme' ]
114120 } )
115121 return
116122 }
117123 }
118- // old theme - use generateComponentTheme function
119- if ( selectedId === doc . id ) {
120- generateTheme = doc ?. componentInstance ?. generateComponentTheme
121- } else {
122- generateTheme = childDoc ?. componentInstance ?. generateComponentTheme
123- }
124+ // Legacy theme: call the component's generateComponentTheme directly.
125+ const generateTheme = currentComponentInstance ?. generateComponentTheme
124126 if ( typeof generateTheme === 'function' && themeVariables ) {
125127 this . setState ( { componentTheme : generateTheme ( themeVariables ) } )
126128 } else {
@@ -158,6 +160,19 @@ class Document extends Component<DocumentProps, DocumentState> {
158160
159161 const themeVariableKeys = componentTheme && Object . keys ( componentTheme )
160162
163+ // The displayed token list comes from another component's registry when
164+ // `themeId` (YAML frontmatter) or the component class's `static themeId`
165+ // points at a different key than the doc's own id. In that case some of
166+ // the listed tokens may not actually be used by this component.
167+ const isLegacyTheme = this . context ?. componentVersion === 'v11_6'
168+ const dotStrippedId = doc . id ?. replace ( / \. / g, '' )
169+ const borrowedThemeId =
170+ doc . themeId || doc . componentInstance ?. themeId
171+ const borrowsTokens =
172+ ! isLegacyTheme &&
173+ borrowedThemeId &&
174+ borrowedThemeId !== dotStrippedId
175+
161176 return themeVariables &&
162177 componentTheme &&
163178 themeVariableKeys &&
@@ -166,6 +181,14 @@ class Document extends Component<DocumentProps, DocumentState> {
166181 < Heading level = "h2" as = "h3" id = { `${ doc . id } Theme` } margin = "0 0 small 0" >
167182 Default Theme Variables
168183 </ Heading >
184+ { borrowsTokens ? (
185+ < View as = "div" margin = "0 0 small 0" >
186+ Note: < code > { doc . id } </ code > shares its theme tokens with{ ' ' }
187+ < code > { borrowedThemeId } </ code > , so the table below lists every
188+ token of < code > { borrowedThemeId } </ code > . Some of these may not
189+ actually be used by < code > { doc . id } </ code > .
190+ </ View >
191+ ) : null }
169192 { doc . themePath ? (
170193 < View as = "div" margin = "0 0 x-small 0" >
171194 See which global theme variables are mapped to the component here:{ ' ' }
@@ -181,9 +204,7 @@ class Document extends Component<DocumentProps, DocumentState> {
181204 </ code >
182205 </ View >
183206 ) : null }
184- < ComponentTheme
185- componentTheme = { componentTheme as any /* TODO-theme-types check */ }
186- />
207+ < ComponentTheme componentTheme = { componentTheme as ComponentThemeData } />
187208
188209 < View margin = "x-large 0 0" display = "block" >
189210 < Heading
0 commit comments