diff --git a/packages/__docs__/src/withStyleForDocs.tsx b/packages/__docs__/src/withStyleForDocs.tsx index 0d7ed8e1e0..1a562c5b30 100644 --- a/packages/__docs__/src/withStyleForDocs.tsx +++ b/packages/__docs__/src/withStyleForDocs.tsx @@ -234,7 +234,7 @@ const withStyleForDocs = decorator( theme, displayName, ComposedComponent.componentId, - componentProps, + (componentProps as ThemeOverrideProp).themeOverride, componentTheme ) diff --git a/packages/emotion/src/InstUISettingsProvider/index.tsx b/packages/emotion/src/InstUISettingsProvider/index.tsx index a28ebb2631..1477097041 100644 --- a/packages/emotion/src/InstUISettingsProvider/index.tsx +++ b/packages/emotion/src/InstUISettingsProvider/index.tsx @@ -32,22 +32,61 @@ import { getTheme } from '../getTheme' import type { ThemeOrLegacyOverride } from '../EmotionTypes' import type { DeterministicIdProviderValue } from '@instructure/ui-react-utils' +import type { NewThemeOverrideObject, Theme } from '@instructure/ui-themes' declare const process: Record | undefined type InstUIProviderProps = { children?: React.ReactNode /** - * A full theme or an override object + * A full theme or an override object. The override only works for legacy + * (v11.6 or earlier) themes, for newer ones use the `themeOverride` prop. */ theme?: ThemeOrLegacyOverride - // TODO-theme-types: fix override typing - // TODO explain the usage of this override object. It will be deep merged into theme.themeOverride and the shape has to be a partial of the "newTheme" object /** - * An override object for the new theming system. + * An override object for the new theming system. It will be deep merged into + * the theme. One can override primitives, semantics and individual component's + * themes, for example: + * ```js + * themeOverride={{ + * semantics: { + * color: { + * stroke: { + * error: 'purple' + * } + * } + * }, + * primitives: { + * color: { + * blue: { + * blue100: 'yellow' + * } + * } + * }, + * components: { + * Alert: { + * background: 'brown', + * infoIconBackground: 'darkblue', + * borderWidth: '0.5rem' + * }, + * Pill: { + * baseTextColor: 'purple', + * baseBorderColor: 'purple' + * } + * }, + * sharedTokens: { + * focusOutline: { + * width: '0.55rem', + * infoColor: 'deeppink' + * } + * } + * }} + * ``` */ - themeOverride?: any + themeOverride?: + | NewThemeOverrideObject + | ((theme: Theme) => NewThemeOverrideObject) /** * @deprecated the `instanceCounterMap` prop is deprecated. You don't need to supply the @@ -100,7 +139,6 @@ function InstUISettingsProvider({ * For backward compatibility reasons, the old way of passing a partial theme to the theme prop is still supported, however only for * legacy (pre v11_7) components. Overriding the newTheme this way could break the system. */ - let providers = ( @@ -120,3 +158,4 @@ function InstUISettingsProvider({ export default InstUISettingsProvider export { InstUISettingsProvider } +export type { InstUIProviderProps } diff --git a/packages/emotion/src/getComponentThemeOverride.ts b/packages/emotion/src/getComponentThemeOverride.ts index 6d4550d8ce..17473724fd 100644 --- a/packages/emotion/src/getComponentThemeOverride.ts +++ b/packages/emotion/src/getComponentThemeOverride.ts @@ -29,7 +29,7 @@ import type { } from './EmotionTypes' import type { ComponentTheme } from '@instructure/shared-types' import { ThemeOverrideProp } from './withStyle' -import { ThemeOverrideValue } from './useStyle' +import type { NewComponentTypes } from '@instructure/ui-themes' type ComponentName = keyof ComponentOverride | undefined @@ -51,8 +51,10 @@ const getComponentThemeOverride = ( theme: ThemeOverride, displayName: string, componentId?: string, - // ThemeOverrideProp is the old type, ThemeOverrideValue is the new one - themeOverride?: ThemeOverrideProp['themeOverride'] | ThemeOverrideValue, + // ThemeOverrideProp['themeOverride'] is the old type + themeOverride?: + | ThemeOverrideProp['themeOverride'] + | ReturnType, componentTheme?: ComponentTheme ): Partial => { const name = displayName as ComponentName diff --git a/packages/emotion/src/getTheme.ts b/packages/emotion/src/getTheme.ts index 8946ef13e9..0322d564ff 100644 --- a/packages/emotion/src/getTheme.ts +++ b/packages/emotion/src/getTheme.ts @@ -24,13 +24,14 @@ import canvas from '@instructure/ui-themes' import { isBaseTheme, mergeDeep } from '@instructure/ui-utils' -import type { BaseTheme } from '@instructure/shared-types' +import type { Theme } from '@instructure/ui-themes' import type { Overrides, ThemeOrLegacyOverride, SpecificThemeOverride } from './EmotionTypes' +import { InstUIProviderProps } from './InstUISettingsProvider' declare const process: Record | undefined /** @@ -45,14 +46,20 @@ declare const process: Record | undefined * the overrides merged together. * * @param themeOrLegacyOverride - A full theme or an override object - * @param themeOverride - if provided, it means it's a new theming-system override. This will be merged into theme.themeOverride and will be treated separately from the old way of applying overrides. This override will be applied in the withStyle.ts decorator + * @param themeOverride - if provided, it means it's a new theming-system override. + * This will be merged into theme.themeOverride and will be treated separately + * from the old way of applying overrides. This override will be applied in the + * `withStyle.ts` decorator * @returns A function that returns with the theme object for the [ThemeProvider](https://emotion.sh/docs/theming#themeprovider-reactcomponenttype) * This function is called by Emotion on theme provider creation, where * `ancestorTheme` is a theme object from an ancestor `ThemeProvider` */ const getTheme = - (themeOrLegacyOverride: ThemeOrLegacyOverride, themeOverride?: any) => - (ancestorTheme = {} as BaseTheme) => { + ( + themeOrLegacyOverride: ThemeOrLegacyOverride, + themeOverride?: InstUIProviderProps['themeOverride'] + ) => + (ancestorTheme = {} as Theme) => { // we need to clone the ancestor theme not to override it let currentTheme if (Object.keys(ancestorTheme).length === 0) { diff --git a/packages/emotion/src/index.ts b/packages/emotion/src/index.ts index 52a912f135..c87581d0d1 100644 --- a/packages/emotion/src/index.ts +++ b/packages/emotion/src/index.ts @@ -43,9 +43,10 @@ export { useStyleLegacy } from './useStyleLegacy' export { useStyle } from './useStyle' export { useTheme } from './useTheme' +export type { InstUIProviderProps } from './InstUISettingsProvider' export type { ComponentStyle, StyleObject, Overrides } from './EmotionTypes' -export type { WithStyleProps } from './withStyleLegacy' -export type { ThemeOverrideValue } from './useStyle' +export type { WithStyleProps } from './withStyle' +export type { NewThemeOverrideProp } from './useStyle' export type { SpacingValues, Spacing, diff --git a/packages/emotion/src/useStyle.ts b/packages/emotion/src/useStyle.ts index 98c1f69fae..9d0df63837 100644 --- a/packages/emotion/src/useStyle.ts +++ b/packages/emotion/src/useStyle.ts @@ -25,8 +25,8 @@ import { useTheme } from './useTheme' import { mergeDeep } from '@instructure/ui-utils' import type { - SharedTokens, NewComponentTypes, + SharedTokens, Theme } from '@instructure/ui-themes' @@ -34,89 +34,84 @@ import type { type SecondParameter any> = Parameters[1] extends undefined ? never : Parameters[1] -type GenerateStyleParams = - | ((componentTheme: any, params: any, sharedTokens: SharedTokens) => any) - | ((componentTheme: any, params: any) => any) - | ((componentTheme: any) => any) +type NewThemeOverrideProp< + ComponentTheme extends ReturnType +> = { + themeOverride?: + | Partial + | ((compTheme: ComponentTheme, theme: Theme) => Partial) +} -/** - * Type for a theme override - */ -type ThemeOverrideValue = - | Partial +type GenerateStyleFn = | (( - componentTheme: Theme, - currentTheme: NewComponentTypes[keyof NewComponentTypes] - ) => Partial) + componentTheme: ComponentTheme, + params: any, + sharedTokens: SharedTokens + ) => any) + | ((componentTheme: ComponentTheme, params: any) => any) + | ((componentTheme: ComponentTheme) => any) /** - * new useStyle syntax, use this with v12 themes + * new useStyle syntax, use this with v11.7+ themes */ - // TODO: improve useStyle to handle generateStyle functions that don't // have a theme. -const useStyle =

(useStyleParams: { - generateStyle: P - params?: SecondParameter

+const useStyle = < + ComponentTheme extends ReturnType, + GenerateStyle extends GenerateStyleFn +>(useStyleParams: { + generateStyle: GenerateStyle + params?: SecondParameter // needs to be a string too because it might be a child component componentId: keyof NewComponentTypes | string - themeOverride: ThemeOverrideValue | undefined + themeOverride?: NewThemeOverrideProp['themeOverride'] displayName?: string - //in case of a child component needed to use it's parent's tokens, provide parent's name + //in case of a child component needed to use its parent's tokens, provide parent's name useTokensFrom?: keyof NewComponentTypes -}): ReturnType

=> { +}): ReturnType => { const { generateStyle, params, componentId, themeOverride } = useStyleParams const useTokensFrom = useStyleParams.useTokensFrom const themeInContext = useTheme() as Theme const themeOverrideFromProvider = themeInContext.themeOverride - const componentWithTokensId = useTokensFrom ?? componentId + const componentWithTokensId = + useTokensFrom ?? (componentId as keyof NewComponentTypes) // resolving the theming functions and applying the overrides const primitiveOverrides = themeOverrideFromProvider?.primitives const semanticsOverrides = themeOverrideFromProvider?.semantics - // @ts-ignore TODO-theme-types: fix typing const sharedTokensOverrides = themeOverrideFromProvider?.sharedTokens const componentOverridesFromSettingsProvider = - // @ts-ignore TODO-theme-types: fix typing - themeOverrideFromProvider?.components?.[ - componentWithTokensId as keyof NewComponentTypes - ] + themeOverrideFromProvider?.components?.[componentWithTokensId] const primitives = mergeDeep( themeInContext.newTheme.primitives, - primitiveOverrides + primitiveOverrides! ) const semantics = mergeDeep( themeInContext.newTheme.semantics?.(primitives), - semanticsOverrides + semanticsOverrides! ) const sharedTokens = mergeDeep( themeInContext.newTheme.sharedTokens?.(semantics), - sharedTokensOverrides + sharedTokensOverrides as Record ) const baseComponentTheme = - themeInContext.newTheme.components[ - componentWithTokensId as keyof NewComponentTypes - ]?.(semantics) + themeInContext.newTheme.components[componentWithTokensId]?.(semantics) const componentThemeFromSettingsProvider = mergeDeep( baseComponentTheme, - componentOverridesFromSettingsProvider - ) + componentOverridesFromSettingsProvider as Record + ) as ComponentTheme const componentTheme = mergeDeep( componentThemeFromSettingsProvider, - // @ts-ignore TODO-theme-types: fix typing typeof themeOverride === 'function' - ? themeOverride( - componentThemeFromSettingsProvider as Theme, - themeInContext as any - ) - : themeOverride + ? themeOverride(componentThemeFromSettingsProvider, themeInContext) + : themeOverride! ) // @ts-ignore TODO-theme-types: fix typing @@ -125,4 +120,4 @@ const useStyle =

(useStyleParams: { export default useStyle export { useStyle } -export type { ThemeOverrideValue } +export type { NewThemeOverrideProp } diff --git a/packages/emotion/src/withStyle.tsx b/packages/emotion/src/withStyle.tsx index 9eb74118ad..923d138577 100644 --- a/packages/emotion/src/withStyle.tsx +++ b/packages/emotion/src/withStyle.tsx @@ -37,11 +37,7 @@ import { decorator } from '@instructure/ui-decorator' import { useTheme } from './useTheme' -import type { - BaseTheme, - ComponentTheme, - InstUIComponent -} from '@instructure/shared-types' +import type { ComponentTheme, InstUIComponent } from '@instructure/shared-types' import type { ComponentStyle, ComponentOverride, @@ -49,7 +45,11 @@ import type { Props } from './EmotionTypes' -import type { NewComponentTypes, Theme } from '@instructure/ui-themes' +import type { + NewComponentTypes, + SharedTokens, + Theme +} from '@instructure/ui-themes' // Extract is needed because it would allow number otherwise // https://stackoverflow.com/a/51808262/319473 @@ -66,28 +66,30 @@ type WithStylePrivateProps< > = Style extends null ? object : { - styles?: Style - makeStyles?: (extraArgs?: Record) => void - } + styles?: Style + makeStyles?: (extraArgs?: Record) => void + } -type ThemeOverrideProp = { +type ThemeOverrideProp< + CompTheme extends ComponentTheme | null = ComponentTheme +> = { themeOverride?: - | Partial - | ((componentTheme: Theme, currentTheme: BaseTheme) => Partial) + | Partial + | ((componentTheme: CompTheme, currentTheme: Theme) => Partial) } type WithStyleProps< - Theme extends ComponentTheme | null = ComponentTheme, + CompTheme extends ComponentTheme | null = ComponentTheme, Style extends ComponentStyle | null = ComponentStyle > = Theme extends null ? WithStylePrivateProps