Skip to content

Commit 620b421

Browse files
committed
refactor(ui-radio-input,ui-avatar,emotion): split useStyle into old and new theming engine part
also make themeOverride prop always avaiable in the props
1 parent 2c3f841 commit 620b421

File tree

18 files changed

+180
-113
lines changed

18 files changed

+180
-113
lines changed

docs/guides/upgrade-guide.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: Upgrade Guide for Version 11
2+
title: Upgrade Guide for Version 12
33
category: Guides
44
order: 1
55
---
@@ -36,6 +36,11 @@ The new icons automatically sync with theme changes, support all InstUI color to
3636

3737
## API Changes
3838

39+
### RadioInput
40+
41+
- Setting `readonly` does not set the low level `<input>` to disabled, but to `readonly`. This also means that the input is still focusable when `readonly`
42+
- its DOM structure has been significantly simplified
43+
3944
## Codemods
4045

4146
To ease the upgrade, we provide codemods that will automate most of the changes. Pay close attention to its output, it cannot refactor complex code! The codemod scripts can be run via the following commands:

packages/emotion/src/__tests__/getComponentThemeOverride.test.tsx

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,7 @@ describe('@getComponentThemeOverride', () => {
8181
canvas,
8282
componentName,
8383
componentId,
84-
{
85-
themeOverride: componentOverride
86-
}
84+
componentOverride
8785
)
8886

8987
expect(override).toEqual(componentOverride)
@@ -100,12 +98,10 @@ describe('@getComponentThemeOverride', () => {
10098
canvas,
10199
componentName,
102100
componentId,
103-
{
104-
themeOverride: (componentTheme, currentTheme) => ({
105-
backgroundBlue: componentTheme.backgroundGreen,
106-
backgroundDark: currentTheme.colors.contrasts.white1010
107-
})
108-
},
101+
(componentTheme: any, currentTheme: any) => ({
102+
backgroundBlue: componentTheme.backgroundGreen,
103+
backgroundDark: currentTheme.colors.contrasts.white1010
104+
}),
109105
theme
110106
)
111107

@@ -128,9 +124,7 @@ describe('@getComponentThemeOverride', () => {
128124
componentName,
129125
componentId,
130126
{
131-
themeOverride: {
132-
componentColor: 'rgb(0, 150, 0)'
133-
}
127+
componentColor: 'rgb(0, 150, 0)'
134128
}
135129
)
136130

packages/emotion/src/__tests__/useStyle.test.tsx renamed to packages/emotion/src/__tests__/useStyleRework.test.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ import type { MockInstance } from 'vitest'
2828
import { render, screen, waitFor } from '@testing-library/react'
2929
import userEvent from '@testing-library/user-event'
3030
import '@testing-library/jest-dom'
31-
import { InstUISettingsProvider, WithStyleProps, useStyle } from '../index'
31+
import {
32+
InstUISettingsProvider,
33+
WithStyleProps,
34+
useStyleRework
35+
} from '../index'
3236

3337
type Props = {
3438
inverse?: boolean
@@ -102,7 +106,7 @@ describe('useStyle', () => {
102106
const ThemedComponent = ({ inverse = false, themeOverride }: Props) => {
103107
const [clearBackground, setClearBackground] = useState(false)
104108

105-
const styles = useStyle({
109+
const styles = useStyleRework({
106110
generateStyle,
107111
generateComponentTheme,
108112
componentId: 'ThemedComponent',

packages/emotion/src/getComponentThemeOverride.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ import type {
2727
Overrides,
2828
ComponentOverride
2929
} from './EmotionTypes'
30-
import type { BaseTheme, ComponentTheme } from '@instructure/shared-types'
31-
import type { WithStyleProps } from './withStyleRework'
30+
import type { ComponentTheme } from '@instructure/shared-types'
31+
import { ThemeOverrideProp } from './withStyle'
32+
import { ThemeOverrideValue } from './useStyle'
3233

3334
type ComponentName = keyof ComponentOverride | undefined
3435

@@ -42,22 +43,21 @@ type ComponentName = keyof ComponentOverride | undefined
4243
* @param theme - Theme object
4344
* @param displayName - Name of the component
4445
* @param componentId - componentId of the component
45-
* @param props - The component's props object
46+
* @param themeOverride - The theme override object
4647
* @param componentTheme - The component's default theme
4748
* @returns The calculated theme override object
4849
*/
4950
const getComponentThemeOverride = (
5051
theme: ThemeOrOverride,
5152
displayName: string,
5253
componentId?: string,
53-
props?: { [k: string]: unknown } & WithStyleProps,
54+
// ThemeOverrideProp is the old type, ThemeOverrideValue is the new one
55+
themeOverride?: ThemeOverrideProp['themeOverride'] | ThemeOverrideValue,
5456
componentTheme?: ComponentTheme
5557
): Partial<ComponentTheme> => {
5658
const name = displayName as ComponentName
5759
const id = componentId as ComponentName
5860

59-
const themeOverride = props ? props.themeOverride : undefined
60-
6161
const { componentOverrides } = theme as Overrides
6262

6363
let overridesFromTheme: Partial<ComponentTheme> = {}
@@ -71,10 +71,11 @@ const getComponentThemeOverride = (
7171
if (themeOverride) {
7272
if (typeof themeOverride === 'function') {
7373
overrideFromComponent = themeOverride(
74-
componentTheme || {},
74+
//TODO type properly when the old theme is gone
75+
componentTheme || ({} as any),
7576
// the `theme` technically could be a partial theme / override object too,
7677
// but we want to display all possible options
77-
theme as BaseTheme
78+
theme as any
7879
)
7980
} else {
8081
overrideFromComponent = themeOverride

packages/emotion/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@ export {
3939
calcFocusOutlineStyles
4040
} from './styleUtils'
4141

42+
export { useStyleRework } from './useStyleRework'
4243
export { useStyle } from './useStyle'
4344
export { useTheme } from './useTheme'
4445

4546
export type { ComponentStyle, StyleObject, Overrides } from './EmotionTypes'
4647
export type { WithStyleProps } from './withStyleRework'
48+
export type { ThemeOverrideValue } from './useStyle'
4749
export type {
4850
SpacingValues,
4951
Spacing,

packages/emotion/src/useStyle.ts

Lines changed: 32 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -24,110 +24,82 @@
2424

2525
import { useTheme } from './useTheme'
2626
import { getComponentThemeOverride } from './getComponentThemeOverride'
27-
import type { ComponentTheme } from '@instructure/shared-types'
2827
import type {
2928
SharedTokens,
3029
NewComponentTypes,
3130
Theme
3231
} from '@instructure/ui-themes'
33-
import { BaseThemeOrOverride } from './EmotionTypes'
32+
import type { BaseThemeOrOverride } from './EmotionTypes'
3433

3534
// returns the second parameter of a function
3635
type SecondParameter<T extends (...args: any) => any> =
3736
Parameters<T>[1] extends undefined ? never : Parameters<T>[1]
3837

39-
type GenerateComponentTheme = (theme: Theme) => ComponentTheme
38+
type GenerateStyleParams =
39+
| ((componentTheme: any, params: any, sharedTokens: SharedTokens) => any)
40+
| ((componentTheme: any, params: any) => any)
41+
| ((componentTheme: any) => any)
4042

41-
// TODO this is only used by the old themes, remove when everything uses the new
42-
// theming system
43-
type UseStyleParamsWithTheme<
44-
P extends (componentTheme: any, params: any, theme: any) => any
45-
> = {
46-
generateStyle: P
47-
params?: SecondParameter<P>
48-
generateComponentTheme: GenerateComponentTheme
49-
componentId: string
50-
displayName?: string
51-
}
43+
/**
44+
* Type for a theme override
45+
*/
46+
type ThemeOverrideValue =
47+
| Partial<Theme>
48+
| ((
49+
componentTheme: Theme,
50+
currentTheme: NewComponentTypes[keyof NewComponentTypes]
51+
) => Partial<Theme>)
5252

53-
// TODO this is only used by the old themes, remove when everything uses the new
54-
// theming system
55-
type UseStyleParamsWithoutTheme<
56-
P extends (componentTheme: any, params: any, theme: any) => any
57-
> = {
58-
generateStyle: P
59-
params?: SecondParameter<P>
60-
generateComponentTheme?: undefined
61-
componentId?: undefined
62-
displayName?: undefined
53+
const isNewThemeObject = (obj: BaseThemeOrOverride): obj is Theme => {
54+
return typeof (obj as any)?.newTheme === 'object'
6355
}
6456

65-
// new useStyle syntax, use this with new themes
66-
type UseStyleParamsNew<
67-
P extends (
68-
componentTheme: any,
69-
params: any,
70-
sharedTokens: SharedTokens
71-
) => any
72-
> = {
57+
/**
58+
* new useStyle syntax, use this with v12 themes
59+
*/
60+
const useStyle = <P extends GenerateStyleParams>(useStyleParams: {
7361
generateStyle: P
7462
params?: SecondParameter<P>
7563
componentId: keyof NewComponentTypes
64+
themeOverride: ThemeOverrideValue | undefined
7665
displayName?: string
7766
//in case of a child component needed to use it's parent's tokens, provide parent's name
7867
useTokensFrom?: keyof NewComponentTypes
79-
}
80-
81-
const isNewThemeObject = (obj: BaseThemeOrOverride): obj is Theme => {
82-
return typeof (obj as any)?.newTheme === 'object'
83-
}
84-
85-
const useStyle = <
86-
P extends (componentTheme: any, params: any, themeOrSharedTokens: any) => any
87-
>(
88-
useStyleParams:
89-
| UseStyleParamsWithTheme<P>
90-
| UseStyleParamsWithoutTheme<P>
91-
| UseStyleParamsNew<P>
92-
): ReturnType<P> => {
93-
const { generateStyle, params, componentId, displayName } = useStyleParams
94-
const useTokensFrom = (useStyleParams as UseStyleParamsNew<P>).useTokensFrom
95-
const generateComponentTheme: GenerateComponentTheme = (
96-
useStyleParams as UseStyleParamsWithTheme<P>
97-
)?.generateComponentTheme
68+
}): ReturnType<P> => {
69+
const { generateStyle, params, componentId, displayName, themeOverride } =
70+
useStyleParams
71+
const useTokensFrom = useStyleParams.useTokensFrom
9872
const theme = useTheme()
9973

100-
let baseComponentTheme =
101-
typeof generateComponentTheme === 'function'
102-
? generateComponentTheme(theme as Theme)
103-
: {}
74+
let baseComponentTheme = {}
10475
const componentWithTokensId = useTokensFrom ?? componentId
10576

10677
if (
107-
isNewThemeObject(theme) &&
78+
isNewThemeObject(theme) && // TODO: is it possible not to have a theme object here?
10879
theme.newTheme.components[componentWithTokensId as keyof NewComponentTypes]
10980
) {
11081
baseComponentTheme =
11182
theme.newTheme.components[
11283
componentWithTokensId as keyof NewComponentTypes
11384
]
11485
}
115-
const themeOverride = getComponentThemeOverride(
86+
const finalOverride = getComponentThemeOverride(
11687
theme,
11788
useTokensFrom ?? displayName ?? componentId ?? '',
11889
componentWithTokensId,
119-
params,
90+
themeOverride,
12091
baseComponentTheme
12192
)
12293

123-
const componentTheme = { ...baseComponentTheme, ...themeOverride }
94+
const componentTheme = { ...baseComponentTheme, ...finalOverride }
12495

12596
return generateStyle(
12697
componentTheme,
127-
params ? params : {},
98+
params,
12899
(theme as Theme).newTheme.components.SharedTokens
129100
)
130101
}
131102

132103
export default useStyle
133104
export { useStyle }
105+
export type { ThemeOverrideValue }
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2015 - present Instructure, Inc.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
import { useTheme } from './useTheme'
26+
import { getComponentThemeOverride } from './getComponentThemeOverride'
27+
import type { ComponentTheme } from '@instructure/shared-types'
28+
import type { Theme } from '@instructure/ui-themes'
29+
30+
// returns the second parameter of a function
31+
type SecondParameter<T extends (...args: any) => any> =
32+
Parameters<T>[1] extends undefined ? never : Parameters<T>[1]
33+
34+
type GenerateComponentTheme = (theme: Theme) => ComponentTheme
35+
36+
type UseStyleParamsWithTheme<
37+
P extends (componentTheme: any, params: any, theme: any) => any
38+
> = {
39+
generateStyle: P
40+
params?: SecondParameter<P>
41+
generateComponentTheme: GenerateComponentTheme
42+
componentId: string
43+
displayName?: string
44+
}
45+
46+
// TODO this is only used by the old themes, remove when everything uses the new
47+
// theming system
48+
type UseStyleParamsWithoutTheme<
49+
P extends (componentTheme: any, params: any, theme: any) => any
50+
> = {
51+
generateStyle: P
52+
params?: SecondParameter<P>
53+
generateComponentTheme?: undefined
54+
componentId?: undefined
55+
displayName?: undefined
56+
}
57+
58+
/*
59+
* This is only used by the **old themes**, remove when everything uses the new
60+
* theming system (InstUI v12)
61+
*/
62+
const useStyleRework = <
63+
P extends (componentTheme: any, params: any, theme: any) => any
64+
>(
65+
useStyleParams: UseStyleParamsWithTheme<P> | UseStyleParamsWithoutTheme<P>
66+
): ReturnType<P> => {
67+
const { generateStyle, params, componentId, displayName } = useStyleParams
68+
const generateComponentTheme: GenerateComponentTheme = (
69+
useStyleParams as UseStyleParamsWithTheme<P>
70+
)?.generateComponentTheme
71+
const theme = useTheme()
72+
73+
const baseComponentTheme =
74+
typeof generateComponentTheme === 'function'
75+
? generateComponentTheme(theme as Theme)
76+
: {}
77+
78+
const themeOverride = getComponentThemeOverride(
79+
theme,
80+
displayName ?? componentId ?? '',
81+
componentId,
82+
params?.themeOverride,
83+
baseComponentTheme
84+
)
85+
86+
const componentTheme = { ...baseComponentTheme, ...themeOverride }
87+
88+
return generateStyle(componentTheme, params ? params : {}, theme)
89+
}
90+
91+
export default useStyleRework
92+
export { useStyleRework }

packages/emotion/src/withStyle.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ const withStyle = decorator(
193193
theme,
194194
displayName,
195195
ComposedComponent.componentId,
196-
componentProps,
196+
(componentProps as any).themeOverride,
197197
baseComponentTheme
198198
)
199199

@@ -264,4 +264,4 @@ const withStyle = decorator(
264264

265265
export default withStyle
266266
export { withStyle }
267-
export type { WithStyleProps }
267+
export type { WithStyleProps, ThemeOverrideProp }

0 commit comments

Comments
 (0)