From 5b7af5df35c86bc17cc5b960127026f91f628f21 Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Tue, 4 Feb 2025 13:32:10 +0500 Subject: [PATCH 01/27] chore: wips --- .../decorators/Theme/ThemeDecorator.tsx | 12 +- packages/react-ui/index.ts | 6 +- .../internal/themes/BasicDarkTheme.ts | 557 ----------------- .../{BasicLightTheme.ts => BasicTheme.ts} | 6 +- .../react-ui/internal/themes/DarkTheme5_0.ts | 558 ++++++++++++++++++ .../react-ui/internal/themes/DarkTheme5_1.ts | 9 + .../react-ui/internal/themes/LightTheme5_0.ts | 8 + .../react-ui/internal/themes/LightTheme5_1.ts | 9 + packages/react-ui/lib/theming/Theme.ts | 6 +- packages/react-ui/lib/theming/ThemeFactory.ts | 10 +- packages/react-ui/lib/theming/ThemeHelpers.ts | 46 +- .../lib/theming/__tests__/Theming-test.tsx | 18 +- .../react-ui/lib/theming/themes/DarkTheme.ts | 6 +- .../lib/theming/themes/DarkTheme2022_0.ts | 5 - .../lib/theming/themes/DarkTheme5_0.ts | 1 + .../lib/theming/themes/DarkTheme5_1.ts | 1 + .../react-ui/lib/theming/themes/LightTheme.ts | 6 +- .../lib/theming/themes/LightTheme2022_0.ts | 5 - .../lib/theming/themes/LightTheme5_0.ts | 1 + .../lib/theming/themes/LightTheme5_1.ts | 1 + 20 files changed, 665 insertions(+), 606 deletions(-) delete mode 100644 packages/react-ui/internal/themes/BasicDarkTheme.ts rename packages/react-ui/internal/themes/{BasicLightTheme.ts => BasicTheme.ts} (99%) create mode 100644 packages/react-ui/internal/themes/DarkTheme5_0.ts create mode 100644 packages/react-ui/internal/themes/DarkTheme5_1.ts create mode 100644 packages/react-ui/internal/themes/LightTheme5_0.ts create mode 100644 packages/react-ui/internal/themes/LightTheme5_1.ts delete mode 100644 packages/react-ui/lib/theming/themes/DarkTheme2022_0.ts create mode 100644 packages/react-ui/lib/theming/themes/DarkTheme5_0.ts create mode 100644 packages/react-ui/lib/theming/themes/DarkTheme5_1.ts delete mode 100644 packages/react-ui/lib/theming/themes/LightTheme2022_0.ts create mode 100644 packages/react-ui/lib/theming/themes/LightTheme5_0.ts create mode 100644 packages/react-ui/lib/theming/themes/LightTheme5_1.ts diff --git a/packages/react-ui/.storybook/decorators/Theme/ThemeDecorator.tsx b/packages/react-ui/.storybook/decorators/Theme/ThemeDecorator.tsx index 3c5042cabb6..e6d40e8faed 100644 --- a/packages/react-ui/.storybook/decorators/Theme/ThemeDecorator.tsx +++ b/packages/react-ui/.storybook/decorators/Theme/ThemeDecorator.tsx @@ -3,8 +3,10 @@ import { Decorator } from '@storybook/react'; import { LIGHT_THEME } from '../../../lib/theming/themes/LightTheme'; import { DARK_THEME } from '../../../lib/theming/themes/DarkTheme'; -import { LIGHT_THEME_2022_0 } from '../../../lib/theming/themes/LightTheme2022_0'; -import { DARK_THEME_2022_0 } from '../../../lib/theming/themes/DarkTheme2022_0'; +import { LIGHT_THEME_5_0 } from '../../../lib/theming/themes/LightTheme5_0'; +import { DARK_THEME_5_0 } from '../../../lib/theming/themes/DarkTheme5_0'; +import { LIGHT_THEME_5_1 } from '../../../lib/theming/themes/LightTheme5_1'; +import { DARK_THEME_5_1 } from '../../../lib/theming/themes/DarkTheme5_1'; import { ThemeContext } from '../../../lib/theming/ThemeContext'; import { ThemeFactory } from '../../../lib/theming/ThemeFactory'; import { isDarkTheme } from '../../../lib/theming/ThemeHelpers'; @@ -12,8 +14,10 @@ import { isDarkTheme } from '../../../lib/theming/ThemeHelpers'; export const themes = { LIGHT_THEME, DARK_THEME, - LIGHT_THEME_2022_0, - DARK_THEME_2022_0, + LIGHT_THEME_5_0, + DARK_THEME_5_0, + LIGHT_THEME_5_1, + DARK_THEME_5_1, }; export const ThemeDecorator: Decorator = (Story, context) => { diff --git a/packages/react-ui/index.ts b/packages/react-ui/index.ts index ead01aa9757..ebbddb58a2d 100644 --- a/packages/react-ui/index.ts +++ b/packages/react-ui/index.ts @@ -51,8 +51,10 @@ export * from './lib/featureFlagsContext'; export * from './lib/locale'; export * from './lib/theming/ThemeContext'; export * from './lib/theming/ThemeFactory'; -export * from './lib/theming/themes/LightTheme2022_0'; -export * from './lib/theming/themes/DarkTheme2022_0'; +export * from './lib/theming/themes/LightTheme5_0'; +export * from './lib/theming/themes/DarkTheme5_0'; +export * from './lib/theming/themes/LightTheme5_1'; +export * from './lib/theming/themes/DarkTheme5_1'; export * from './lib/theming/themes/LightTheme'; export * from './lib/theming/themes/DarkTheme'; export * from './lib/types/props'; diff --git a/packages/react-ui/internal/themes/BasicDarkTheme.ts b/packages/react-ui/internal/themes/BasicDarkTheme.ts deleted file mode 100644 index 8fa6ab89991..00000000000 --- a/packages/react-ui/internal/themes/BasicDarkTheme.ts +++ /dev/null @@ -1,557 +0,0 @@ -import { exposeGetters } from '../../lib/theming/ThemeHelpers'; - -import { BasicLightTheme } from './BasicLightTheme'; - -export class BasicDarkThemeInternal extends (class {} as typeof BasicLightTheme) { - //#region Common variables - public static grayXLight = '#313131'; - public static gray = 'rgba(255, 255, 255, 0.48)'; - public static grayDark = '#e1e1e1'; - public static green = '#23A14A'; - public static greenDark = '#1C8A3F'; - public static red = '#FE4C4C'; - public static redDark = '#DD3333'; - public static yellowXxLight = '#ffe0c3'; - public static yellow = '#ffa236'; - public static yellowDark = '#ea7324'; - public static warningMain = '#ffa236'; - public static get errorMain() { - return this.red; - } - public static errorText = '#FE6C6C'; - public static errorSecondary = '#AB0D0D'; - public static borderColorFocusLight = '#1260ae'; - public static borderColorGrayLight = 'rgba(255, 255, 255, 0.16)'; - public static borderColorDisabled = 'rgba(0, 0, 0, 0.10)'; - public static placeholderColor = 'rgba(255, 255, 255, 0.32)'; - public static placeholderColorLight = 'rgba(255, 255, 255, 0.16)'; - public static textColorDefault = 'rgba(255, 255, 255, 0.865)'; - public static textColorDisabled = 'rgba(255, 255, 255, 0.32)'; - public static textColorDisabledContrast = 'rgba(255, 255, 255, 0.48)'; - public static textColorInvert = 'rgba(255, 255, 255, 0.865)'; - public static bgDisabled = '#434343'; - public static bgDefault = '#1f1f1f'; - public static bgSecondary = '#333333'; - public static bgChecked = '#EBEBEB'; - public static outlineColorFocus = '#EBEBEB'; - public static borderColorFocus = '#EBEBEB'; - public static get borderColorError() { - return this.errorMain; - } - public static fixedPanelShadow = 'none'; - //#endregion Common variables - //#region CloseIcon, CloseButtonIcon - public static closeBtnIconColor = 'rgba(255, 255, 255, 0.32)'; - //#endregion CloseIcon, CloseButtonIcon - //#region Link - public static linkColor = 'rgba(255, 255, 255, 0.87)'; - public static linkHoverColor = '#ffffff'; - public static linkActiveColor = '#c2c2c2'; - - public static linkGrayedColor = 'rgba(255, 255, 255, 0.54)'; - public static linkGrayedHoverColor = '#FFFFFF'; - public static linkGrayedActiveColor = '#C2C2C2'; - - public static linkSuccessColor = '#46CD68'; - public static linkSuccessHoverColor = '#67D881'; - public static get linkSuccessActiveColor() { - return this.green; - } - - public static get linkDangerColor() { - return this.errorText; - } - public static linkDangerHoverColor = '#FE8C8C'; - public static get linkDangerActiveColor() { - return '#ED3F3F'; - } - - public static linkFocusOutlineColor = '#EBEBEB'; - public static linkHoverTextDecoration = 'none'; - - public static get linkDisabledColor() { - return this.textColorDisabled; - } - //#endregion Link - //#region Button - public static btnFocusShadowWidth = '2px'; - public static btnOutlineColorFocus = '#1f1f1f'; - public static btnInsetColor = '#1f1f1f'; - - // default - public static btnDefaultBg = 'rgba(255, 255, 255, 0.1)'; - public static btnDefaultHoverBg = 'rgba(255, 255, 255, 0.16)'; - public static btnDefaultActiveBg = 'rgba(255, 255, 255, 0.1)'; - public static btnDefaultHoverBgStart = 'none'; - public static btnDefaultHoverBgEnd = 'none'; - public static get btnDefaultHoverBorderColor() { - return this.borderColorGrayLight; - } - public static get btnDefaultActiveBorderColor() { - return this.borderColorGrayLight; - } - public static btnDefaultBorderColor = 'rgba(255, 255, 255, 0.16)'; - public static btnDefaultTextColor = 'rgba(255, 255, 255, 0.87)'; - - // success - public static get btnSuccessBg() { - return this.green; - } - public static get btnSuccessBorderColor() { - return this.btnSuccessBg; - } - public static get btnSuccessTextColor() { - return this.textColorDefault; - } - - public static btnSuccessHoverBg = '#26AD50'; - public static get btnSuccessHoverBorderColor() { - return this.btnSuccessHoverBg; - } - - public static get btnSuccessActiveBg() { - return this.greenDark; - } - public static get btnSuccessActiveBorderColor() { - return this.btnSuccessActiveBg; - } - - // primary - public static btnPrimaryBg = '#EBEBEB'; - public static btnPrimaryBorderColor = '#3D3D3D'; - - public static btnPrimaryHoverBg = '#FFFFFF'; - public static btnPrimaryActiveBg = '#C2C2C2'; - public static btnPrimaryActiveBorderColor = '#1261ae'; - - public static btnPrimaryTextColor = 'rgba(0, 0, 0, 0.865)'; - - // danger - public static get btnDangerBg() { - return this.errorMain; - } - public static get btnDangerBorderColor() { - return this.btnDangerBg; - } - - public static btnDangerHoverBg = '#ED3F3F'; - public static get btnDangerHoverBorderColor() { - return this.btnDangerHoverBg; - } - public static get btnDangerTextColor() { - return this.textColorDefault; - } - public static btnDangerActiveBg = '#DD3333'; - public static get btnDangerActiveBorderColor() { - return this.btnDangerActiveBg; - } - - // pay - public static btnPayBg = '#FCB73E'; - public static get btnPayBorderColor() { - return this.btnPayBg; - } - public static btnPayHoverBg = '#FCC660'; - public static get btnPayHoverBorderColor() { - return this.btnPayHoverBg; - } - public static btnPayTextColor = '#222222'; - public static btnPayActiveBg = '#F69912'; - public static get btnPayActiveBorderColor() { - return this.btnPayActiveBg; - } - - // backless - public static btnBacklessBg = 'transparent'; - public static btnBacklessHoverBg = 'rgba(255, 255, 255, 0.1)'; - public static btnBacklessActiveBg = 'rgba(255, 255, 255, 0.06)'; - public static btnBacklessBorderColor = 'rgba(255, 255, 255, 0.16)'; - public static btnBacklessDisabledBorderColor = 'rgba(255, 255, 255, 0.06)'; - public static btnBacklessHoverBorderColor = 'rgba(255, 255, 255, 0.1)'; - public static btnBacklessTextColor = 'rgba(255, 255, 255, 0.87)'; - public static btnBacklessActiveBorderColor = 'rgba(255, 255, 255, 0.1)'; - - // text - public static btnTextBg = 'transparent'; - public static btnTextHoverBg = 'rgba(255, 255, 255, 0.1)'; - public static btnTextActiveBg = 'rgba(255, 255, 255, 0.06)'; - public static btnTextBorderColor = 'transparent'; - - // warning, error - public static btnWarningSecondary = 'rgba(212, 100, 33, 1)'; - public static get btnErrorSecondary() { - return this.errorSecondary; - } - - // disabled - public static btnDisabledBg = 'rgba(255, 255, 255, 0.04)'; - public static get btnDisabledTextColor() { - return this.textColorDisabled; - } - public static btnDisabledBorderColor = 'transparent'; - - // checked - public static btnCheckedBg = '#EBEBEB'; - public static btnCheckedTextColor = 'rgba(0, 0, 0, 0.865)'; - public static btnCheckedDisabledBg = 'rgba(255, 255, 255, 0.32) !important'; - public static btnCheckedDisabledColor = 'rgba(0, 0, 0, 0.48)'; - //#endregion Button - //#region Tooltip - public static tooltipCloseBtnColor = 'rgba(255, 255, 255, 0.32)'; - public static tooltipCloseBtnHoverColor = 'rgba(255, 255, 255, 0.865)'; - public static tooltipBg = '#333333'; - //#endregion Tooltip - //#region Modal - public static modalBg = '#222'; - public static modalBackBg = '#000'; - public static modalBackOpacity = '0.7'; - - public static modalCloseButtonColor = 'rgba(255, 255, 255, 0.32)'; - public static modalCloseButtonHoverColor = 'rgba(255, 255, 255, 0.865)'; - - public static modalFooterBg = '#222222'; - public static get modalFooterTextColor() { - return this.textColorDefault; - } - public static get modalBodyTextColor() { - return this.textColorDefault; - } - public static get modalFixedPanelShadow() { - return this.fixedPanelShadow; - } - public static get modalFixedHeaderBg() { - return this.modalBg; - } - public static modalSeparatorBorderBottom = 'solid 1px rgba(255, 255, 255, 0.1)'; - //#endregion Modal - //#region SidePage - public static sidePageFooterPanelBg = ' #222222'; - public static sidePageBackingBg = '#000'; - public static sidePageBgDefault = '#222'; - public static sidePageBackingBgOpacity = '0.7'; - public static sidePageCloseButtonColor = 'rgba(255, 255, 255, 0.32)'; - public static sidePageCloseButtonHoverColor = 'rgba(255, 255, 255, 0.865)'; - public static get sidePageBodyTextColor() { - return this.textColorDefault; - } - public static get sidePageFooterTextColor() { - return this.textColorDefault; - } - public static get sidePageFixedPanelShadow() { - return this.fixedPanelShadow; - } - //#endregion SidePage - - //#region Select - public static selectMenuArrowColor = 'rgba(255, 255, 255, 0.54)'; - public static get selectPlaceholderColorDisabled() { - return this.textColorDisabled; - } - public static get selectMenuArrowColorDisabled() { - return this.textColorDisabled; - } - //#endregion Select - //#region Calendar - public static get calendarCellWeekendColor() { - return this.errorText; - } - public static calendarCellTodayBorder = '1px solid rgba(255, 255, 255, 0.48)'; - public static calendarCellSelectedBgColor = 'rgba(255, 255, 255, 0.1)'; - public static calendarMonthTitleBorderBottomColor = 'rgba(255, 255, 255, 0.08)'; - public static calendarCellHoverBgColor = 'rgba(255, 255, 255, 0.06)'; - public static calendarCellHoverColor = 'rgba(255, 255, 255, 0.87)'; - public static calendarCellSelectedFontColor = 'rgba(255, 255, 255, 0.87)'; - //#endregion Calendar - //#region Paging - public static pagingPageLinkColor = 'rgba(255, 255, 255, 0.87)'; - public static pagingPageLinkHoverBg = 'rgba(255, 255, 255, 0.06)'; - public static pagingPageLinkActiveBg = 'rgba(255, 255, 255, 0.1)'; - public static pagingPageLinkDisabledActiveBg = 'rgba(255, 255, 255, 0.08)'; - public static pagingForwardLinkColor = 'rgba(255, 255, 255, 0.87)'; - public static get pagingForwardLinkDisabledColor() { - return this.linkDisabledColor; - } - //#endregion Paging - //#region Toast - public static toastLinkColor = 'rgba(44, 44, 44, 1.0)'; - public static toastCloseColor = 'rgba(0, 0, 0, 0.32)'; - - public static toastColor = 'rgba(44, 44, 44, 1.0)'; - public static toastBg = 'rgba(255, 255, 255, 0.8)'; - public static toastLinkBgHover = 'rgba(255, 255, 255, 0.87)'; - public static toastLinkBgActive = 'rgba(0, 0, 0, 0.16)'; - public static toastCloseHoverColor = 'rgba(0, 0, 0, 0.87)'; - //#endregion Toast - - //#region Tab, Tabs - public static tabColorHover = 'rgba(255, 255, 255, 0.16)'; - //#endregion Tab, Tabs - //#region Menu - public static get menuBgDefault() { - return this.bgSecondary; - } - // menuItem - public static menuItemTextColor = 'rgba(255, 255, 255, 0.87)'; - public static menuItemHoverBg = 'rgba(255, 255, 255, 0.1)'; - public static menuItemSelectedBg = 'rgba(255, 255, 255, 0.16)'; - - public static menuItemCommentColor = 'rgba(255, 255, 255, 0.54)'; - public static menuItemCommentOpacity = '1'; - public static get menuItemDisabledColor() { - return this.textColorDisabled; - } - // menuHeader - public static get menuHeaderColor() { - return this.gray; - } - // menuFooter - public static get menuFooterColor() { - return this.menuHeaderColor; - } - //menuSeparator - public static menuSeparatorBorderColor = 'rgba(255, 255, 255, 0.1)'; - //#endregion Menu - //#region Toggle - public static toggleBorderColor = 'rgba(255, 255, 255, 0.32)'; - public static toggleBaseBg = 'none'; - - public static toggleOutlineColorFocus = '#1F1F1F'; - - // idle - public static toggleContainerBg = 'rgba(255, 255, 255, 0.1)'; - public static toggleContainerBoxShadow = 'inset 0 0 0 1px rgba(255, 255, 255, 0.06)'; - public static toggleHandleBg = 'rgba(255, 255, 255, 0.32)'; - public static toggleHandleBoxShadow = 'inset 0 0 0 1px rgba(255, 255, 255, 0.06)'; - - // idle :hover - public static toggleBgHover = 'rgba(255, 255, 255, 0.16)'; - public static toggleContainerBoxShadowHover = 'inset 0 0 0 1px rgba(255, 255, 255, 0.06)'; - public static toggleHandleBgHover = 'rgba(255, 255, 255, 0.32)'; - public static toggleHandleBoxShadowHover = 'inset 0 0 0 1px rgba(255, 255, 255, 0.06)'; - - // checked - public static toggleBgChecked = 'rgba(255, 255, 255, 0.1)'; - public static toggleContainerBoxShadowChecked = 'inset 0 0 0 1px rgba(255, 255, 255, 0.06)'; - public static toggleHandleBgChecked = '#EBEBEB'; - public static toggleHandleBoxShadowChecked = 'none'; - - // checked :hover - public static get toggleContainerBgCheckedHover() { - return this.toggleContainerBgHover; - } - public static toggleContainerBoxShadowCheckedHover = 'inset 0 0 0 1px rgba(255, 255, 255, 0.06)'; - public static toggleHandleBgCheckedHover = '#FFFFFF'; - public static toggleHandleBoxShadowCheckedHover = 'none'; - - // disabled - public static get toggleBgDisabled() { - return this.bgDisabled; - } - public static toggleDisabledHandleBg = '#525252'; - - public static toggleContainerBgDisabled = 'rgba(255, 255, 255, 0.04)'; - public static toggleContainerBoxShadowDisabled = 'none'; - public static toggleHandleBgDisabled = 'rgba(0, 0, 0, 0.16)'; - public static toggleHandleBoxShadowDisabled = 'none'; - - // disabled checked - public static toggleContainerBgDisabledChecked = 'rgba(255, 255, 255, 0.06)'; - public static toggleContainerBoxShadowDisabledChecked = 'none'; - public static toggleHandleBgDisabledChecked = 'rgba(0, 0, 0, 0.16)'; - public static toggleHandleBoxShadowDisabledChecked = 'none'; - public static toggleBgDisabledChecked = 'rgba(255, 255, 255, 0.48)'; - - //#endregion Toggle - //#region Token - public static tokenShadowDisabled = ''; - public static tokenBorderColorDisabled = 'transparent'; - public static get tokenTextColorDisabled() { - return this.textColorDisabled; - } - - public static tokenColor = 'rgba(255, 255, 255, 0.865)'; - public static tokenBg = 'rgba(255, 255, 255, 0.1)'; - public static tokenBorderColor = 'rgba(255, 255, 255, 0.06)'; - public static tokenColorHover = 'rgba(255, 255, 255, 0.87)'; - public static tokenBgHover = 'rgba(255, 255, 255, 0.16)'; - public static tokenBorderColorHover = 'rgba(255, 255, 255, 0.06)'; - public static tokenColorActive = 'rgba(0, 0, 0, 0.87)'; - public static tokenBgActive = '#EBEBEB'; - public static tokenBorderColorActive = 'transparent'; - //#endregion Token - //#region Input - public static inputBg = 'rgba(255, 255, 255, 0.1)'; - public static get inputBorderTopColor() { - return this.inputBorderColor; - } - public static inputColorScheme = 'dark'; - public static inputBlinkColor = '#EBEBEB'; - public static inputTextColor = 'rgba(255, 255, 255, 0.865)'; - public static inputBorderColor = 'rgba(255, 255, 255, 0.06)'; - public static inputBackgroundClip = 'border-box'; - public static inputBorderColorHover = 'rgba(255, 255, 255, 0.16)'; - public static inputBorderColorFocus = 'rgba(235, 235, 235, 1)'; - public static get inputFocusShadow() { - return `0 0 0 1px ${this.inputBorderColorFocus}`; - } - - public static inputBorderColorWarning = 'rgba(252, 183, 62, 1)'; - public static get inputBorderColorError() { - return this.borderColorError; - } - public static inputOutlineWidth = '1px'; - - public static get inputTextColorDisabled() { - return this.textColorDisabled; - } - public static get inputIconColorDisabled() { - return this.textColorDisabled; - } - public static get inputPlaceholderColorDisabled() { - return this.textColorDisabled; - } - public static inputDisabledBg = 'rgba(255, 255, 255, 0.04)'; - public static inputDisabledBorderColor = 'rgba(255, 255, 255, 0.04)'; - //#endregion Input - //#region DateSelect - public static dateSelectMenuItemBgActive = 'rgba(255, 255, 255, 0.06)'; - public static dateSelectMenuItemBgSelected = 'rgba(255, 255, 255, 0.1)'; - //#endregion DateSelect - //#region DateInput - public static dateInputComponentSelectedBgColor = ''; - public static dateInputComponentSelectedTextColor = ''; - //#endregion DateInput - //#region Hint - public static hintColor = 'rgba(44, 44, 44, 1.0)'; - public static hintBgColor = 'rgba(255, 255, 255, 0.8)'; - //#endregion Hint - //#region Loader - public static loaderBg = 'rgba(51, 51, 51, 0.8)'; - //#endregion - //#region GlobalLoader - public static globalLoaderColor = 'rgba(238, 80, 66, 1)'; - //#endregion GlobalLoader - //#region TextArea - public static get textareaBg() { - return this.inputBg; - } - public static get textareaBorderColor() { - return this.inputBorderColor; - } - public static get textareaTextColorDisabled() { - return this.inputTextColorDisabled; - } - public static get textareaPlaceholderColorDisabled() { - return this.textColorDisabled; - } - public static get textareaBorderTopColor() { - return this.textareaBorderColor; - } - public static get textareaDisabledBorderColor() { - return this.inputDisabledBorderColor; - } - public static get textareaBorderColorFocus() { - return this.inputBorderColorFocus; - } - //#endregion TextArea - //#region PasswordInput - public static get passwordInputVisibilityIconColor() { - return this.gray; - } - public static passwordInputVisibilityIconOpacity = '1'; - public static get passwordInputVisibilityIconHoverColor() { - return this.textColorDefault; - } - //#endregion PasswordInput - //#region Radio - public static radioBgColor = 'rgba(255, 255, 255, 0.04)'; - public static radioBorderColor = 'rgba(255, 255, 255, 0.32)'; - - public static radioFocusShadow = 'inset 0 0 0 1px rgba(0, 0, 0, 0.87)'; - - public static radioDisabledBg = 'rgba(255, 255, 255, 0.04)'; - public static radioDisabledShadow = 'none'; - public static radioCheckedDisabledBulletBg = 'rgba(255, 255, 255, 0.16)'; - - public static radioCheckedBgColor = '#FFFFFF'; - public static radioCheckedBulletColor = 'rgba(0, 0, 0, 0.87)'; - public static radioCheckedBorderColor = 'transparent'; - //#endregion Radio - //#region Checkbox - public static checkboxBg = 'rgba(255, 255, 255, 0.04)'; - public static get checkboxShadow() { - return `0 0 0 ${this.checkboxBorderWidth} rgba(255, 255, 255, 0.32)`; - } - public static get checkboxShadowHover() { - return this.checkboxShadow; - } - - public static get checkboxCheckedBg() { - return this.bgChecked; - } - public static checkboxCheckedColor = 'rgba(0, 0, 0, 0.87)'; - public static checkboxHoverBg = 'rgba(255, 255, 255, 0.16)'; - public static get checkboxCheckedHoverBg() { - return this.checkboxCheckedBg; - } - public static checkboxOutlineColorFocus = 'rgba(0, 0, 0, 0.87)'; - public static checkboxBgDisabled = 'rgba(255, 255, 255, 0.04)'; - public static checkboxShadowDisabled = 'none'; - //#endregion Checkbox - //#region TokenInput - public static inputDisabledBackgroundClip = 'border-box'; - //#endregion TokenInput - //#region Switcher - public static get switcherButtonDisabledBorderColor() { - return this.borderColorDisabled; - } - public static switcherButtonCheckedDisabledShadow = 'none'; - //#endregion Switcher - //#region FileUploader - public static fileUploaderBg = 'none'; - public static fileUploaderTextColorDefault = '#fff'; - public static fileUploaderBorderColor = 'rgba(255, 255, 255, 0.32)'; - public static get fileUploaderBorderColorFocus() { - return this.btnBorderColorFocus; - } - public static fileUploaderLinkColor = 'rgba(255, 255, 255, 0.87)'; - public static fileUploaderIconColor = 'rgba(255, 255, 255, 0.87)'; - public static fileUploaderIconHoverColor = 'rgba(255, 255, 255, 0.80)'; - public static get fileUploaderBorderColorError() { - return this.borderColorError; - } - public static fileUploaderBorderColorWarning = '#ffa236'; - public static get fileUploaderDisabledBg() { - return this.btnDisabledBg; - } - public static fileUploaderDisabledBorderColor = 'rgba(255, 255, 255, 0.1)'; - public static get fileUploaderDisabledTextColor() { - return this.textColorDisabled; - } - public static get fileUploaderDisabledLinkColor() { - return this.textColorDisabled; - } - public static get fileUploaderDisabledIconColor() { - return this.textColorDisabled; - } - - public static fileUploaderUploadButtonBg = 'rgba(255, 255, 255, 0.1)'; - public static fileUploaderHoveredBg = 'rgba(255, 255, 255, 0.16)'; - - public static fileUploaderAfterLinkColor = 'rgba(255, 255, 255, 0.54)'; - public static fileUploaderDragOverShadow = '0px 0px 0px 4px #EBEBEB'; - - //#endregion FileUploader - - //#region react-ui-validations - public static get validationsTextColorError() { - return this.errorText; - } - public static validationsTextColorWarning = '#fdd481'; - //#endregion -} - -export const BasicDarkTheme = Object.setPrototypeOf( - exposeGetters(BasicDarkThemeInternal), - BasicLightTheme, -) as typeof BasicDarkThemeInternal; diff --git a/packages/react-ui/internal/themes/BasicLightTheme.ts b/packages/react-ui/internal/themes/BasicTheme.ts similarity index 99% rename from packages/react-ui/internal/themes/BasicLightTheme.ts rename to packages/react-ui/internal/themes/BasicTheme.ts index 2ae1a22315c..cf13fda528b 100644 --- a/packages/react-ui/internal/themes/BasicLightTheme.ts +++ b/packages/react-ui/internal/themes/BasicTheme.ts @@ -1,7 +1,7 @@ +import { createThemeFromClass } from '../../lib/theming/ThemeHelpers'; import * as ColorFunctions from '../../lib/styles/ColorFunctions'; -import { exposeGetters } from '../../lib/theming/ThemeHelpers'; -export class BasicLightThemeInternal { +export class BasicThemeClass { //#region Common variables public static labGrotesqueBaselineCompensation = '1'; public static brand = '#2291ff'; @@ -2484,4 +2484,4 @@ export class BasicLightThemeInternal { //#endregion } -export const BasicLightTheme = exposeGetters(BasicLightThemeInternal); +export const BasicTheme = createThemeFromClass(BasicThemeClass); diff --git a/packages/react-ui/internal/themes/DarkTheme5_0.ts b/packages/react-ui/internal/themes/DarkTheme5_0.ts new file mode 100644 index 00000000000..3e563e7b1f5 --- /dev/null +++ b/packages/react-ui/internal/themes/DarkTheme5_0.ts @@ -0,0 +1,558 @@ +import { markAsDarkTheme, createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; + +import { BasicTheme, BasicThemeClass } from './BasicTheme'; + +export const DarkTheme5_0 = createThemeFromClass( + class DarkTheme5_0 extends (class {} as typeof BasicThemeClass) { + //#region Common variables + public static grayXLight = '#313131'; + public static gray = 'rgba(255, 255, 255, 0.48)'; + public static grayDark = '#e1e1e1'; + public static green = '#23A14A'; + public static greenDark = '#1C8A3F'; + public static red = '#FE4C4C'; + public static redDark = '#DD3333'; + public static yellowXxLight = '#ffe0c3'; + public static yellow = '#ffa236'; + public static yellowDark = '#ea7324'; + public static warningMain = '#ffa236'; + public static get errorMain() { + return this.red; + } + public static errorText = '#FE6C6C'; + public static errorSecondary = '#AB0D0D'; + public static borderColorFocusLight = '#1260ae'; + public static borderColorGrayLight = 'rgba(255, 255, 255, 0.16)'; + public static borderColorDisabled = 'rgba(0, 0, 0, 0.10)'; + public static placeholderColor = 'rgba(255, 255, 255, 0.32)'; + public static placeholderColorLight = 'rgba(255, 255, 255, 0.16)'; + public static textColorDefault = 'rgba(255, 255, 255, 0.865)'; + public static textColorDisabled = 'rgba(255, 255, 255, 0.32)'; + public static textColorDisabledContrast = 'rgba(255, 255, 255, 0.48)'; + public static textColorInvert = 'rgba(255, 255, 255, 0.865)'; + public static bgDisabled = '#434343'; + public static bgDefault = '#1f1f1f'; + public static bgSecondary = '#333333'; + public static bgChecked = '#EBEBEB'; + public static outlineColorFocus = '#EBEBEB'; + public static borderColorFocus = '#EBEBEB'; + public static get borderColorError() { + return this.errorMain; + } + public static fixedPanelShadow = 'none'; + //#endregion Common variables + //#region CloseIcon, CloseButtonIcon + public static closeBtnIconColor = 'rgba(255, 255, 255, 0.32)'; + //#endregion CloseIcon, CloseButtonIcon + //#region Link + public static linkColor = 'rgba(255, 255, 255, 0.87)'; + public static linkHoverColor = '#ffffff'; + public static linkActiveColor = '#c2c2c2'; + + public static linkGrayedColor = 'rgba(255, 255, 255, 0.54)'; + public static linkGrayedHoverColor = '#FFFFFF'; + public static linkGrayedActiveColor = '#C2C2C2'; + + public static linkSuccessColor = '#46CD68'; + public static linkSuccessHoverColor = '#67D881'; + public static get linkSuccessActiveColor() { + return this.green; + } + + public static get linkDangerColor() { + return this.errorText; + } + public static linkDangerHoverColor = '#FE8C8C'; + public static get linkDangerActiveColor() { + return '#ED3F3F'; + } + + public static linkFocusOutlineColor = '#EBEBEB'; + public static linkHoverTextDecoration = 'none'; + + public static get linkDisabledColor() { + return this.textColorDisabled; + } + //#endregion Link + //#region Button + public static btnFocusShadowWidth = '2px'; + public static btnOutlineColorFocus = '#1f1f1f'; + public static btnInsetColor = '#1f1f1f'; + + // default + public static btnDefaultBg = 'rgba(255, 255, 255, 0.1)'; + public static btnDefaultHoverBg = 'rgba(255, 255, 255, 0.16)'; + public static btnDefaultActiveBg = 'rgba(255, 255, 255, 0.1)'; + public static btnDefaultHoverBgStart = 'none'; + public static btnDefaultHoverBgEnd = 'none'; + public static get btnDefaultHoverBorderColor() { + return this.borderColorGrayLight; + } + public static get btnDefaultActiveBorderColor() { + return this.borderColorGrayLight; + } + public static btnDefaultBorderColor = 'rgba(255, 255, 255, 0.16)'; + public static btnDefaultTextColor = 'rgba(255, 255, 255, 0.87)'; + + // success + public static get btnSuccessBg() { + return this.green; + } + public static get btnSuccessBorderColor() { + return this.btnSuccessBg; + } + public static get btnSuccessTextColor() { + return this.textColorDefault; + } + + public static btnSuccessHoverBg = '#26AD50'; + public static get btnSuccessHoverBorderColor() { + return this.btnSuccessHoverBg; + } + + public static get btnSuccessActiveBg() { + return this.greenDark; + } + public static get btnSuccessActiveBorderColor() { + return this.btnSuccessActiveBg; + } + + // primary + public static btnPrimaryBg = '#EBEBEB'; + public static btnPrimaryBorderColor = '#3D3D3D'; + + public static btnPrimaryHoverBg = '#FFFFFF'; + public static btnPrimaryActiveBg = '#C2C2C2'; + public static btnPrimaryActiveBorderColor = '#1261ae'; + + public static btnPrimaryTextColor = 'rgba(0, 0, 0, 0.865)'; + + // danger + public static get btnDangerBg() { + return this.errorMain; + } + public static get btnDangerBorderColor() { + return this.btnDangerBg; + } + + public static btnDangerHoverBg = '#ED3F3F'; + public static get btnDangerHoverBorderColor() { + return this.btnDangerHoverBg; + } + public static get btnDangerTextColor() { + return this.textColorDefault; + } + public static btnDangerActiveBg = '#DD3333'; + public static get btnDangerActiveBorderColor() { + return this.btnDangerActiveBg; + } + + // pay + public static btnPayBg = '#FCB73E'; + public static get btnPayBorderColor() { + return this.btnPayBg; + } + public static btnPayHoverBg = '#FCC660'; + public static get btnPayHoverBorderColor() { + return this.btnPayHoverBg; + } + public static btnPayTextColor = '#222222'; + public static btnPayActiveBg = '#F69912'; + public static get btnPayActiveBorderColor() { + return this.btnPayActiveBg; + } + + // backless + public static btnBacklessBg = 'transparent'; + public static btnBacklessHoverBg = 'rgba(255, 255, 255, 0.1)'; + public static btnBacklessActiveBg = 'rgba(255, 255, 255, 0.06)'; + public static btnBacklessBorderColor = 'rgba(255, 255, 255, 0.16)'; + public static btnBacklessDisabledBorderColor = 'rgba(255, 255, 255, 0.06)'; + public static btnBacklessHoverBorderColor = 'rgba(255, 255, 255, 0.1)'; + public static btnBacklessTextColor = 'rgba(255, 255, 255, 0.87)'; + public static btnBacklessActiveBorderColor = 'rgba(255, 255, 255, 0.1)'; + + // text + public static btnTextBg = 'transparent'; + public static btnTextHoverBg = 'rgba(255, 255, 255, 0.1)'; + public static btnTextActiveBg = 'rgba(255, 255, 255, 0.06)'; + public static btnTextBorderColor = 'transparent'; + + // warning, error + public static btnWarningSecondary = 'rgba(212, 100, 33, 1)'; + public static get btnErrorSecondary() { + return this.errorSecondary; + } + + // disabled + public static btnDisabledBg = 'rgba(255, 255, 255, 0.04)'; + public static get btnDisabledTextColor() { + return this.textColorDisabled; + } + public static btnDisabledBorderColor = 'transparent'; + + // checked + public static btnCheckedBg = '#EBEBEB'; + public static btnCheckedTextColor = 'rgba(0, 0, 0, 0.865)'; + public static btnCheckedDisabledBg = 'rgba(255, 255, 255, 0.32) !important'; + public static btnCheckedDisabledColor = 'rgba(0, 0, 0, 0.48)'; + //#endregion Button + //#region Tooltip + public static tooltipCloseBtnColor = 'rgba(255, 255, 255, 0.32)'; + public static tooltipCloseBtnHoverColor = 'rgba(255, 255, 255, 0.865)'; + public static tooltipBg = '#333333'; + //#endregion Tooltip + //#region Modal + public static modalBg = '#222'; + public static modalBackBg = '#000'; + public static modalBackOpacity = '0.7'; + + public static modalCloseButtonColor = 'rgba(255, 255, 255, 0.32)'; + public static modalCloseButtonHoverColor = 'rgba(255, 255, 255, 0.865)'; + + public static modalFooterBg = '#222222'; + public static get modalFooterTextColor() { + return this.textColorDefault; + } + public static get modalBodyTextColor() { + return this.textColorDefault; + } + public static get modalFixedPanelShadow() { + return this.fixedPanelShadow; + } + public static get modalFixedHeaderBg() { + return this.modalBg; + } + public static modalSeparatorBorderBottom = 'solid 1px rgba(255, 255, 255, 0.1)'; + //#endregion Modal + //#region SidePage + public static sidePageFooterPanelBg = ' #222222'; + public static sidePageBackingBg = '#000'; + public static sidePageBgDefault = '#222'; + public static sidePageBackingBgOpacity = '0.7'; + public static sidePageCloseButtonColor = 'rgba(255, 255, 255, 0.32)'; + public static sidePageCloseButtonHoverColor = 'rgba(255, 255, 255, 0.865)'; + public static get sidePageBodyTextColor() { + return this.textColorDefault; + } + public static get sidePageFooterTextColor() { + return this.textColorDefault; + } + public static get sidePageFixedPanelShadow() { + return this.fixedPanelShadow; + } + //#endregion SidePage + + //#region Select + public static selectMenuArrowColor = 'rgba(255, 255, 255, 0.54)'; + public static get selectPlaceholderColorDisabled() { + return this.textColorDisabled; + } + public static get selectMenuArrowColorDisabled() { + return this.textColorDisabled; + } + //#endregion Select + //#region Calendar + public static get calendarCellWeekendColor() { + return this.errorText; + } + public static calendarCellTodayBorder = '1px solid rgba(255, 255, 255, 0.48)'; + public static calendarCellSelectedBgColor = 'rgba(255, 255, 255, 0.1)'; + public static calendarMonthTitleBorderBottomColor = 'rgba(255, 255, 255, 0.08)'; + public static calendarCellHoverBgColor = 'rgba(255, 255, 255, 0.06)'; + public static calendarCellHoverColor = 'rgba(255, 255, 255, 0.87)'; + public static calendarCellSelectedFontColor = 'rgba(255, 255, 255, 0.87)'; + //#endregion Calendar + //#region Paging + public static pagingPageLinkColor = 'rgba(255, 255, 255, 0.87)'; + public static pagingPageLinkHoverBg = 'rgba(255, 255, 255, 0.06)'; + public static pagingPageLinkActiveBg = 'rgba(255, 255, 255, 0.1)'; + public static pagingPageLinkDisabledActiveBg = 'rgba(255, 255, 255, 0.08)'; + public static pagingForwardLinkColor = 'rgba(255, 255, 255, 0.87)'; + public static get pagingForwardLinkDisabledColor() { + return this.linkDisabledColor; + } + //#endregion Paging + //#region Toast + public static toastLinkColor = 'rgba(44, 44, 44, 1.0)'; + public static toastCloseColor = 'rgba(0, 0, 0, 0.32)'; + + public static toastColor = 'rgba(44, 44, 44, 1.0)'; + public static toastBg = 'rgba(255, 255, 255, 0.8)'; + public static toastLinkBgHover = 'rgba(255, 255, 255, 0.87)'; + public static toastLinkBgActive = 'rgba(0, 0, 0, 0.16)'; + public static toastCloseHoverColor = 'rgba(0, 0, 0, 0.87)'; + //#endregion Toast + + //#region Tab, Tabs + public static tabColorHover = 'rgba(255, 255, 255, 0.16)'; + //#endregion Tab, Tabs + //#region Menu + public static get menuBgDefault() { + return this.bgSecondary; + } + // menuItem + public static menuItemTextColor = 'rgba(255, 255, 255, 0.87)'; + public static menuItemHoverBg = 'rgba(255, 255, 255, 0.1)'; + public static menuItemSelectedBg = 'rgba(255, 255, 255, 0.16)'; + + public static menuItemCommentColor = 'rgba(255, 255, 255, 0.54)'; + public static menuItemCommentOpacity = '1'; + public static get menuItemDisabledColor() { + return this.textColorDisabled; + } + // menuHeader + public static get menuHeaderColor() { + return this.gray; + } + // menuFooter + public static get menuFooterColor() { + return this.menuHeaderColor; + } + //menuSeparator + public static menuSeparatorBorderColor = 'rgba(255, 255, 255, 0.1)'; + //#endregion Menu + //#region Toggle + public static toggleBorderColor = 'rgba(255, 255, 255, 0.32)'; + public static toggleBaseBg = 'none'; + + public static toggleOutlineColorFocus = '#1F1F1F'; + + // idle + public static toggleContainerBg = 'rgba(255, 255, 255, 0.1)'; + public static toggleContainerBoxShadow = 'inset 0 0 0 1px rgba(255, 255, 255, 0.06)'; + public static toggleHandleBg = 'rgba(255, 255, 255, 0.32)'; + public static toggleHandleBoxShadow = 'inset 0 0 0 1px rgba(255, 255, 255, 0.06)'; + + // idle :hover + public static toggleBgHover = 'rgba(255, 255, 255, 0.16)'; + public static toggleContainerBoxShadowHover = 'inset 0 0 0 1px rgba(255, 255, 255, 0.06)'; + public static toggleHandleBgHover = 'rgba(255, 255, 255, 0.32)'; + public static toggleHandleBoxShadowHover = 'inset 0 0 0 1px rgba(255, 255, 255, 0.06)'; + + // checked + public static toggleBgChecked = 'rgba(255, 255, 255, 0.1)'; + public static toggleContainerBoxShadowChecked = 'inset 0 0 0 1px rgba(255, 255, 255, 0.06)'; + public static toggleHandleBgChecked = '#EBEBEB'; + public static toggleHandleBoxShadowChecked = 'none'; + + // checked :hover + public static get toggleContainerBgCheckedHover() { + return this.toggleContainerBgHover; + } + public static toggleContainerBoxShadowCheckedHover = 'inset 0 0 0 1px rgba(255, 255, 255, 0.06)'; + public static toggleHandleBgCheckedHover = '#FFFFFF'; + public static toggleHandleBoxShadowCheckedHover = 'none'; + + // disabled + public static get toggleBgDisabled() { + return this.bgDisabled; + } + public static toggleDisabledHandleBg = '#525252'; + + public static toggleContainerBgDisabled = 'rgba(255, 255, 255, 0.04)'; + public static toggleContainerBoxShadowDisabled = 'none'; + public static toggleHandleBgDisabled = 'rgba(0, 0, 0, 0.16)'; + public static toggleHandleBoxShadowDisabled = 'none'; + + // disabled checked + public static toggleContainerBgDisabledChecked = 'rgba(255, 255, 255, 0.06)'; + public static toggleContainerBoxShadowDisabledChecked = 'none'; + public static toggleHandleBgDisabledChecked = 'rgba(0, 0, 0, 0.16)'; + public static toggleHandleBoxShadowDisabledChecked = 'none'; + public static toggleBgDisabledChecked = 'rgba(255, 255, 255, 0.48)'; + + //#endregion Toggle + //#region Token + public static tokenShadowDisabled = ''; + public static tokenBorderColorDisabled = 'transparent'; + public static get tokenTextColorDisabled() { + return this.textColorDisabled; + } + + public static tokenColor = 'rgba(255, 255, 255, 0.865)'; + public static tokenBg = 'rgba(255, 255, 255, 0.1)'; + public static tokenBorderColor = 'rgba(255, 255, 255, 0.06)'; + public static tokenColorHover = 'rgba(255, 255, 255, 0.87)'; + public static tokenBgHover = 'rgba(255, 255, 255, 0.16)'; + public static tokenBorderColorHover = 'rgba(255, 255, 255, 0.06)'; + public static tokenColorActive = 'rgba(0, 0, 0, 0.87)'; + public static tokenBgActive = '#EBEBEB'; + public static tokenBorderColorActive = 'transparent'; + //#endregion Token + //#region Input + public static inputBg = 'rgba(255, 255, 255, 0.1)'; + public static get inputBorderTopColor() { + return this.inputBorderColor; + } + public static inputColorScheme = 'dark'; + public static inputBlinkColor = '#EBEBEB'; + public static inputTextColor = 'rgba(255, 255, 255, 0.865)'; + public static inputBorderColor = 'rgba(255, 255, 255, 0.06)'; + public static inputBackgroundClip = 'border-box'; + public static inputBorderColorHover = 'rgba(255, 255, 255, 0.16)'; + public static inputBorderColorFocus = 'rgba(235, 235, 235, 1)'; + public static get inputFocusShadow() { + return `0 0 0 1px ${this.inputBorderColorFocus}`; + } + + public static inputBorderColorWarning = 'rgba(252, 183, 62, 1)'; + public static get inputBorderColorError() { + return this.borderColorError; + } + public static inputOutlineWidth = '1px'; + + public static get inputTextColorDisabled() { + return this.textColorDisabled; + } + public static get inputIconColorDisabled() { + return this.textColorDisabled; + } + public static get inputPlaceholderColorDisabled() { + return this.textColorDisabled; + } + public static inputDisabledBg = 'rgba(255, 255, 255, 0.04)'; + public static inputDisabledBorderColor = 'rgba(255, 255, 255, 0.04)'; + //#endregion Input + //#region DateSelect + public static dateSelectMenuItemBgActive = 'rgba(255, 255, 255, 0.06)'; + public static dateSelectMenuItemBgSelected = 'rgba(255, 255, 255, 0.1)'; + //#endregion DateSelect + //#region DateInput + public static dateInputComponentSelectedBgColor = ''; + public static dateInputComponentSelectedTextColor = ''; + //#endregion DateInput + //#region Hint + public static hintColor = 'rgba(44, 44, 44, 1.0)'; + public static hintBgColor = 'rgba(255, 255, 255, 0.8)'; + //#endregion Hint + //#region Loader + public static loaderBg = 'rgba(51, 51, 51, 0.8)'; + //#endregion + //#region GlobalLoader + public static globalLoaderColor = 'rgba(238, 80, 66, 1)'; + //#endregion GlobalLoader + //#region TextArea + public static get textareaBg() { + return this.inputBg; + } + public static get textareaBorderColor() { + return this.inputBorderColor; + } + public static get textareaTextColorDisabled() { + return this.inputTextColorDisabled; + } + public static get textareaPlaceholderColorDisabled() { + return this.textColorDisabled; + } + public static get textareaBorderTopColor() { + return this.textareaBorderColor; + } + public static get textareaDisabledBorderColor() { + return this.inputDisabledBorderColor; + } + public static get textareaBorderColorFocus() { + return this.inputBorderColorFocus; + } + //#endregion TextArea + //#region PasswordInput + public static get passwordInputVisibilityIconColor() { + return this.gray; + } + public static passwordInputVisibilityIconOpacity = '1'; + public static get passwordInputVisibilityIconHoverColor() { + return this.textColorDefault; + } + //#endregion PasswordInput + //#region Radio + public static radioBgColor = 'rgba(255, 255, 255, 0.04)'; + public static radioBorderColor = 'rgba(255, 255, 255, 0.32)'; + + public static radioFocusShadow = 'inset 0 0 0 1px rgba(0, 0, 0, 0.87)'; + + public static radioDisabledBg = 'rgba(255, 255, 255, 0.04)'; + public static radioDisabledShadow = 'none'; + public static radioCheckedDisabledBulletBg = 'rgba(255, 255, 255, 0.16)'; + + public static radioCheckedBgColor = '#FFFFFF'; + public static radioCheckedBulletColor = 'rgba(0, 0, 0, 0.87)'; + public static radioCheckedBorderColor = 'transparent'; + //#endregion Radio + //#region Checkbox + public static checkboxBg = 'rgba(255, 255, 255, 0.04)'; + public static get checkboxShadow() { + return `0 0 0 ${this.checkboxBorderWidth} rgba(255, 255, 255, 0.32)`; + } + public static get checkboxShadowHover() { + return this.checkboxShadow; + } + + public static get checkboxCheckedBg() { + return this.bgChecked; + } + public static checkboxCheckedColor = 'rgba(0, 0, 0, 0.87)'; + public static checkboxHoverBg = 'rgba(255, 255, 255, 0.16)'; + public static get checkboxCheckedHoverBg() { + return this.checkboxCheckedBg; + } + public static checkboxOutlineColorFocus = 'rgba(0, 0, 0, 0.87)'; + public static checkboxBgDisabled = 'rgba(255, 255, 255, 0.04)'; + public static checkboxShadowDisabled = 'none'; + //#endregion Checkbox + //#region TokenInput + public static inputDisabledBackgroundClip = 'border-box'; + //#endregion TokenInput + //#region Switcher + public static get switcherButtonDisabledBorderColor() { + return this.borderColorDisabled; + } + public static switcherButtonCheckedDisabledShadow = 'none'; + //#endregion Switcher + //#region FileUploader + public static fileUploaderBg = 'none'; + public static fileUploaderTextColorDefault = '#fff'; + public static fileUploaderBorderColor = 'rgba(255, 255, 255, 0.32)'; + public static get fileUploaderBorderColorFocus() { + return this.btnBorderColorFocus; + } + public static fileUploaderLinkColor = 'rgba(255, 255, 255, 0.87)'; + public static fileUploaderIconColor = 'rgba(255, 255, 255, 0.87)'; + public static fileUploaderIconHoverColor = 'rgba(255, 255, 255, 0.80)'; + public static get fileUploaderBorderColorError() { + return this.borderColorError; + } + public static fileUploaderBorderColorWarning = '#ffa236'; + public static get fileUploaderDisabledBg() { + return this.btnDisabledBg; + } + public static fileUploaderDisabledBorderColor = 'rgba(255, 255, 255, 0.1)'; + public static get fileUploaderDisabledTextColor() { + return this.textColorDisabled; + } + public static get fileUploaderDisabledLinkColor() { + return this.textColorDisabled; + } + public static get fileUploaderDisabledIconColor() { + return this.textColorDisabled; + } + + public static fileUploaderUploadButtonBg = 'rgba(255, 255, 255, 0.1)'; + public static fileUploaderHoveredBg = 'rgba(255, 255, 255, 0.16)'; + + public static fileUploaderAfterLinkColor = 'rgba(255, 255, 255, 0.54)'; + public static fileUploaderDragOverShadow = '0px 0px 0px 4px #EBEBEB'; + + //#endregion FileUploader + + //#region react-ui-validations + public static get validationsTextColorError() { + return this.errorText; + } + public static validationsTextColorWarning = '#fdd481'; + //#endregion + }, + { + prototypeTheme: BasicTheme, + themeMarkers: [markAsDarkTheme, markThemeVersion(5.0)], + }, +); diff --git a/packages/react-ui/internal/themes/DarkTheme5_1.ts b/packages/react-ui/internal/themes/DarkTheme5_1.ts new file mode 100644 index 00000000000..a9f33070e68 --- /dev/null +++ b/packages/react-ui/internal/themes/DarkTheme5_1.ts @@ -0,0 +1,9 @@ +import { createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; + +import { BasicThemeClass } from './BasicTheme'; +import { DarkTheme5_0 } from './DarkTheme5_0'; + +export const DarkTheme5_1 = createThemeFromClass(class DarkTheme5_1 extends (class {} as typeof BasicThemeClass) {}, { + prototypeTheme: DarkTheme5_0, + themeMarkers: [markThemeVersion(5.1)], +}); diff --git a/packages/react-ui/internal/themes/LightTheme5_0.ts b/packages/react-ui/internal/themes/LightTheme5_0.ts new file mode 100644 index 00000000000..22f71ff5918 --- /dev/null +++ b/packages/react-ui/internal/themes/LightTheme5_0.ts @@ -0,0 +1,8 @@ +import { createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; + +import { BasicTheme, BasicThemeClass } from './BasicTheme'; + +export const LightTheme5_0 = createThemeFromClass(class LightTheme5_0 extends (class {} as typeof BasicThemeClass) {}, { + prototypeTheme: BasicTheme, + themeMarkers: [markThemeVersion(5.0)], +}); diff --git a/packages/react-ui/internal/themes/LightTheme5_1.ts b/packages/react-ui/internal/themes/LightTheme5_1.ts new file mode 100644 index 00000000000..660fb09b002 --- /dev/null +++ b/packages/react-ui/internal/themes/LightTheme5_1.ts @@ -0,0 +1,9 @@ +import { createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; + +import { BasicThemeClass } from './BasicTheme'; +import { LightTheme5_0 } from './LightTheme5_0'; + +export const LightTheme5_1 = createThemeFromClass(class LightTheme5_1 extends (class {} as typeof BasicThemeClass) {}, { + prototypeTheme: LightTheme5_0, + themeMarkers: [markThemeVersion(5.1)], +}); diff --git a/packages/react-ui/lib/theming/Theme.ts b/packages/react-ui/lib/theming/Theme.ts index edc85faf5d8..cc19058a9fb 100644 --- a/packages/react-ui/lib/theming/Theme.ts +++ b/packages/react-ui/lib/theming/Theme.ts @@ -1,4 +1,4 @@ -import { BasicLightTheme } from '../../internal/themes/BasicLightTheme'; +import { BasicThemeClass } from '../../internal/themes/BasicTheme'; -export type Theme = Readonly; -export type ThemeIn = Partial; +export type Theme = Readonly; +export type ThemeIn = Partial; diff --git a/packages/react-ui/lib/theming/ThemeFactory.ts b/packages/react-ui/lib/theming/ThemeFactory.ts index f344c787beb..e623a2a04e2 100644 --- a/packages/react-ui/lib/theming/ThemeFactory.ts +++ b/packages/react-ui/lib/theming/ThemeFactory.ts @@ -1,4 +1,4 @@ -import { BasicLightTheme } from '../../internal/themes/BasicLightTheme'; +import { LIGHT_THEME } from '../../lib/theming/themes/LightTheme'; import { isNonNullable, NoInfer } from '../utils'; import { Theme, ThemeIn } from './Theme'; @@ -6,21 +6,21 @@ import { findPropertyDescriptor, REACT_UI_THEME_MARKERS } from './ThemeHelpers'; export class ThemeFactory { public static create(theme: ThemeIn & NoInfer, baseTheme?: Theme): Readonly> { - const base = baseTheme || BasicLightTheme; + const base = baseTheme || LIGHT_THEME; return this.constructTheme(base, theme); } public static overrideBaseTheme(theme: Theme) { // copying theme variables - ThemeFactory.getKeys(BasicLightTheme).forEach((variableName) => { + ThemeFactory.getKeys(LIGHT_THEME).forEach((variableName) => { const descriptor = findPropertyDescriptor(theme, variableName); - Object.defineProperty(BasicLightTheme, variableName, descriptor); + Object.defineProperty(LIGHT_THEME, variableName, descriptor); }); // copying theme markers Object.values(REACT_UI_THEME_MARKERS).forEach((marker) => { const descriptor = findPropertyDescriptor(theme, marker.key); - Object.defineProperty(BasicLightTheme, marker.key, descriptor); + Object.defineProperty(LIGHT_THEME, marker.key, descriptor); }); } diff --git a/packages/react-ui/lib/theming/ThemeHelpers.ts b/packages/react-ui/lib/theming/ThemeHelpers.ts index a1949a2b680..2455dfc88f9 100644 --- a/packages/react-ui/lib/theming/ThemeHelpers.ts +++ b/packages/react-ui/lib/theming/ThemeHelpers.ts @@ -1,3 +1,4 @@ +import { BasicThemeClass } from '../../internal/themes/BasicTheme'; import { isNonNullable } from '../utils'; import { Theme, ThemeIn } from './Theme'; @@ -26,6 +27,10 @@ export const REACT_UI_THEME_MARKERS = { key: '__IS_REACT_UI_THEME_2022__', value: true, }, + themeVersion: { + key: '__REACT_UI_THEME_VERSION__', + value: 0, + }, }; // backward compatible @@ -58,11 +63,27 @@ export const markAsTheme2022: Marker = (theme) => { }); }; +export const markThemeVersion: (version: number) => Marker = (version) => (theme) => { + return Object.create(theme, { + [REACT_UI_THEME_MARKERS.themeVersion.key]: { + value: version, + writable: false, + enumerable: false, + configurable: false, + }, + }); +}; + export const isTheme2022 = (theme: Theme | ThemeIn): boolean => { // @ts-expect-error: internal value. return theme[REACT_UI_THEME_MARKERS.theme2022.key] === REACT_UI_THEME_MARKERS.theme2022.value; }; +export const isThemeVersionGreaterOrEqual = (theme: Theme | ThemeIn, version: number): boolean => { + // @ts-expect-error: internal value. + return theme[REACT_UI_THEME_MARKERS.themeVersion.key] >= version; +}; + export function findPropertyDescriptor(theme: Theme, propName: string) { // TODO: Rewrite for loop. // TODO: Enable `no-param-reassign` rule. @@ -76,9 +97,24 @@ export function findPropertyDescriptor(theme: Theme, propName: string) { } export function applyMarkers(theme: Readonly, markers: Markers) { - let markedTheme: Readonly = theme; - markers.forEach((marker) => { - markedTheme = marker(theme); - }); - return markedTheme; + return markers.reduce((markedTheme, marker) => { + return marker(markedTheme); + }, Object.create(theme)) as typeof theme; +} + +export function createThemeFromClass( + themeObject: typeof BasicThemeClass, + options?: { + prototypeTheme?: Theme; + themeMarkers?: Markers; + }, +): Theme { + const theme = exposeGetters(themeObject); + const { prototypeTheme, themeMarkers = [] } = options || {}; + + if (prototypeTheme) { + Object.setPrototypeOf(theme, prototypeTheme); + } + + return applyMarkers(theme as Theme, themeMarkers); } diff --git a/packages/react-ui/lib/theming/__tests__/Theming-test.tsx b/packages/react-ui/lib/theming/__tests__/Theming-test.tsx index 0041164b018..e3d128cee67 100644 --- a/packages/react-ui/lib/theming/__tests__/Theming-test.tsx +++ b/packages/react-ui/lib/theming/__tests__/Theming-test.tsx @@ -2,11 +2,12 @@ import { render } from '@testing-library/react'; import React from 'react'; import { ThemeContext } from '../ThemeContext'; -import { applyMarkers, exposeGetters, Marker, REACT_UI_THEME_MARKERS } from '../ThemeHelpers'; +import { applyMarkers, createThemeFromClass, Marker, REACT_UI_THEME_MARKERS } from '../ThemeHelpers'; import { ThemeFactory } from '../ThemeFactory'; import { Theme } from '../Theme'; -import { BasicLightThemeInternal, BasicLightTheme } from '../../../internal/themes/BasicLightTheme'; +import { LIGHT_THEME } from '../../../lib/theming/themes/LightTheme'; import { AnyObject } from '../../utils'; +import { BasicTheme, BasicThemeClass } from '../../../internal/themes/BasicTheme'; const TEST_MARKERS = { test: { @@ -34,9 +35,12 @@ const getConsumedTheme = () => { // test theme const myTheme = { brand: 'custom', bgDefault: 'custom' } as const; -const TestTheme = Object.setPrototypeOf( - exposeGetters({ bgDefault: 'default', bgSecondary: 'default' }), - BasicLightTheme, +const TestTheme = createThemeFromClass( + class extends (class {} as typeof BasicThemeClass) { + public static bgDefault = 'default'; + public static bgSecondary = 'default'; + }, + { prototypeTheme: BasicTheme }, ); // test marker @@ -63,7 +67,7 @@ describe('Theming', () => { const theme = ThemeFactory.create(myTheme); expect(theme.brand).toEqual(myTheme.brand); - expect(theme.black).toEqual(BasicLightTheme.black); + expect(theme.black).toEqual(LIGHT_THEME.black); }); test('with args [theme, baseTheme]', () => { const theme = ThemeFactory.create(myTheme, TestTheme); @@ -93,7 +97,7 @@ describe('Theming', () => { }); test('getKeys()', () => { const keys_1 = ThemeFactory.getKeys(TestTheme); - const keys_2 = ThemeFactory.getKeys(BasicLightThemeInternal); + const keys_2 = ThemeFactory.getKeys(LIGHT_THEME); expect(keys_1).toEqual(keys_2); }); diff --git a/packages/react-ui/lib/theming/themes/DarkTheme.ts b/packages/react-ui/lib/theming/themes/DarkTheme.ts index 9a9ee2d4945..5bb4818d073 100644 --- a/packages/react-ui/lib/theming/themes/DarkTheme.ts +++ b/packages/react-ui/lib/theming/themes/DarkTheme.ts @@ -1,5 +1 @@ -import { ThemeFactory } from '../ThemeFactory'; -import { BasicDarkTheme } from '../../../internal/themes/BasicDarkTheme'; -import { applyMarkers, markAsDarkTheme, markAsTheme2022 } from '../ThemeHelpers'; - -export const DARK_THEME = applyMarkers(ThemeFactory.create({}, BasicDarkTheme), [markAsTheme2022, markAsDarkTheme]); +export { DarkTheme5_0 as DARK_THEME } from '../../../internal/themes/DarkTheme5_0'; diff --git a/packages/react-ui/lib/theming/themes/DarkTheme2022_0.ts b/packages/react-ui/lib/theming/themes/DarkTheme2022_0.ts deleted file mode 100644 index 613140c3ccf..00000000000 --- a/packages/react-ui/lib/theming/themes/DarkTheme2022_0.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { ThemeFactory } from '../ThemeFactory'; - -import { DARK_THEME } from './DarkTheme'; - -export const DARK_THEME_2022_0 = ThemeFactory.create({}, DARK_THEME); diff --git a/packages/react-ui/lib/theming/themes/DarkTheme5_0.ts b/packages/react-ui/lib/theming/themes/DarkTheme5_0.ts new file mode 100644 index 00000000000..33f88ffe5ae --- /dev/null +++ b/packages/react-ui/lib/theming/themes/DarkTheme5_0.ts @@ -0,0 +1 @@ +export { DarkTheme5_0 as DARK_THEME_5_0 } from '../../../internal/themes/DarkTheme5_0'; diff --git a/packages/react-ui/lib/theming/themes/DarkTheme5_1.ts b/packages/react-ui/lib/theming/themes/DarkTheme5_1.ts new file mode 100644 index 00000000000..5f3db09e7aa --- /dev/null +++ b/packages/react-ui/lib/theming/themes/DarkTheme5_1.ts @@ -0,0 +1 @@ +export { DarkTheme5_1 as DARK_THEME_5_1 } from '../../../internal/themes/DarkTheme5_1'; diff --git a/packages/react-ui/lib/theming/themes/LightTheme.ts b/packages/react-ui/lib/theming/themes/LightTheme.ts index 231723fc86f..e182922ced3 100644 --- a/packages/react-ui/lib/theming/themes/LightTheme.ts +++ b/packages/react-ui/lib/theming/themes/LightTheme.ts @@ -1,5 +1 @@ -import { ThemeFactory } from '../ThemeFactory'; -import { BasicLightTheme } from '../../../internal/themes/BasicLightTheme'; -import { applyMarkers, markAsTheme2022 } from '../ThemeHelpers'; - -export const LIGHT_THEME = applyMarkers(ThemeFactory.create({}, BasicLightTheme), [markAsTheme2022]); +export { LightTheme5_0 as LIGHT_THEME } from '../../../internal/themes/LightTheme5_0'; diff --git a/packages/react-ui/lib/theming/themes/LightTheme2022_0.ts b/packages/react-ui/lib/theming/themes/LightTheme2022_0.ts deleted file mode 100644 index b4c61948691..00000000000 --- a/packages/react-ui/lib/theming/themes/LightTheme2022_0.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { ThemeFactory } from '../ThemeFactory'; - -import { LIGHT_THEME } from './LightTheme'; - -export const LIGHT_THEME_2022_0 = ThemeFactory.create({}, LIGHT_THEME); diff --git a/packages/react-ui/lib/theming/themes/LightTheme5_0.ts b/packages/react-ui/lib/theming/themes/LightTheme5_0.ts new file mode 100644 index 00000000000..dd64c1f0d0b --- /dev/null +++ b/packages/react-ui/lib/theming/themes/LightTheme5_0.ts @@ -0,0 +1 @@ +export { LightTheme5_0 as LIGHT_THEME_5_0 } from '../../../internal/themes/LightTheme5_0'; diff --git a/packages/react-ui/lib/theming/themes/LightTheme5_1.ts b/packages/react-ui/lib/theming/themes/LightTheme5_1.ts new file mode 100644 index 00000000000..49010a780f1 --- /dev/null +++ b/packages/react-ui/lib/theming/themes/LightTheme5_1.ts @@ -0,0 +1 @@ +export { LightTheme5_1 as LIGHT_THEME_5_1 } from '../../../internal/themes/LightTheme5_1'; From 9fe86e1f6173224d972089b24029729ee52eb5c3 Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Tue, 4 Feb 2025 13:50:36 +0500 Subject: [PATCH 02/27] chore: wips --- .../themes}/__tests__/Theming-test.tsx | 17 +++--- packages/react-ui/lib/theming/ThemeHelpers.ts | 16 ----- .../theming/themes/__tests__/Themes-test.tsx | 61 +++++++++++++++++++ 3 files changed, 69 insertions(+), 25 deletions(-) rename packages/react-ui/{lib/theming => internal/themes}/__tests__/Theming-test.tsx (85%) create mode 100644 packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx diff --git a/packages/react-ui/lib/theming/__tests__/Theming-test.tsx b/packages/react-ui/internal/themes/__tests__/Theming-test.tsx similarity index 85% rename from packages/react-ui/lib/theming/__tests__/Theming-test.tsx rename to packages/react-ui/internal/themes/__tests__/Theming-test.tsx index e3d128cee67..3bf700c39af 100644 --- a/packages/react-ui/lib/theming/__tests__/Theming-test.tsx +++ b/packages/react-ui/internal/themes/__tests__/Theming-test.tsx @@ -1,13 +1,12 @@ import { render } from '@testing-library/react'; import React from 'react'; -import { ThemeContext } from '../ThemeContext'; -import { applyMarkers, createThemeFromClass, Marker, REACT_UI_THEME_MARKERS } from '../ThemeHelpers'; -import { ThemeFactory } from '../ThemeFactory'; -import { Theme } from '../Theme'; -import { LIGHT_THEME } from '../../../lib/theming/themes/LightTheme'; -import { AnyObject } from '../../utils'; -import { BasicTheme, BasicThemeClass } from '../../../internal/themes/BasicTheme'; +import { ThemeContext } from '../../../lib/theming/ThemeContext'; +import { applyMarkers, createThemeFromClass, Marker, REACT_UI_THEME_MARKERS } from '../../../lib/theming/ThemeHelpers'; +import { ThemeFactory } from '../../../lib/theming/ThemeFactory'; +import { Theme } from '../../../lib/theming/Theme'; +import { AnyObject } from '../../../lib/utils'; +import { BasicTheme, BasicThemeClass } from '../BasicTheme'; const TEST_MARKERS = { test: { @@ -67,7 +66,7 @@ describe('Theming', () => { const theme = ThemeFactory.create(myTheme); expect(theme.brand).toEqual(myTheme.brand); - expect(theme.black).toEqual(LIGHT_THEME.black); + expect(theme.black).toEqual(BasicTheme.black); }); test('with args [theme, baseTheme]', () => { const theme = ThemeFactory.create(myTheme, TestTheme); @@ -97,7 +96,7 @@ describe('Theming', () => { }); test('getKeys()', () => { const keys_1 = ThemeFactory.getKeys(TestTheme); - const keys_2 = ThemeFactory.getKeys(LIGHT_THEME); + const keys_2 = ThemeFactory.getKeys(BasicTheme); expect(keys_1).toEqual(keys_2); }); diff --git a/packages/react-ui/lib/theming/ThemeHelpers.ts b/packages/react-ui/lib/theming/ThemeHelpers.ts index 2455dfc88f9..7d54fcf03ec 100644 --- a/packages/react-ui/lib/theming/ThemeHelpers.ts +++ b/packages/react-ui/lib/theming/ThemeHelpers.ts @@ -52,17 +52,6 @@ export const markAsDarkTheme: Marker = (theme) => { }); }; -export const markAsTheme2022: Marker = (theme) => { - return Object.create(theme, { - [REACT_UI_THEME_MARKERS.theme2022.key]: { - value: REACT_UI_THEME_MARKERS.theme2022.value, - writable: false, - enumerable: false, - configurable: false, - }, - }); -}; - export const markThemeVersion: (version: number) => Marker = (version) => (theme) => { return Object.create(theme, { [REACT_UI_THEME_MARKERS.themeVersion.key]: { @@ -74,11 +63,6 @@ export const markThemeVersion: (version: number) => Marker = (version) => (theme }); }; -export const isTheme2022 = (theme: Theme | ThemeIn): boolean => { - // @ts-expect-error: internal value. - return theme[REACT_UI_THEME_MARKERS.theme2022.key] === REACT_UI_THEME_MARKERS.theme2022.value; -}; - export const isThemeVersionGreaterOrEqual = (theme: Theme | ThemeIn, version: number): boolean => { // @ts-expect-error: internal value. return theme[REACT_UI_THEME_MARKERS.themeVersion.key] >= version; diff --git a/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx b/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx new file mode 100644 index 00000000000..2e7b3babce0 --- /dev/null +++ b/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx @@ -0,0 +1,61 @@ +import { isDarkTheme, isThemeVersionGreaterOrEqual } from '../../ThemeHelpers'; +import { DARK_THEME } from '../DarkTheme'; +import { DARK_THEME_5_0 } from '../DarkTheme5_0'; +import { DARK_THEME_5_1 } from '../DarkTheme5_1'; +import { LIGHT_THEME } from '../LightTheme'; +import { LIGHT_THEME_5_0 } from '../LightTheme5_0'; +import { LIGHT_THEME_5_1 } from '../LightTheme5_1'; + +describe('Themes', () => { + describe('LIGHT_THEME', () => { + test('should be equal LIGHT_THEME_5_0', () => { + expect(LIGHT_THEME).toBe(LIGHT_THEME_5_0); + }); + }); + + describe('LIGHT_THEME_5_0', () => { + test('should be 5.0 version', () => { + expect(isThemeVersionGreaterOrEqual(LIGHT_THEME_5_0, 5.0)).toBe(true); + expect(isThemeVersionGreaterOrEqual(LIGHT_THEME_5_0, 5.1)).toBe(false); + }); + test('should not be dark', () => { + expect(isDarkTheme(LIGHT_THEME_5_0)).toBe(false); + }); + }); + + describe('LIGHT_THEME_5_1', () => { + test('should be 5.1 version', () => { + expect(isThemeVersionGreaterOrEqual(LIGHT_THEME_5_1, 5.0)).toBe(true); + expect(isThemeVersionGreaterOrEqual(LIGHT_THEME_5_1, 5.1)).toBe(true); + }); + test('should not be dark', () => { + expect(isDarkTheme(LIGHT_THEME_5_1)).toBe(false); + }); + }); + + describe('DARK_THEME', () => { + test('should be equal DARK_THEME_5_0', () => { + expect(DARK_THEME).toBe(DARK_THEME_5_0); + }); + }); + + describe('DARK_THEME_5_0', () => { + test('should be 5.0 version', () => { + expect(isThemeVersionGreaterOrEqual(DARK_THEME_5_0, 5.0)).toBe(true); + expect(isThemeVersionGreaterOrEqual(DARK_THEME_5_0, 5.1)).toBe(false); + }); + test('should be dark', () => { + expect(isDarkTheme(DARK_THEME_5_0)).toBe(true); + }); + }); + + describe('DARK_THEME_5_1', () => { + test('should be 5.1 version', () => { + expect(isThemeVersionGreaterOrEqual(DARK_THEME_5_1, 5.0)).toBe(true); + expect(isThemeVersionGreaterOrEqual(DARK_THEME_5_1, 5.1)).toBe(true); + }); + test('should be dark', () => { + expect(isDarkTheme(DARK_THEME_5_1)).toBe(true); + }); + }); +}); From 7f02890080cfb522887233787cb39fa197d1ca36 Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Tue, 4 Feb 2025 14:36:32 +0500 Subject: [PATCH 03/27] chore: wip --- .../themes/__tests__/Theming-test.tsx | 64 +++++++++++++++++-- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/packages/react-ui/internal/themes/__tests__/Theming-test.tsx b/packages/react-ui/internal/themes/__tests__/Theming-test.tsx index 3bf700c39af..288ad3243c1 100644 --- a/packages/react-ui/internal/themes/__tests__/Theming-test.tsx +++ b/packages/react-ui/internal/themes/__tests__/Theming-test.tsx @@ -2,7 +2,17 @@ import { render } from '@testing-library/react'; import React from 'react'; import { ThemeContext } from '../../../lib/theming/ThemeContext'; -import { applyMarkers, createThemeFromClass, Marker, REACT_UI_THEME_MARKERS } from '../../../lib/theming/ThemeHelpers'; +import { + applyMarkers, + createThemeFromClass, + exposeGetters, + isDarkTheme, + isThemeVersionGreaterOrEqual, + markAsDarkTheme, + Marker, + markThemeVersion, + REACT_UI_THEME_MARKERS, +} from '../../../lib/theming/ThemeHelpers'; import { ThemeFactory } from '../../../lib/theming/ThemeFactory'; import { Theme } from '../../../lib/theming/Theme'; import { AnyObject } from '../../../lib/utils'; @@ -101,11 +111,57 @@ describe('Theming', () => { expect(keys_1).toEqual(keys_2); }); }); - describe('applyMarkers() should mark theme', () => { - test('should mark custom theme', () => { - const theme = applyMarkers(ThemeFactory.create(myTheme), [markAsTest]); + describe('exposeGetters', () => { + const theme = class extends (class {} as typeof BasicThemeClass) { + public static get errorText() { + return 'red'; + } + }; + const themeWithExposedGetters = exposeGetters(theme); + + test('getter is not enumerable by default in JS', () => { + expect(ThemeFactory.getKeys(theme).indexOf('errorText')).toBe(-1); + }); + + test('exposed getter should be enumerable', () => { + expect(ThemeFactory.getKeys(themeWithExposedGetters).indexOf('errorText')).toBeGreaterThan(-1); + }); + }); + + describe('applyMarker', () => { + test('test marker should mark custom theme', () => { + const theme = applyMarkers(ThemeFactory.create(myTheme), [markAsTest]); expect(isTestTheme(theme)).toBeTruthy(); }); + + describe('isThemeVersionGreaterOrEqual', () => { + const theme5_1 = applyMarkers(ThemeFactory.create(myTheme), [markThemeVersion(5.1)]); + + test('5.1 should BE greater or equal that 5.0', () => { + expect(isThemeVersionGreaterOrEqual(theme5_1, 5.0)).toBe(true); + }); + + test('5.1 should BE greater or equal that 5.1', () => { + expect(isThemeVersionGreaterOrEqual(theme5_1, 5.1)).toBe(true); + }); + + test('5.1 should NOT BE greater or equal that 5.2', () => { + expect(isThemeVersionGreaterOrEqual(theme5_1, 5.2)).toBe(false); + }); + }); + + describe('isDarkTheme', () => { + const lightTheme = applyMarkers(ThemeFactory.create(myTheme), []); + const darkTheme = applyMarkers(ThemeFactory.create(myTheme), [markAsDarkTheme]); + + test('light theme should NOT BE dark', () => { + expect(isDarkTheme(lightTheme)).toBe(false); + }); + + test('dark theme should BE dark', () => { + expect(isDarkTheme(darkTheme)).toBe(true); + }); + }); }); }); From 4c4434bedbb84efc13ed5ed340af26f6d5c730db Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Tue, 4 Feb 2025 14:59:31 +0500 Subject: [PATCH 04/27] chore: add tests --- .../themes/__tests__/Theming-test.tsx | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/react-ui/internal/themes/__tests__/Theming-test.tsx b/packages/react-ui/internal/themes/__tests__/Theming-test.tsx index 288ad3243c1..bc62ea45bcb 100644 --- a/packages/react-ui/internal/themes/__tests__/Theming-test.tsx +++ b/packages/react-ui/internal/themes/__tests__/Theming-test.tsx @@ -118,14 +118,42 @@ describe('Theming', () => { return 'red'; } }; - const themeWithExposedGetters = exposeGetters(theme); test('getter is not enumerable by default in JS', () => { expect(ThemeFactory.getKeys(theme).indexOf('errorText')).toBe(-1); }); test('exposed getter should be enumerable', () => { - expect(ThemeFactory.getKeys(themeWithExposedGetters).indexOf('errorText')).toBeGreaterThan(-1); + exposeGetters(theme); + expect(ThemeFactory.getKeys(theme).indexOf('errorText')).toBeGreaterThan(-1); + }); + }); + + describe('createThemeFromClass', () => { + const theme = createThemeFromClass( + class extends (class {} as typeof BasicThemeClass) { + public static get errorText() { + return this.black + this.blue; + } + }, + { prototypeTheme: BasicTheme, themeMarkers: [markAsDarkTheme, markThemeVersion(1.0)] }, + ); + + test('should inherit prototype theme', () => { + expect(theme.errorText).toBe(BasicTheme.black + BasicTheme.blue); + }); + + test('should expose getters', () => { + expect(ThemeFactory.getKeys(theme).indexOf('errorText')).toBeGreaterThan(-1); + }); + + describe('should apply markers', () => { + test('dark theme', () => { + expect(isDarkTheme(theme)).toBe(true); + }); + test('theme version', () => { + expect(isThemeVersionGreaterOrEqual(theme, 1.0)).toBe(true); + }); }); }); From f31c247dafeafa55e7caac5c8facaac366f4fbac Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Wed, 5 Feb 2025 14:05:23 +0500 Subject: [PATCH 05/27] chore: wip --- .../__stories__/ThemeVersioning.stories.tsx | 66 +++++++++++++++++++ .../themes/__tests__/Theming-test.tsx | 12 ++-- packages/react-ui/lib/theming/ThemeHelpers.ts | 26 ++++---- .../theming/themes/__tests__/Themes-test.tsx | 18 ++--- 4 files changed, 96 insertions(+), 26 deletions(-) create mode 100644 packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx diff --git a/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx new file mode 100644 index 00000000000..d286ec7bfc6 --- /dev/null +++ b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx @@ -0,0 +1,66 @@ +import React, { CSSProperties } from 'react'; + +import { Story, Meta } from '../../../typings/stories'; +import { createThemeFromClass, isThemeVersionGTE, markThemeVersion } from '../../../lib/theming/ThemeHelpers'; + +export default { + title: 'ThemeVersions/Test', +} as Meta; + +class TestThemeClass { + public static color = 'initial'; + public static textTransform = 'none'; + public static fontStyle = 'normal'; +} + +type TestThemeIn = Partial; + +const TEST_THEME_BASE = createThemeFromClass(TestThemeClass); + +const TEST_THEME_V_1_0 = createThemeFromClass( + class extends (class {} as typeof TestThemeClass) { + public static color = 'red'; + public static textTransform = 'lowercase'; + }, + { themeMarkers: [markThemeVersion(1.0)], prototypeTheme: TEST_THEME_BASE }, +); + +const TEST_THEME_V_1_1 = createThemeFromClass( + class extends (class {} as typeof TestThemeClass) { + public static color = 'green'; + public static fontStyle = 'italic'; + }, + { themeMarkers: [markThemeVersion(1.1)], prototypeTheme: TEST_THEME_V_1_0 }, +); + +const Component = ({ theme }: { theme: TestThemeIn }) => { + const styles = { + color: theme.color, + fontStyle: theme.fontStyle, + textTransform: theme.textTransform as CSSProperties['textTransform'], + }; + + const themeVersions = Object.entries({ + '1.0': isThemeVersionGTE(theme, 1.0), + '1.1': isThemeVersionGTE(theme, 1.1), + }) + .filter(([_, isDetected]) => isDetected === true) + .map(([version]) => version); + + return ( +
+ Test Component.  + {themeVersions.length + ? 'Detected theme versions: ' + themeVersions.join(', ') + '.' + : 'No theme versions detected.'} +
+ ); +}; + +export const BaseTheme: Story = () => ; + +export const Theme1_0: Story = () => ; +Theme1_0.storyName = 'Theme 1.0'; + +export const Theme1_1: Story = () => ; +Theme1_1.storyName = 'Theme 1.1'; diff --git a/packages/react-ui/internal/themes/__tests__/Theming-test.tsx b/packages/react-ui/internal/themes/__tests__/Theming-test.tsx index bc62ea45bcb..beeca805543 100644 --- a/packages/react-ui/internal/themes/__tests__/Theming-test.tsx +++ b/packages/react-ui/internal/themes/__tests__/Theming-test.tsx @@ -7,7 +7,7 @@ import { createThemeFromClass, exposeGetters, isDarkTheme, - isThemeVersionGreaterOrEqual, + isThemeVersionGTE, markAsDarkTheme, Marker, markThemeVersion, @@ -152,7 +152,7 @@ describe('Theming', () => { expect(isDarkTheme(theme)).toBe(true); }); test('theme version', () => { - expect(isThemeVersionGreaterOrEqual(theme, 1.0)).toBe(true); + expect(isThemeVersionGTE(theme, 1.0)).toBe(true); }); }); }); @@ -163,19 +163,19 @@ describe('Theming', () => { expect(isTestTheme(theme)).toBeTruthy(); }); - describe('isThemeVersionGreaterOrEqual', () => { + describe('isThemeVersionGTE', () => { const theme5_1 = applyMarkers(ThemeFactory.create(myTheme), [markThemeVersion(5.1)]); test('5.1 should BE greater or equal that 5.0', () => { - expect(isThemeVersionGreaterOrEqual(theme5_1, 5.0)).toBe(true); + expect(isThemeVersionGTE(theme5_1, 5.0)).toBe(true); }); test('5.1 should BE greater or equal that 5.1', () => { - expect(isThemeVersionGreaterOrEqual(theme5_1, 5.1)).toBe(true); + expect(isThemeVersionGTE(theme5_1, 5.1)).toBe(true); }); test('5.1 should NOT BE greater or equal that 5.2', () => { - expect(isThemeVersionGreaterOrEqual(theme5_1, 5.2)).toBe(false); + expect(isThemeVersionGTE(theme5_1, 5.2)).toBe(false); }); }); diff --git a/packages/react-ui/lib/theming/ThemeHelpers.ts b/packages/react-ui/lib/theming/ThemeHelpers.ts index 7d54fcf03ec..dc45f6f5f6b 100644 --- a/packages/react-ui/lib/theming/ThemeHelpers.ts +++ b/packages/react-ui/lib/theming/ThemeHelpers.ts @@ -1,4 +1,3 @@ -import { BasicThemeClass } from '../../internal/themes/BasicTheme'; import { isNonNullable } from '../utils'; import { Theme, ThemeIn } from './Theme'; @@ -63,7 +62,7 @@ export const markThemeVersion: (version: number) => Marker = (version) => (theme }); }; -export const isThemeVersionGreaterOrEqual = (theme: Theme | ThemeIn, version: number): boolean => { +export const isThemeVersionGTE = (theme: Theme | ThemeIn, version: number): boolean => { // @ts-expect-error: internal value. return theme[REACT_UI_THEME_MARKERS.themeVersion.key] >= version; }; @@ -80,25 +79,30 @@ export function findPropertyDescriptor(theme: Theme, propName: string) { return {}; } -export function applyMarkers(theme: Readonly, markers: Markers) { +export function applyMarkers(theme: T, markers: Markers): T { return markers.reduce((markedTheme, marker) => { return marker(markedTheme); - }, Object.create(theme)) as typeof theme; + }, Object.create(theme)); } -export function createThemeFromClass( - themeObject: typeof BasicThemeClass, +export function extendObject(baseTheme: B, prototypeTheme: P): void { + Object.setPrototypeOf(baseTheme, prototypeTheme); +} + +export function createThemeFromClass( + themeObject: T, options?: { - prototypeTheme?: Theme; + prototypeTheme?: P; themeMarkers?: Markers; }, -): Theme { - const theme = exposeGetters(themeObject); +): Readonly { const { prototypeTheme, themeMarkers = [] } = options || {}; if (prototypeTheme) { - Object.setPrototypeOf(theme, prototypeTheme); + extendObject(themeObject, prototypeTheme); } - return applyMarkers(theme as Theme, themeMarkers); + const theme = applyMarkers(exposeGetters(themeObject), themeMarkers); + + return Object.freeze(theme); } diff --git a/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx b/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx index 2e7b3babce0..cbd6b370878 100644 --- a/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx +++ b/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx @@ -1,4 +1,4 @@ -import { isDarkTheme, isThemeVersionGreaterOrEqual } from '../../ThemeHelpers'; +import { isDarkTheme, isThemeVersionGTE } from '../../ThemeHelpers'; import { DARK_THEME } from '../DarkTheme'; import { DARK_THEME_5_0 } from '../DarkTheme5_0'; import { DARK_THEME_5_1 } from '../DarkTheme5_1'; @@ -15,8 +15,8 @@ describe('Themes', () => { describe('LIGHT_THEME_5_0', () => { test('should be 5.0 version', () => { - expect(isThemeVersionGreaterOrEqual(LIGHT_THEME_5_0, 5.0)).toBe(true); - expect(isThemeVersionGreaterOrEqual(LIGHT_THEME_5_0, 5.1)).toBe(false); + expect(isThemeVersionGTE(LIGHT_THEME_5_0, 5.0)).toBe(true); + expect(isThemeVersionGTE(LIGHT_THEME_5_0, 5.1)).toBe(false); }); test('should not be dark', () => { expect(isDarkTheme(LIGHT_THEME_5_0)).toBe(false); @@ -25,8 +25,8 @@ describe('Themes', () => { describe('LIGHT_THEME_5_1', () => { test('should be 5.1 version', () => { - expect(isThemeVersionGreaterOrEqual(LIGHT_THEME_5_1, 5.0)).toBe(true); - expect(isThemeVersionGreaterOrEqual(LIGHT_THEME_5_1, 5.1)).toBe(true); + expect(isThemeVersionGTE(LIGHT_THEME_5_1, 5.0)).toBe(true); + expect(isThemeVersionGTE(LIGHT_THEME_5_1, 5.1)).toBe(true); }); test('should not be dark', () => { expect(isDarkTheme(LIGHT_THEME_5_1)).toBe(false); @@ -41,8 +41,8 @@ describe('Themes', () => { describe('DARK_THEME_5_0', () => { test('should be 5.0 version', () => { - expect(isThemeVersionGreaterOrEqual(DARK_THEME_5_0, 5.0)).toBe(true); - expect(isThemeVersionGreaterOrEqual(DARK_THEME_5_0, 5.1)).toBe(false); + expect(isThemeVersionGTE(DARK_THEME_5_0, 5.0)).toBe(true); + expect(isThemeVersionGTE(DARK_THEME_5_0, 5.1)).toBe(false); }); test('should be dark', () => { expect(isDarkTheme(DARK_THEME_5_0)).toBe(true); @@ -51,8 +51,8 @@ describe('Themes', () => { describe('DARK_THEME_5_1', () => { test('should be 5.1 version', () => { - expect(isThemeVersionGreaterOrEqual(DARK_THEME_5_1, 5.0)).toBe(true); - expect(isThemeVersionGreaterOrEqual(DARK_THEME_5_1, 5.1)).toBe(true); + expect(isThemeVersionGTE(DARK_THEME_5_1, 5.0)).toBe(true); + expect(isThemeVersionGTE(DARK_THEME_5_1, 5.1)).toBe(true); }); test('should be dark', () => { expect(isDarkTheme(DARK_THEME_5_1)).toBe(true); From 87a55f261766752274e52692eb6ed65ac31d8cdd Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Wed, 5 Feb 2025 15:46:30 +0500 Subject: [PATCH 06/27] chore: wip --- .../Test/Base Theme/chrome2022.png | 3 +++ .../Test/Theme 1.0/chrome2022.png | 3 +++ .../Test/Theme 1.1/chrome2022.png | 3 +++ packages/react-ui/index.ts | 4 ---- .../__stories__/ThemeVersioning.stories.tsx | 22 ++++++++++++++----- packages/react-ui/lib/theming/ThemeFactory.ts | 10 ++++----- packages/react-ui/lib/theming/ThemeHelpers.ts | 10 +++------ .../react-ui/lib/theming/themes/DarkTheme.ts | 9 +++++++- .../lib/theming/themes/DarkTheme5_0.ts | 1 - .../lib/theming/themes/DarkTheme5_1.ts | 1 - .../react-ui/lib/theming/themes/LightTheme.ts | 9 +++++++- .../lib/theming/themes/LightTheme5_0.ts | 1 - .../lib/theming/themes/LightTheme5_1.ts | 1 - .../theming/themes/__tests__/Themes-test.tsx | 8 ++----- 14 files changed, 51 insertions(+), 34 deletions(-) create mode 100644 packages/react-ui/.creevey/images/ThemeVersions/Test/Base Theme/chrome2022.png create mode 100644 packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1.0/chrome2022.png create mode 100644 packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1.1/chrome2022.png delete mode 100644 packages/react-ui/lib/theming/themes/DarkTheme5_0.ts delete mode 100644 packages/react-ui/lib/theming/themes/DarkTheme5_1.ts delete mode 100644 packages/react-ui/lib/theming/themes/LightTheme5_0.ts delete mode 100644 packages/react-ui/lib/theming/themes/LightTheme5_1.ts diff --git a/packages/react-ui/.creevey/images/ThemeVersions/Test/Base Theme/chrome2022.png b/packages/react-ui/.creevey/images/ThemeVersions/Test/Base Theme/chrome2022.png new file mode 100644 index 00000000000..29ae6a0e266 --- /dev/null +++ b/packages/react-ui/.creevey/images/ThemeVersions/Test/Base Theme/chrome2022.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3f89207b2b08d704faa1aaa52f9a3c985d6377bcddf7b996de037b2b27c2b1c +size 6327 diff --git a/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1.0/chrome2022.png b/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1.0/chrome2022.png new file mode 100644 index 00000000000..cf2ed46280c --- /dev/null +++ b/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1.0/chrome2022.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59c4be40e4689b13aace15f3ddc1231d73dc8f600b4309788a181597fd5fc57b +size 6615 diff --git a/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1.1/chrome2022.png b/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1.1/chrome2022.png new file mode 100644 index 00000000000..3e8f75e3c06 --- /dev/null +++ b/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1.1/chrome2022.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:261bec5189925ff071d4ac3909f6cb3b966d89b7a522fd0b533acf63554be45f +size 7914 diff --git a/packages/react-ui/index.ts b/packages/react-ui/index.ts index ebbddb58a2d..8145ef2b7e1 100644 --- a/packages/react-ui/index.ts +++ b/packages/react-ui/index.ts @@ -51,10 +51,6 @@ export * from './lib/featureFlagsContext'; export * from './lib/locale'; export * from './lib/theming/ThemeContext'; export * from './lib/theming/ThemeFactory'; -export * from './lib/theming/themes/LightTheme5_0'; -export * from './lib/theming/themes/DarkTheme5_0'; -export * from './lib/theming/themes/LightTheme5_1'; -export * from './lib/theming/themes/DarkTheme5_1'; export * from './lib/theming/themes/LightTheme'; export * from './lib/theming/themes/DarkTheme'; export * from './lib/types/props'; diff --git a/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx index d286ec7bfc6..31c1142200e 100644 --- a/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx +++ b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx @@ -5,6 +5,13 @@ import { createThemeFromClass, isThemeVersionGTE, markThemeVersion } from '../.. export default { title: 'ThemeVersions/Test', + parameters: { + creevey: { + skip: { + 'no themes': { in: /^(?!\bchrome2022\b)/ }, + }, + }, + }, } as Meta; class TestThemeClass { @@ -48,12 +55,15 @@ const Component = ({ theme }: { theme: TestThemeIn }) => { .map(([version]) => version); return ( -
- Test Component.  - {themeVersions.length - ? 'Detected theme versions: ' + themeVersions.join(', ') + '.' - : 'No theme versions detected.'} -
+ <> +
+ Test Component.  + {themeVersions.length + ? 'Detected theme versions: ' + themeVersions.join(', ') + '.' + : 'No theme versions detected.'} +
+
{JSON.stringify(styles, null, 2)}
+ ); }; diff --git a/packages/react-ui/lib/theming/ThemeFactory.ts b/packages/react-ui/lib/theming/ThemeFactory.ts index e623a2a04e2..055c8bf8d5c 100644 --- a/packages/react-ui/lib/theming/ThemeFactory.ts +++ b/packages/react-ui/lib/theming/ThemeFactory.ts @@ -1,4 +1,4 @@ -import { LIGHT_THEME } from '../../lib/theming/themes/LightTheme'; +import { BasicTheme } from '../../internal/themes/BasicTheme'; import { isNonNullable, NoInfer } from '../utils'; import { Theme, ThemeIn } from './Theme'; @@ -6,21 +6,21 @@ import { findPropertyDescriptor, REACT_UI_THEME_MARKERS } from './ThemeHelpers'; export class ThemeFactory { public static create(theme: ThemeIn & NoInfer, baseTheme?: Theme): Readonly> { - const base = baseTheme || LIGHT_THEME; + const base = baseTheme || BasicTheme; return this.constructTheme(base, theme); } public static overrideBaseTheme(theme: Theme) { // copying theme variables - ThemeFactory.getKeys(LIGHT_THEME).forEach((variableName) => { + ThemeFactory.getKeys(BasicTheme).forEach((variableName) => { const descriptor = findPropertyDescriptor(theme, variableName); - Object.defineProperty(LIGHT_THEME, variableName, descriptor); + Object.defineProperty(BasicTheme, variableName, descriptor); }); // copying theme markers Object.values(REACT_UI_THEME_MARKERS).forEach((marker) => { const descriptor = findPropertyDescriptor(theme, marker.key); - Object.defineProperty(LIGHT_THEME, marker.key, descriptor); + Object.defineProperty(BasicTheme, marker.key, descriptor); }); } diff --git a/packages/react-ui/lib/theming/ThemeHelpers.ts b/packages/react-ui/lib/theming/ThemeHelpers.ts index dc45f6f5f6b..a1156ea4373 100644 --- a/packages/react-ui/lib/theming/ThemeHelpers.ts +++ b/packages/react-ui/lib/theming/ThemeHelpers.ts @@ -85,24 +85,20 @@ export function applyMarkers(theme: T, markers: Markers): T { }, Object.create(theme)); } -export function extendObject(baseTheme: B, prototypeTheme: P): void { - Object.setPrototypeOf(baseTheme, prototypeTheme); -} - export function createThemeFromClass( themeObject: T, options?: { prototypeTheme?: P; themeMarkers?: Markers; }, -): Readonly { +): T { const { prototypeTheme, themeMarkers = [] } = options || {}; if (prototypeTheme) { - extendObject(themeObject, prototypeTheme); + Object.setPrototypeOf(themeObject, prototypeTheme); } const theme = applyMarkers(exposeGetters(themeObject), themeMarkers); - return Object.freeze(theme); + return theme; } diff --git a/packages/react-ui/lib/theming/themes/DarkTheme.ts b/packages/react-ui/lib/theming/themes/DarkTheme.ts index 5bb4818d073..e37d5ae2421 100644 --- a/packages/react-ui/lib/theming/themes/DarkTheme.ts +++ b/packages/react-ui/lib/theming/themes/DarkTheme.ts @@ -1 +1,8 @@ -export { DarkTheme5_0 as DARK_THEME } from '../../../internal/themes/DarkTheme5_0'; +import { ThemeFactory } from '../ThemeFactory'; +import { DarkTheme5_0 } from '../../../internal/themes/DarkTheme5_0'; +import { DarkTheme5_1 } from '../../../internal/themes/DarkTheme5_1'; + +export const DARK_THEME_5_0 = ThemeFactory.create({}, DarkTheme5_0); +export const DARK_THEME_5_1 = ThemeFactory.create({}, DarkTheme5_1); + +export const DARK_THEME = DARK_THEME_5_0; diff --git a/packages/react-ui/lib/theming/themes/DarkTheme5_0.ts b/packages/react-ui/lib/theming/themes/DarkTheme5_0.ts deleted file mode 100644 index 33f88ffe5ae..00000000000 --- a/packages/react-ui/lib/theming/themes/DarkTheme5_0.ts +++ /dev/null @@ -1 +0,0 @@ -export { DarkTheme5_0 as DARK_THEME_5_0 } from '../../../internal/themes/DarkTheme5_0'; diff --git a/packages/react-ui/lib/theming/themes/DarkTheme5_1.ts b/packages/react-ui/lib/theming/themes/DarkTheme5_1.ts deleted file mode 100644 index 5f3db09e7aa..00000000000 --- a/packages/react-ui/lib/theming/themes/DarkTheme5_1.ts +++ /dev/null @@ -1 +0,0 @@ -export { DarkTheme5_1 as DARK_THEME_5_1 } from '../../../internal/themes/DarkTheme5_1'; diff --git a/packages/react-ui/lib/theming/themes/LightTheme.ts b/packages/react-ui/lib/theming/themes/LightTheme.ts index e182922ced3..c930410b630 100644 --- a/packages/react-ui/lib/theming/themes/LightTheme.ts +++ b/packages/react-ui/lib/theming/themes/LightTheme.ts @@ -1 +1,8 @@ -export { LightTheme5_0 as LIGHT_THEME } from '../../../internal/themes/LightTheme5_0'; +import { ThemeFactory } from '../ThemeFactory'; +import { LightTheme5_0 } from '../../../internal/themes/LightTheme5_0'; +import { LightTheme5_1 } from '../../../internal/themes/LightTheme5_1'; + +export const LIGHT_THEME_5_0 = ThemeFactory.create({}, LightTheme5_0); +export const LIGHT_THEME_5_1 = ThemeFactory.create({}, LightTheme5_1); + +export const LIGHT_THEME = LIGHT_THEME_5_0; diff --git a/packages/react-ui/lib/theming/themes/LightTheme5_0.ts b/packages/react-ui/lib/theming/themes/LightTheme5_0.ts deleted file mode 100644 index dd64c1f0d0b..00000000000 --- a/packages/react-ui/lib/theming/themes/LightTheme5_0.ts +++ /dev/null @@ -1 +0,0 @@ -export { LightTheme5_0 as LIGHT_THEME_5_0 } from '../../../internal/themes/LightTheme5_0'; diff --git a/packages/react-ui/lib/theming/themes/LightTheme5_1.ts b/packages/react-ui/lib/theming/themes/LightTheme5_1.ts deleted file mode 100644 index 49010a780f1..00000000000 --- a/packages/react-ui/lib/theming/themes/LightTheme5_1.ts +++ /dev/null @@ -1 +0,0 @@ -export { LightTheme5_1 as LIGHT_THEME_5_1 } from '../../../internal/themes/LightTheme5_1'; diff --git a/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx b/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx index cbd6b370878..0eb8b2cbd91 100644 --- a/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx +++ b/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx @@ -1,10 +1,6 @@ import { isDarkTheme, isThemeVersionGTE } from '../../ThemeHelpers'; -import { DARK_THEME } from '../DarkTheme'; -import { DARK_THEME_5_0 } from '../DarkTheme5_0'; -import { DARK_THEME_5_1 } from '../DarkTheme5_1'; -import { LIGHT_THEME } from '../LightTheme'; -import { LIGHT_THEME_5_0 } from '../LightTheme5_0'; -import { LIGHT_THEME_5_1 } from '../LightTheme5_1'; +import { DARK_THEME, DARK_THEME_5_0, DARK_THEME_5_1 } from '../DarkTheme'; +import { LIGHT_THEME, LIGHT_THEME_5_0, LIGHT_THEME_5_1 } from '../LightTheme'; describe('Themes', () => { describe('LIGHT_THEME', () => { From f8dfc3d82ea2dc3883f019ea8d093697558a972f Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Wed, 5 Feb 2025 15:58:22 +0500 Subject: [PATCH 07/27] chore: wip --- .../.storybook/decorators/Theme/ThemeDecorator.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/react-ui/.storybook/decorators/Theme/ThemeDecorator.tsx b/packages/react-ui/.storybook/decorators/Theme/ThemeDecorator.tsx index e6d40e8faed..fe02803d1db 100644 --- a/packages/react-ui/.storybook/decorators/Theme/ThemeDecorator.tsx +++ b/packages/react-ui/.storybook/decorators/Theme/ThemeDecorator.tsx @@ -1,12 +1,8 @@ import React from 'react'; import { Decorator } from '@storybook/react'; -import { LIGHT_THEME } from '../../../lib/theming/themes/LightTheme'; -import { DARK_THEME } from '../../../lib/theming/themes/DarkTheme'; -import { LIGHT_THEME_5_0 } from '../../../lib/theming/themes/LightTheme5_0'; -import { DARK_THEME_5_0 } from '../../../lib/theming/themes/DarkTheme5_0'; -import { LIGHT_THEME_5_1 } from '../../../lib/theming/themes/LightTheme5_1'; -import { DARK_THEME_5_1 } from '../../../lib/theming/themes/DarkTheme5_1'; +import { LIGHT_THEME, LIGHT_THEME_5_0, LIGHT_THEME_5_1 } from '../../../lib/theming/themes/LightTheme'; +import { DARK_THEME, DARK_THEME_5_0, DARK_THEME_5_1 } from '../../../lib/theming/themes/DarkTheme'; import { ThemeContext } from '../../../lib/theming/ThemeContext'; import { ThemeFactory } from '../../../lib/theming/ThemeFactory'; import { isDarkTheme } from '../../../lib/theming/ThemeHelpers'; From 15d8d03e850df093aadeda048d50a5bc6744d58b Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Mon, 10 Feb 2025 09:59:13 +0500 Subject: [PATCH 08/27] chore: wip --- .../Test/Base Theme/chrome2022.png | 4 +- .../Test/Theme 1_0/chrome2022.png | 3 + .../Test/Theme 1_1/chrome2022.png | 3 + .../__stories__/ThemeVersioning.stories.tsx | 45 ++++++----- .../react-ui/internal/themes/DarkTheme5_0.ts | 2 +- .../react-ui/internal/themes/DarkTheme5_1.ts | 2 +- .../react-ui/internal/themes/LightTheme5_0.ts | 2 +- .../themes/__tests__/Theming-test.tsx | 18 ++--- packages/react-ui/lib/theming/ThemeHelpers.ts | 8 +- .../lib/theming/ThemeVersionsComparator.ts | 28 +++++++ .../ThemeVersionsComparator-test.tsx | 74 +++++++++++++++++++ .../theming/themes/__tests__/Themes-test.tsx | 24 +++--- 12 files changed, 164 insertions(+), 49 deletions(-) create mode 100644 packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1_0/chrome2022.png create mode 100644 packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1_1/chrome2022.png create mode 100644 packages/react-ui/lib/theming/ThemeVersionsComparator.ts create mode 100644 packages/react-ui/lib/theming/themes/__tests__/ThemeVersionsComparator-test.tsx diff --git a/packages/react-ui/.creevey/images/ThemeVersions/Test/Base Theme/chrome2022.png b/packages/react-ui/.creevey/images/ThemeVersions/Test/Base Theme/chrome2022.png index 29ae6a0e266..328928a67e0 100644 --- a/packages/react-ui/.creevey/images/ThemeVersions/Test/Base Theme/chrome2022.png +++ b/packages/react-ui/.creevey/images/ThemeVersions/Test/Base Theme/chrome2022.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3f89207b2b08d704faa1aaa52f9a3c985d6377bcddf7b996de037b2b27c2b1c -size 6327 +oid sha256:b1743713df39d11c025ecac8d308d432cdefe8812fd6314d15f9e0c58aa150f3 +size 6936 diff --git a/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1_0/chrome2022.png b/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1_0/chrome2022.png new file mode 100644 index 00000000000..9627d035615 --- /dev/null +++ b/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1_0/chrome2022.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f6e360563574dc38e9a1d8c0bf83c323b955e8654d42535e7c8e5624470f1ca +size 7511 diff --git a/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1_1/chrome2022.png b/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1_1/chrome2022.png new file mode 100644 index 00000000000..93ed0ad409a --- /dev/null +++ b/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1_1/chrome2022.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:39911d7ef4f6f5136cd54ec2608d1f2e6d77814e6c3b8b38860b8d231d331d0f +size 8154 diff --git a/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx index 31c1142200e..c681cdf6474 100644 --- a/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx +++ b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx @@ -1,3 +1,4 @@ +/* eslint-disable no-nested-ternary */ import React, { CSSProperties } from 'react'; import { Story, Meta } from '../../../typings/stories'; @@ -24,20 +25,20 @@ type TestThemeIn = Partial; const TEST_THEME_BASE = createThemeFromClass(TestThemeClass); -const TEST_THEME_V_1_0 = createThemeFromClass( +const TEST_THEME_1_0 = createThemeFromClass( class extends (class {} as typeof TestThemeClass) { public static color = 'red'; public static textTransform = 'lowercase'; }, - { themeMarkers: [markThemeVersion(1.0)], prototypeTheme: TEST_THEME_BASE }, + { themeMarkers: [markThemeVersion('1_0')], prototypeTheme: TEST_THEME_BASE }, ); -const TEST_THEME_V_1_1 = createThemeFromClass( +const TEST_THEME_1_1 = createThemeFromClass( class extends (class {} as typeof TestThemeClass) { public static color = 'green'; public static fontStyle = 'italic'; }, - { themeMarkers: [markThemeVersion(1.1)], prototypeTheme: TEST_THEME_V_1_0 }, + { themeMarkers: [markThemeVersion('1_1')], prototypeTheme: TEST_THEME_1_0 }, ); const Component = ({ theme }: { theme: TestThemeIn }) => { @@ -47,30 +48,34 @@ const Component = ({ theme }: { theme: TestThemeIn }) => { textTransform: theme.textTransform as CSSProperties['textTransform'], }; - const themeVersions = Object.entries({ - '1.0': isThemeVersionGTE(theme, 1.0), - '1.1': isThemeVersionGTE(theme, 1.1), + const themeVersionList = Object.entries({ + '1_0': isThemeVersionGTE(theme, '1_0'), + '1_1': isThemeVersionGTE(theme, '1_1'), }) .filter(([_, isDetected]) => isDetected === true) - .map(([version]) => version); + .map(([version]) =>
  • {version}
  • ); return ( - <> -
    - Test Component.  - {themeVersions.length - ? 'Detected theme versions: ' + themeVersions.join(', ') + '.' - : 'No theme versions detected.'} -
    +
    + Test Component.
    {JSON.stringify(styles, null, 2)}
    - +
    + Detected theme versions: {themeVersionList.length === 0 && 'none'} + + {isThemeVersionGTE(theme, '1_1') ? ( +
      {themeVersionList}
    + ) : isThemeVersionGTE(theme, '1_0') ? ( +
      {themeVersionList}
    + ) : null} +
    +
    ); }; export const BaseTheme: Story = () => ; -export const Theme1_0: Story = () => ; -Theme1_0.storyName = 'Theme 1.0'; +export const Theme1_0: Story = () => ; +Theme1_0.storyName = 'Theme 1_0'; -export const Theme1_1: Story = () => ; -Theme1_1.storyName = 'Theme 1.1'; +export const Theme1_1: Story = () => ; +Theme1_1.storyName = 'Theme 1_1'; diff --git a/packages/react-ui/internal/themes/DarkTheme5_0.ts b/packages/react-ui/internal/themes/DarkTheme5_0.ts index 3e563e7b1f5..f97a9ac23db 100644 --- a/packages/react-ui/internal/themes/DarkTheme5_0.ts +++ b/packages/react-ui/internal/themes/DarkTheme5_0.ts @@ -553,6 +553,6 @@ export const DarkTheme5_0 = createThemeFromClass( }, { prototypeTheme: BasicTheme, - themeMarkers: [markAsDarkTheme, markThemeVersion(5.0)], + themeMarkers: [markAsDarkTheme, markThemeVersion('5_0')], }, ); diff --git a/packages/react-ui/internal/themes/DarkTheme5_1.ts b/packages/react-ui/internal/themes/DarkTheme5_1.ts index a9f33070e68..af19a97c837 100644 --- a/packages/react-ui/internal/themes/DarkTheme5_1.ts +++ b/packages/react-ui/internal/themes/DarkTheme5_1.ts @@ -5,5 +5,5 @@ import { DarkTheme5_0 } from './DarkTheme5_0'; export const DarkTheme5_1 = createThemeFromClass(class DarkTheme5_1 extends (class {} as typeof BasicThemeClass) {}, { prototypeTheme: DarkTheme5_0, - themeMarkers: [markThemeVersion(5.1)], + themeMarkers: [markThemeVersion('5_1')], }); diff --git a/packages/react-ui/internal/themes/LightTheme5_0.ts b/packages/react-ui/internal/themes/LightTheme5_0.ts index 22f71ff5918..372f95fc468 100644 --- a/packages/react-ui/internal/themes/LightTheme5_0.ts +++ b/packages/react-ui/internal/themes/LightTheme5_0.ts @@ -4,5 +4,5 @@ import { BasicTheme, BasicThemeClass } from './BasicTheme'; export const LightTheme5_0 = createThemeFromClass(class LightTheme5_0 extends (class {} as typeof BasicThemeClass) {}, { prototypeTheme: BasicTheme, - themeMarkers: [markThemeVersion(5.0)], + themeMarkers: [markThemeVersion('5_0')], }); diff --git a/packages/react-ui/internal/themes/__tests__/Theming-test.tsx b/packages/react-ui/internal/themes/__tests__/Theming-test.tsx index beeca805543..98a6c58a09b 100644 --- a/packages/react-ui/internal/themes/__tests__/Theming-test.tsx +++ b/packages/react-ui/internal/themes/__tests__/Theming-test.tsx @@ -136,7 +136,7 @@ describe('Theming', () => { return this.black + this.blue; } }, - { prototypeTheme: BasicTheme, themeMarkers: [markAsDarkTheme, markThemeVersion(1.0)] }, + { prototypeTheme: BasicTheme, themeMarkers: [markAsDarkTheme, markThemeVersion('1_0')] }, ); test('should inherit prototype theme', () => { @@ -152,7 +152,7 @@ describe('Theming', () => { expect(isDarkTheme(theme)).toBe(true); }); test('theme version', () => { - expect(isThemeVersionGTE(theme, 1.0)).toBe(true); + expect(isThemeVersionGTE(theme, '1_0')).toBe(true); }); }); }); @@ -164,18 +164,18 @@ describe('Theming', () => { }); describe('isThemeVersionGTE', () => { - const theme5_1 = applyMarkers(ThemeFactory.create(myTheme), [markThemeVersion(5.1)]); + const theme5_1 = applyMarkers(ThemeFactory.create(myTheme), [markThemeVersion('5_1')]); - test('5.1 should BE greater or equal that 5.0', () => { - expect(isThemeVersionGTE(theme5_1, 5.0)).toBe(true); + test('5_1 should BE greater or equal that 5_0', () => { + expect(isThemeVersionGTE(theme5_1, '5_0')).toBe(true); }); - test('5.1 should BE greater or equal that 5.1', () => { - expect(isThemeVersionGTE(theme5_1, 5.1)).toBe(true); + test('5_1 should BE greater or equal that 5_1', () => { + expect(isThemeVersionGTE(theme5_1, '5_1')).toBe(true); }); - test('5.1 should NOT BE greater or equal that 5.2', () => { - expect(isThemeVersionGTE(theme5_1, 5.2)).toBe(false); + test('5_1 should NOT BE greater or equal that 5_2', () => { + expect(isThemeVersionGTE(theme5_1, '5_2')).toBe(false); }); }); diff --git a/packages/react-ui/lib/theming/ThemeHelpers.ts b/packages/react-ui/lib/theming/ThemeHelpers.ts index a1156ea4373..b9cbeb70cbb 100644 --- a/packages/react-ui/lib/theming/ThemeHelpers.ts +++ b/packages/react-ui/lib/theming/ThemeHelpers.ts @@ -1,6 +1,7 @@ import { isNonNullable } from '../utils'; import { Theme, ThemeIn } from './Theme'; +import { isVersionGTE } from './ThemeVersionsComparator'; export type Marker = (theme: Readonly) => Readonly; export type Markers = Marker[]; @@ -51,7 +52,7 @@ export const markAsDarkTheme: Marker = (theme) => { }); }; -export const markThemeVersion: (version: number) => Marker = (version) => (theme) => { +export const markThemeVersion: (version: string) => Marker = (version) => (theme) => { return Object.create(theme, { [REACT_UI_THEME_MARKERS.themeVersion.key]: { value: version, @@ -62,9 +63,10 @@ export const markThemeVersion: (version: number) => Marker = (version) => (theme }); }; -export const isThemeVersionGTE = (theme: Theme | ThemeIn, version: number): boolean => { +export const isThemeVersionGTE = (theme: Theme | ThemeIn, version: string): boolean => { // @ts-expect-error: internal value. - return theme[REACT_UI_THEME_MARKERS.themeVersion.key] >= version; + const themeVersion = theme[REACT_UI_THEME_MARKERS.themeVersion.key] || ''; + return isVersionGTE(themeVersion, version); }; export function findPropertyDescriptor(theme: Theme, propName: string) { diff --git a/packages/react-ui/lib/theming/ThemeVersionsComparator.ts b/packages/react-ui/lib/theming/ThemeVersionsComparator.ts new file mode 100644 index 00000000000..b8bff89f052 --- /dev/null +++ b/packages/react-ui/lib/theming/ThemeVersionsComparator.ts @@ -0,0 +1,28 @@ +const compareVersions = (ver1: string, ver2: string, comparator: (a: number, b: number) => boolean) => { + if (!ver1 || !ver2) { + return false; + } + + const [arr1, arr2] = [ver1, ver2].map((version) => version.split('_').map(Number)); + const maxArrLength = Math.max(arr1.length, arr2.length); + + for (let i = 0; i < maxArrLength; i++) { + const item1 = arr1[i] || 0; + const item2 = arr2[i] || 0; + + if ([item1, item2].find(Number.isNaN)) { + return false; + } + + if (item1 === item2) { + continue; + } else { + return comparator(item1, item2); + } + } + return comparator(1, 1); +}; + +export const isVersionEQ = (ver1: string, ver2: string) => compareVersions(ver1, ver2, (a, b) => a === b); +export const isVersionGT = (ver1: string, ver2: string) => compareVersions(ver1, ver2, (a, b) => a > b); +export const isVersionGTE = (ver1: string, ver2: string) => compareVersions(ver1, ver2, (a, b) => a >= b); diff --git a/packages/react-ui/lib/theming/themes/__tests__/ThemeVersionsComparator-test.tsx b/packages/react-ui/lib/theming/themes/__tests__/ThemeVersionsComparator-test.tsx new file mode 100644 index 00000000000..30b95178c2c --- /dev/null +++ b/packages/react-ui/lib/theming/themes/__tests__/ThemeVersionsComparator-test.tsx @@ -0,0 +1,74 @@ +import { isVersionEQ, isVersionGT, isVersionGTE } from '../../ThemeVersionsComparator'; + +describe('ThemeVersionsComparator', () => { + describe('isVersionsEQ', () => { + test.each` + a | b | expected + ${''} | ${''} | ${false} + ${'5_0_0'} | ${'5_0_0'} | ${true} + ${'5_0'} | ${'5_0'} | ${true} + ${'5'} | ${'5'} | ${true} + ${'5_0_0'} | ${'5_0'} | ${true} + ${'5_0'} | ${'5'} | ${true} + ${'5'} | ${'5_0_0'} | ${true} + ${'5_00'} | ${'5_0'} | ${true} + ${'5_1'} | ${'5_0'} | ${false} + ${'5_1'} | ${'5_10'} | ${false} + ${'5_0_1'} | ${'5_01'} | ${false} + ${'5_1'} | ${'5_01'} | ${true} + `('isVersionsEQ($a, $b) returns $expected', ({ a, b, expected }) => { + expect(isVersionEQ(a, b)).toBe(expected); + }); + }); + + describe('isVersionsGT', () => { + test.each` + a | b | expected + ${''} | ${''} | ${false} + ${'5_0_0'} | ${'5_0_0'} | ${false} + ${'5_0'} | ${'5_0'} | ${false} + ${'5'} | ${'5'} | ${false} + ${'5_0_0'} | ${'5_0'} | ${false} + ${'5_0'} | ${'5'} | ${false} + ${'5'} | ${'5_0_0'} | ${false} + ${'5_00'} | ${'5_0'} | ${false} + ${'5_1_0'} | ${'5_0_0'} | ${true} + ${'5_0_1'} | ${'5_0_0'} | ${true} + ${'5'} | ${'4'} | ${true} + ${'5'} | ${'4_9'} | ${true} + ${'5'} | ${'4_9_9'} | ${true} + ${'5_1'} | ${'5_10'} | ${false} + ${'5_0_1'} | ${'5_01'} | ${false} + ${'5_1'} | ${'5_01'} | ${false} + ${'5_0'} | ${'5_1'} | ${false} + `('isVersionsGT($a, $b) returns $expected', ({ a, b, expected }) => { + expect(isVersionGT(a, b)).toBe(expected); + }); + }); + + describe('isVersionsGTE', () => { + test.each` + a | b | expected + ${''} | ${''} | ${false} + ${'5_1_0'} | ${'5_0_0'} | ${true} + ${'5_0_0'} | ${'5_0_0'} | ${true} + ${'5_0'} | ${'5_0'} | ${true} + ${'5'} | ${'5'} | ${true} + ${'5_0_0'} | ${'5_0'} | ${true} + ${'5_0'} | ${'5'} | ${true} + ${'5'} | ${'5_0_0'} | ${true} + ${'5_00'} | ${'5_0'} | ${true} + ${'5_1_0'} | ${'5_0_0'} | ${true} + ${'5_0_1'} | ${'5_0_0'} | ${true} + ${'5'} | ${'4'} | ${true} + ${'5'} | ${'4_9'} | ${true} + ${'5'} | ${'4_9_9'} | ${true} + ${'5_1'} | ${'5_10'} | ${false} + ${'5_0_1'} | ${'5_01'} | ${false} + ${'5_1'} | ${'5_01'} | ${true} + ${'5_0'} | ${'5_1'} | ${false} + `('isVersionsGTE($a, $b) returns $expected', ({ a, b, expected }) => { + expect(isVersionGTE(a, b)).toBe(expected); + }); + }); +}); diff --git a/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx b/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx index 0eb8b2cbd91..5a13dc9aa88 100644 --- a/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx +++ b/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx @@ -10,9 +10,9 @@ describe('Themes', () => { }); describe('LIGHT_THEME_5_0', () => { - test('should be 5.0 version', () => { - expect(isThemeVersionGTE(LIGHT_THEME_5_0, 5.0)).toBe(true); - expect(isThemeVersionGTE(LIGHT_THEME_5_0, 5.1)).toBe(false); + test('should be 5_0 version', () => { + expect(isThemeVersionGTE(LIGHT_THEME_5_0, '5_0')).toBe(true); + expect(isThemeVersionGTE(LIGHT_THEME_5_0, '5_1')).toBe(false); }); test('should not be dark', () => { expect(isDarkTheme(LIGHT_THEME_5_0)).toBe(false); @@ -20,9 +20,9 @@ describe('Themes', () => { }); describe('LIGHT_THEME_5_1', () => { - test('should be 5.1 version', () => { - expect(isThemeVersionGTE(LIGHT_THEME_5_1, 5.0)).toBe(true); - expect(isThemeVersionGTE(LIGHT_THEME_5_1, 5.1)).toBe(true); + test('should be 5_1 version', () => { + expect(isThemeVersionGTE(LIGHT_THEME_5_1, '5_0')).toBe(true); + expect(isThemeVersionGTE(LIGHT_THEME_5_1, '5_1')).toBe(true); }); test('should not be dark', () => { expect(isDarkTheme(LIGHT_THEME_5_1)).toBe(false); @@ -36,9 +36,9 @@ describe('Themes', () => { }); describe('DARK_THEME_5_0', () => { - test('should be 5.0 version', () => { - expect(isThemeVersionGTE(DARK_THEME_5_0, 5.0)).toBe(true); - expect(isThemeVersionGTE(DARK_THEME_5_0, 5.1)).toBe(false); + test('should be 5_0 version', () => { + expect(isThemeVersionGTE(DARK_THEME_5_0, '5_0')).toBe(true); + expect(isThemeVersionGTE(DARK_THEME_5_0, '5_1')).toBe(false); }); test('should be dark', () => { expect(isDarkTheme(DARK_THEME_5_0)).toBe(true); @@ -46,9 +46,9 @@ describe('Themes', () => { }); describe('DARK_THEME_5_1', () => { - test('should be 5.1 version', () => { - expect(isThemeVersionGTE(DARK_THEME_5_1, 5.0)).toBe(true); - expect(isThemeVersionGTE(DARK_THEME_5_1, 5.1)).toBe(true); + test('should be 5_1 version', () => { + expect(isThemeVersionGTE(DARK_THEME_5_1, '5_0')).toBe(true); + expect(isThemeVersionGTE(DARK_THEME_5_1, '5_1')).toBe(true); }); test('should be dark', () => { expect(isDarkTheme(DARK_THEME_5_1)).toBe(true); From 9c1ea8327073b7ad8e4f5e0498b72dd9aacfe6a3 Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Mon, 10 Feb 2025 10:12:20 +0500 Subject: [PATCH 09/27] chore: wip --- packages/react-ui/internal/themes/LightTheme5_1.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-ui/internal/themes/LightTheme5_1.ts b/packages/react-ui/internal/themes/LightTheme5_1.ts index 660fb09b002..2f23878bee3 100644 --- a/packages/react-ui/internal/themes/LightTheme5_1.ts +++ b/packages/react-ui/internal/themes/LightTheme5_1.ts @@ -5,5 +5,5 @@ import { LightTheme5_0 } from './LightTheme5_0'; export const LightTheme5_1 = createThemeFromClass(class LightTheme5_1 extends (class {} as typeof BasicThemeClass) {}, { prototypeTheme: LightTheme5_0, - themeMarkers: [markThemeVersion(5.1)], + themeMarkers: [markThemeVersion('5_1')], }); From 4022527262c0452cbd0b96cf2b63504e4b6071c4 Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Tue, 11 Feb 2025 12:40:57 +0500 Subject: [PATCH 10/27] chore: wip --- .../images/ThemeVersions/Test/Theme 1.0/chrome2022.png | 3 --- .../images/ThemeVersions/Test/Theme 1.1/chrome2022.png | 3 --- 2 files changed, 6 deletions(-) delete mode 100644 packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1.0/chrome2022.png delete mode 100644 packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1.1/chrome2022.png diff --git a/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1.0/chrome2022.png b/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1.0/chrome2022.png deleted file mode 100644 index cf2ed46280c..00000000000 --- a/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1.0/chrome2022.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:59c4be40e4689b13aace15f3ddc1231d73dc8f600b4309788a181597fd5fc57b -size 6615 diff --git a/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1.1/chrome2022.png b/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1.1/chrome2022.png deleted file mode 100644 index 3e8f75e3c06..00000000000 --- a/packages/react-ui/.creevey/images/ThemeVersions/Test/Theme 1.1/chrome2022.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:261bec5189925ff071d4ac3909f6cb3b966d89b7a522fd0b533acf63554be45f -size 7914 From 9486f030da24a32e276ed6e6412b60c37eeb0e2d Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Tue, 11 Feb 2025 17:19:53 +0500 Subject: [PATCH 11/27] chore: wip --- .../lib/theming/ThemeVersionsComparator.ts | 37 ++++--- .../ThemeVersionsComparator-test.tsx | 102 +++++++++++++----- 2 files changed, 101 insertions(+), 38 deletions(-) diff --git a/packages/react-ui/lib/theming/ThemeVersionsComparator.ts b/packages/react-ui/lib/theming/ThemeVersionsComparator.ts index b8bff89f052..72ad04f20e9 100644 --- a/packages/react-ui/lib/theming/ThemeVersionsComparator.ts +++ b/packages/react-ui/lib/theming/ThemeVersionsComparator.ts @@ -1,26 +1,37 @@ -const compareVersions = (ver1: string, ver2: string, comparator: (a: number, b: number) => boolean) => { - if (!ver1 || !ver2) { - return false; - } +import warning from 'warning'; + +const versionToNumbers = (version: string, delimiter: string): number[] => { + return version.split(delimiter).map((i) => (/^\d+$/.test(i) ? Number(i) : Number.NaN)); +}; - const [arr1, arr2] = [ver1, ver2].map((version) => version.split('_').map(Number)); +const compareVersions = ( + ver1: string, + ver2: string, + comparator: (a: number, b: number) => boolean, + delimiter: string = '_', +): boolean => { + const arr1 = versionToNumbers(ver1, delimiter); + const arr2 = versionToNumbers(ver2, delimiter); const maxArrLength = Math.max(arr1.length, arr2.length); for (let i = 0; i < maxArrLength; i++) { - const item1 = arr1[i] || 0; - const item2 = arr2[i] || 0; + const item1 = arr1[i] ?? 0; + const item2 = arr2[i] ?? 0; - if ([item1, item2].find(Number.isNaN)) { - return false; + if (Number.isNaN(item1) || Number.isNaN(item2)) { + warning( + true, + `[ThemeVersionsComparator]: unsupported version format. Only numbers and "${delimiter}" are allowed in ${ver1} and ${ver2}.`, + ); + break; } - if (item1 === item2) { - continue; - } else { + if (item1 !== item2 || i === maxArrLength - 1) { return comparator(item1, item2); } } - return comparator(1, 1); + + return false; }; export const isVersionEQ = (ver1: string, ver2: string) => compareVersions(ver1, ver2, (a, b) => a === b); diff --git a/packages/react-ui/lib/theming/themes/__tests__/ThemeVersionsComparator-test.tsx b/packages/react-ui/lib/theming/themes/__tests__/ThemeVersionsComparator-test.tsx index 30b95178c2c..293c6fe73cf 100644 --- a/packages/react-ui/lib/theming/themes/__tests__/ThemeVersionsComparator-test.tsx +++ b/packages/react-ui/lib/theming/themes/__tests__/ThemeVersionsComparator-test.tsx @@ -5,17 +5,38 @@ describe('ThemeVersionsComparator', () => { test.each` a | b | expected ${''} | ${''} | ${false} - ${'5_0_0'} | ${'5_0_0'} | ${true} - ${'5_0'} | ${'5_0'} | ${true} - ${'5'} | ${'5'} | ${true} + ${'5_0'} | ${''} | ${false} + ${''} | ${'5_0'} | ${false} + ${'5.0'} | ${'5_0'} | ${false} + ${'5_0'} | ${'5.0'} | ${false} + ${'5_'} | ${'5_0'} | ${false} + ${'5_0'} | ${'5_'} | ${false} ${'5_0_0'} | ${'5_0'} | ${true} ${'5_0'} | ${'5'} | ${true} ${'5'} | ${'5_0_0'} | ${true} ${'5_00'} | ${'5_0'} | ${true} + ${'5_10'} | ${'5_1'} | ${false} + ${'5_01'} | ${'5_0_1'} | ${false} + ${'5_01'} | ${'5_1'} | ${true} + ${'5_1'} | ${'5_0'} | ${false} + ${'5_0_0'} | ${'5_0_0'} | ${true} + ${'6_0_0'} | ${'5_0_0'} | ${false} + ${'5_0_0'} | ${'6_0_0'} | ${false} + ${'5_1_0'} | ${'5_1_0'} | ${true} + ${'5_1_0'} | ${'5_0_0'} | ${false} + ${'5_0_0'} | ${'5_1_0'} | ${false} + ${'5_0_1'} | ${'5_0_1'} | ${true} + ${'5_0_1'} | ${'5_0_0'} | ${false} + ${'5_0_0'} | ${'5_0_1'} | ${false} + ${'5_0'} | ${'5_0'} | ${true} + ${'6_0'} | ${'5_0'} | ${false} + ${'5_0'} | ${'6_0'} | ${false} + ${'5_1'} | ${'5_1'} | ${true} ${'5_1'} | ${'5_0'} | ${false} - ${'5_1'} | ${'5_10'} | ${false} - ${'5_0_1'} | ${'5_01'} | ${false} - ${'5_1'} | ${'5_01'} | ${true} + ${'5_0'} | ${'5_1'} | ${false} + ${'5'} | ${'5'} | ${true} + ${'6'} | ${'5'} | ${false} + ${'5'} | ${'6'} | ${false} `('isVersionsEQ($a, $b) returns $expected', ({ a, b, expected }) => { expect(isVersionEQ(a, b)).toBe(expected); }); @@ -25,22 +46,38 @@ describe('ThemeVersionsComparator', () => { test.each` a | b | expected ${''} | ${''} | ${false} - ${'5_0_0'} | ${'5_0_0'} | ${false} - ${'5_0'} | ${'5_0'} | ${false} - ${'5'} | ${'5'} | ${false} + ${'5_0'} | ${''} | ${false} + ${''} | ${'5_0'} | ${false} + ${'5.0'} | ${'5_0'} | ${false} + ${'5_0'} | ${'5.0'} | ${false} + ${'5_'} | ${'5_0'} | ${false} + ${'5_0'} | ${'5_'} | ${false} ${'5_0_0'} | ${'5_0'} | ${false} ${'5_0'} | ${'5'} | ${false} ${'5'} | ${'5_0_0'} | ${false} ${'5_00'} | ${'5_0'} | ${false} + ${'5_10'} | ${'5_1'} | ${true} + ${'5_01'} | ${'5_0_1'} | ${true} + ${'5_01'} | ${'5_1'} | ${false} + ${'5_1'} | ${'5_0'} | ${true} + ${'5_0_0'} | ${'5_0_0'} | ${false} + ${'6_0_0'} | ${'5_0_0'} | ${true} + ${'5_0_0'} | ${'6_0_0'} | ${false} + ${'5_1_0'} | ${'5_1_0'} | ${false} ${'5_1_0'} | ${'5_0_0'} | ${true} + ${'5_0_0'} | ${'5_1_0'} | ${false} + ${'5_0_1'} | ${'5_0_1'} | ${false} ${'5_0_1'} | ${'5_0_0'} | ${true} - ${'5'} | ${'4'} | ${true} - ${'5'} | ${'4_9'} | ${true} - ${'5'} | ${'4_9_9'} | ${true} - ${'5_1'} | ${'5_10'} | ${false} - ${'5_0_1'} | ${'5_01'} | ${false} - ${'5_1'} | ${'5_01'} | ${false} + ${'5_0_0'} | ${'5_0_1'} | ${false} + ${'5_0'} | ${'5_0'} | ${false} + ${'6_0'} | ${'5_0'} | ${true} + ${'5_0'} | ${'6_0'} | ${false} + ${'5_1'} | ${'5_1'} | ${false} + ${'5_1'} | ${'5_0'} | ${true} ${'5_0'} | ${'5_1'} | ${false} + ${'5'} | ${'5'} | ${false} + ${'6'} | ${'5'} | ${true} + ${'5'} | ${'6'} | ${false} `('isVersionsGT($a, $b) returns $expected', ({ a, b, expected }) => { expect(isVersionGT(a, b)).toBe(expected); }); @@ -50,23 +87,38 @@ describe('ThemeVersionsComparator', () => { test.each` a | b | expected ${''} | ${''} | ${false} - ${'5_1_0'} | ${'5_0_0'} | ${true} - ${'5_0_0'} | ${'5_0_0'} | ${true} - ${'5_0'} | ${'5_0'} | ${true} - ${'5'} | ${'5'} | ${true} + ${'5_0'} | ${''} | ${false} + ${''} | ${'5_0'} | ${false} + ${'5.0'} | ${'5_0'} | ${false} + ${'5_0'} | ${'5.0'} | ${false} + ${'5_'} | ${'5_0'} | ${false} + ${'5_0'} | ${'5_'} | ${false} ${'5_0_0'} | ${'5_0'} | ${true} ${'5_0'} | ${'5'} | ${true} ${'5'} | ${'5_0_0'} | ${true} ${'5_00'} | ${'5_0'} | ${true} + ${'5_10'} | ${'5_1'} | ${true} + ${'5_01'} | ${'5_0_1'} | ${true} + ${'5_01'} | ${'5_1'} | ${true} + ${'5_1'} | ${'5_0'} | ${true} + ${'5_0_0'} | ${'5_0_0'} | ${true} + ${'6_0_0'} | ${'5_0_0'} | ${true} + ${'5_0_0'} | ${'6_0_0'} | ${false} + ${'5_1_0'} | ${'5_1_0'} | ${true} ${'5_1_0'} | ${'5_0_0'} | ${true} + ${'5_0_0'} | ${'5_1_0'} | ${false} + ${'5_0_1'} | ${'5_0_1'} | ${true} ${'5_0_1'} | ${'5_0_0'} | ${true} - ${'5'} | ${'4'} | ${true} - ${'5'} | ${'4_9'} | ${true} - ${'5'} | ${'4_9_9'} | ${true} - ${'5_1'} | ${'5_10'} | ${false} - ${'5_0_1'} | ${'5_01'} | ${false} - ${'5_1'} | ${'5_01'} | ${true} + ${'5_0_0'} | ${'5_0_1'} | ${false} + ${'5_0'} | ${'5_0'} | ${true} + ${'6_0'} | ${'5_0'} | ${true} + ${'5_0'} | ${'6_0'} | ${false} + ${'5_1'} | ${'5_1'} | ${true} + ${'5_1'} | ${'5_0'} | ${true} ${'5_0'} | ${'5_1'} | ${false} + ${'5'} | ${'5'} | ${true} + ${'6'} | ${'5'} | ${true} + ${'5'} | ${'6'} | ${false} `('isVersionsGTE($a, $b) returns $expected', ({ a, b, expected }) => { expect(isVersionGTE(a, b)).toBe(expected); }); From 838dc4470c21aa585f11e83ad7bee98c821fa71b Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Wed, 12 Feb 2025 11:07:47 +0500 Subject: [PATCH 12/27] chore: wip --- .../__stories__/ThemeVersioning.stories.tsx | 12 +- .../react-ui/internal/themes/DarkTheme5_0.ts | 2 +- .../react-ui/internal/themes/DarkTheme5_1.ts | 2 +- .../react-ui/internal/themes/LightTheme5_0.ts | 2 +- .../react-ui/internal/themes/LightTheme5_1.ts | 2 +- packages/react-ui/lib/theming/ThemeHelpers.ts | 22 ++- .../lib/theming/ThemeVersionsComparator.ts | 39 ------ .../theming/__tests__/ThemeFactory-test.tsx | 102 ++++++++++++++ .../theming/__tests__/ThemeHelpers-test.tsx} | 126 +++++++----------- .../ThemeVersionsComparator-test.tsx | 126 ------------------ .../theming/themes/__tests__/Themes-test.tsx | 16 +-- 11 files changed, 185 insertions(+), 266 deletions(-) delete mode 100644 packages/react-ui/lib/theming/ThemeVersionsComparator.ts create mode 100644 packages/react-ui/lib/theming/__tests__/ThemeFactory-test.tsx rename packages/react-ui/{internal/themes/__tests__/Theming-test.tsx => lib/theming/__tests__/ThemeHelpers-test.tsx} (51%) delete mode 100644 packages/react-ui/lib/theming/themes/__tests__/ThemeVersionsComparator-test.tsx diff --git a/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx index c681cdf6474..c227f8070e6 100644 --- a/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx +++ b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx @@ -30,7 +30,7 @@ const TEST_THEME_1_0 = createThemeFromClass( public static color = 'red'; public static textTransform = 'lowercase'; }, - { themeMarkers: [markThemeVersion('1_0')], prototypeTheme: TEST_THEME_BASE }, + { themeMarkers: [markThemeVersion(1, 0)], prototypeTheme: TEST_THEME_BASE }, ); const TEST_THEME_1_1 = createThemeFromClass( @@ -38,7 +38,7 @@ const TEST_THEME_1_1 = createThemeFromClass( public static color = 'green'; public static fontStyle = 'italic'; }, - { themeMarkers: [markThemeVersion('1_1')], prototypeTheme: TEST_THEME_1_0 }, + { themeMarkers: [markThemeVersion(1, 1)], prototypeTheme: TEST_THEME_1_0 }, ); const Component = ({ theme }: { theme: TestThemeIn }) => { @@ -49,8 +49,8 @@ const Component = ({ theme }: { theme: TestThemeIn }) => { }; const themeVersionList = Object.entries({ - '1_0': isThemeVersionGTE(theme, '1_0'), - '1_1': isThemeVersionGTE(theme, '1_1'), + '1_0': isThemeVersionGTE(theme, 1, 0), + '1_1': isThemeVersionGTE(theme, 1, 1), }) .filter(([_, isDetected]) => isDetected === true) .map(([version]) =>
  • {version}
  • ); @@ -62,9 +62,9 @@ const Component = ({ theme }: { theme: TestThemeIn }) => {
    Detected theme versions: {themeVersionList.length === 0 && 'none'} - {isThemeVersionGTE(theme, '1_1') ? ( + {isThemeVersionGTE(theme, 1, 1) ? (
      {themeVersionList}
    - ) : isThemeVersionGTE(theme, '1_0') ? ( + ) : isThemeVersionGTE(theme, 1, 0) ? (
      {themeVersionList}
    ) : null}
    diff --git a/packages/react-ui/internal/themes/DarkTheme5_0.ts b/packages/react-ui/internal/themes/DarkTheme5_0.ts index f97a9ac23db..0fa7e4767ce 100644 --- a/packages/react-ui/internal/themes/DarkTheme5_0.ts +++ b/packages/react-ui/internal/themes/DarkTheme5_0.ts @@ -553,6 +553,6 @@ export const DarkTheme5_0 = createThemeFromClass( }, { prototypeTheme: BasicTheme, - themeMarkers: [markAsDarkTheme, markThemeVersion('5_0')], + themeMarkers: [markAsDarkTheme, markThemeVersion(5, 0)], }, ); diff --git a/packages/react-ui/internal/themes/DarkTheme5_1.ts b/packages/react-ui/internal/themes/DarkTheme5_1.ts index af19a97c837..b25538a1389 100644 --- a/packages/react-ui/internal/themes/DarkTheme5_1.ts +++ b/packages/react-ui/internal/themes/DarkTheme5_1.ts @@ -5,5 +5,5 @@ import { DarkTheme5_0 } from './DarkTheme5_0'; export const DarkTheme5_1 = createThemeFromClass(class DarkTheme5_1 extends (class {} as typeof BasicThemeClass) {}, { prototypeTheme: DarkTheme5_0, - themeMarkers: [markThemeVersion('5_1')], + themeMarkers: [markThemeVersion(5, 1)], }); diff --git a/packages/react-ui/internal/themes/LightTheme5_0.ts b/packages/react-ui/internal/themes/LightTheme5_0.ts index 372f95fc468..0d5a9df791b 100644 --- a/packages/react-ui/internal/themes/LightTheme5_0.ts +++ b/packages/react-ui/internal/themes/LightTheme5_0.ts @@ -4,5 +4,5 @@ import { BasicTheme, BasicThemeClass } from './BasicTheme'; export const LightTheme5_0 = createThemeFromClass(class LightTheme5_0 extends (class {} as typeof BasicThemeClass) {}, { prototypeTheme: BasicTheme, - themeMarkers: [markThemeVersion('5_0')], + themeMarkers: [markThemeVersion(5, 0)], }); diff --git a/packages/react-ui/internal/themes/LightTheme5_1.ts b/packages/react-ui/internal/themes/LightTheme5_1.ts index 2f23878bee3..d89b5e42fae 100644 --- a/packages/react-ui/internal/themes/LightTheme5_1.ts +++ b/packages/react-ui/internal/themes/LightTheme5_1.ts @@ -5,5 +5,5 @@ import { LightTheme5_0 } from './LightTheme5_0'; export const LightTheme5_1 = createThemeFromClass(class LightTheme5_1 extends (class {} as typeof BasicThemeClass) {}, { prototypeTheme: LightTheme5_0, - themeMarkers: [markThemeVersion('5_1')], + themeMarkers: [markThemeVersion(5, 1)], }); diff --git a/packages/react-ui/lib/theming/ThemeHelpers.ts b/packages/react-ui/lib/theming/ThemeHelpers.ts index b9cbeb70cbb..03d5d194420 100644 --- a/packages/react-ui/lib/theming/ThemeHelpers.ts +++ b/packages/react-ui/lib/theming/ThemeHelpers.ts @@ -1,7 +1,6 @@ import { isNonNullable } from '../utils'; import { Theme, ThemeIn } from './Theme'; -import { isVersionGTE } from './ThemeVersionsComparator'; export type Marker = (theme: Readonly) => Readonly; export type Markers = Marker[]; @@ -52,10 +51,10 @@ export const markAsDarkTheme: Marker = (theme) => { }); }; -export const markThemeVersion: (version: string) => Marker = (version) => (theme) => { +export const markThemeVersion: (major: number, minor: number) => Marker = (major: number, minor: number) => (theme) => { return Object.create(theme, { [REACT_UI_THEME_MARKERS.themeVersion.key]: { - value: version, + value: { major, minor }, writable: false, enumerable: false, configurable: false, @@ -63,10 +62,21 @@ export const markThemeVersion: (version: string) => Marker = (version) => (theme }); }; -export const isThemeVersionGTE = (theme: Theme | ThemeIn, version: string): boolean => { +export const isThemeVersionGTE = (theme: Theme | ThemeIn, major: number, minor: number): boolean => { // @ts-expect-error: internal value. - const themeVersion = theme[REACT_UI_THEME_MARKERS.themeVersion.key] || ''; - return isVersionGTE(themeVersion, version); + const themeVersion: { major: number; minor: number } | undefined = theme[REACT_UI_THEME_MARKERS.themeVersion.key]; + + if (!themeVersion) { + return false; + } + + if (themeVersion.major > major) { + return true; + } else if (themeVersion.major === major) { + return themeVersion.minor >= minor; + } + + return false; }; export function findPropertyDescriptor(theme: Theme, propName: string) { diff --git a/packages/react-ui/lib/theming/ThemeVersionsComparator.ts b/packages/react-ui/lib/theming/ThemeVersionsComparator.ts deleted file mode 100644 index 72ad04f20e9..00000000000 --- a/packages/react-ui/lib/theming/ThemeVersionsComparator.ts +++ /dev/null @@ -1,39 +0,0 @@ -import warning from 'warning'; - -const versionToNumbers = (version: string, delimiter: string): number[] => { - return version.split(delimiter).map((i) => (/^\d+$/.test(i) ? Number(i) : Number.NaN)); -}; - -const compareVersions = ( - ver1: string, - ver2: string, - comparator: (a: number, b: number) => boolean, - delimiter: string = '_', -): boolean => { - const arr1 = versionToNumbers(ver1, delimiter); - const arr2 = versionToNumbers(ver2, delimiter); - const maxArrLength = Math.max(arr1.length, arr2.length); - - for (let i = 0; i < maxArrLength; i++) { - const item1 = arr1[i] ?? 0; - const item2 = arr2[i] ?? 0; - - if (Number.isNaN(item1) || Number.isNaN(item2)) { - warning( - true, - `[ThemeVersionsComparator]: unsupported version format. Only numbers and "${delimiter}" are allowed in ${ver1} and ${ver2}.`, - ); - break; - } - - if (item1 !== item2 || i === maxArrLength - 1) { - return comparator(item1, item2); - } - } - - return false; -}; - -export const isVersionEQ = (ver1: string, ver2: string) => compareVersions(ver1, ver2, (a, b) => a === b); -export const isVersionGT = (ver1: string, ver2: string) => compareVersions(ver1, ver2, (a, b) => a > b); -export const isVersionGTE = (ver1: string, ver2: string) => compareVersions(ver1, ver2, (a, b) => a >= b); diff --git a/packages/react-ui/lib/theming/__tests__/ThemeFactory-test.tsx b/packages/react-ui/lib/theming/__tests__/ThemeFactory-test.tsx new file mode 100644 index 00000000000..33d86cc8f0b --- /dev/null +++ b/packages/react-ui/lib/theming/__tests__/ThemeFactory-test.tsx @@ -0,0 +1,102 @@ +import { render } from '@testing-library/react'; +import React from 'react'; + +import { ThemeContext } from '../ThemeContext'; +import { applyMarkers, createThemeFromClass, Marker, REACT_UI_THEME_MARKERS } from '../ThemeHelpers'; +import { ThemeFactory } from '../ThemeFactory'; +import { Theme } from '../Theme'; +import { AnyObject } from '../../utils'; +import { BasicTheme, BasicThemeClass } from '../../../internal/themes/BasicTheme'; + +const TEST_MARKERS = { + test: { + key: '__REACT_UI_TEST_KEY__', + value: true, + }, +}; + +// @ts-expect-error: extension for test +REACT_UI_THEME_MARKERS.test = TEST_MARKERS.test; + +const getConsumedTheme = () => { + let _theme: Theme; + render( + + {(theme) => { + _theme = theme; + return null; + }} + , + ); + // @ts-expect-error: render is sync + return _theme; +}; + +// test theme +const myTheme = { brand: 'custom', bgDefault: 'custom' } as const; +const TestTheme = createThemeFromClass( + class extends (class {} as typeof BasicThemeClass) { + public static bgDefault = 'default'; + public static bgSecondary = 'default'; + }, + { prototypeTheme: BasicTheme }, +); + +// test marker +const markAsTest: Marker = (theme) => { + return Object.create(theme, { + [TEST_MARKERS.test.key]: { + value: TEST_MARKERS.test.value, + writable: false, + enumerable: false, + configurable: false, + }, + }); +}; +const isTestTheme = (theme: AnyObject): boolean => { + return theme[TEST_MARKERS.test.key] === TEST_MARKERS.test.value; +}; + +markAsTest(TestTheme); + +describe('ThemeFactory', () => { + describe('create() should return inherited theme', () => { + test('with args [theme]', () => { + const theme = ThemeFactory.create(myTheme); + + expect(theme.brand).toEqual(myTheme.brand); + expect(theme.black).toEqual(BasicTheme.black); + }); + test('with args [theme, baseTheme]', () => { + const theme = ThemeFactory.create(myTheme, TestTheme); + + expect(theme.brand).toEqual(myTheme.brand); + expect(theme.bgSecondary).toEqual(TestTheme.bgSecondary); + }); + }); + describe('overrideDefaultTheme()', () => { + test('markers should be overridden', () => { + const theme = applyMarkers(ThemeFactory.create(myTheme, TestTheme), [markAsTest]); + + ThemeFactory.overrideBaseTheme(theme); + + const consumedTheme = getConsumedTheme(); + + expect(isTestTheme(consumedTheme)).toBeTruthy(); + }); + test('variables should be overridden', () => { + ThemeFactory.overrideBaseTheme(ThemeFactory.create(myTheme)); + + const consumedTheme = getConsumedTheme(); + + expect(consumedTheme.brand).toEqual(myTheme.brand); + expect(consumedTheme.bgDefault).toEqual(myTheme.bgDefault); + }); + }); + test('getKeys()', () => { + const keys_1 = ThemeFactory.getKeys(TestTheme); + const keys_2 = ThemeFactory.getKeys(BasicTheme); + + expect(keys_1).toEqual(keys_2); + }); +}); diff --git a/packages/react-ui/internal/themes/__tests__/Theming-test.tsx b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx similarity index 51% rename from packages/react-ui/internal/themes/__tests__/Theming-test.tsx rename to packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx index 98a6c58a09b..fd9550dd9d5 100644 --- a/packages/react-ui/internal/themes/__tests__/Theming-test.tsx +++ b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx @@ -1,7 +1,3 @@ -import { render } from '@testing-library/react'; -import React from 'react'; - -import { ThemeContext } from '../../../lib/theming/ThemeContext'; import { applyMarkers, createThemeFromClass, @@ -12,11 +8,10 @@ import { Marker, markThemeVersion, REACT_UI_THEME_MARKERS, -} from '../../../lib/theming/ThemeHelpers'; -import { ThemeFactory } from '../../../lib/theming/ThemeFactory'; -import { Theme } from '../../../lib/theming/Theme'; -import { AnyObject } from '../../../lib/utils'; -import { BasicTheme, BasicThemeClass } from '../BasicTheme'; +} from '../ThemeHelpers'; +import { ThemeFactory } from '../ThemeFactory'; +import { AnyObject } from '../../utils'; +import { BasicTheme, BasicThemeClass } from '../../../internal/themes/BasicTheme'; const TEST_MARKERS = { test: { @@ -28,22 +23,6 @@ const TEST_MARKERS = { // @ts-expect-error: extension for test REACT_UI_THEME_MARKERS.test = TEST_MARKERS.test; -const getConsumedTheme = () => { - let _theme: Theme; - render( - - {(theme) => { - _theme = theme; - return null; - }} - , - ); - // @ts-expect-error: render is sync - return _theme; -}; - -// test theme -const myTheme = { brand: 'custom', bgDefault: 'custom' } as const; const TestTheme = createThemeFromClass( class extends (class {} as typeof BasicThemeClass) { public static bgDefault = 'default'; @@ -69,49 +48,7 @@ const isTestTheme = (theme: AnyObject): boolean => { markAsTest(TestTheme); -describe('Theming', () => { - describe('ThemeFactory', () => { - describe('create() should return inherited theme', () => { - test('with args [theme]', () => { - const theme = ThemeFactory.create(myTheme); - - expect(theme.brand).toEqual(myTheme.brand); - expect(theme.black).toEqual(BasicTheme.black); - }); - test('with args [theme, baseTheme]', () => { - const theme = ThemeFactory.create(myTheme, TestTheme); - - expect(theme.brand).toEqual(myTheme.brand); - expect(theme.bgSecondary).toEqual(TestTheme.bgSecondary); - }); - }); - describe('overrideDefaultTheme()', () => { - test('markers should be overridden', () => { - const theme = applyMarkers(ThemeFactory.create(myTheme, TestTheme), [markAsTest]); - - ThemeFactory.overrideBaseTheme(theme); - - const consumedTheme = getConsumedTheme(); - - expect(isTestTheme(consumedTheme)).toBeTruthy(); - }); - test('variables should be overridden', () => { - ThemeFactory.overrideBaseTheme(ThemeFactory.create(myTheme)); - - const consumedTheme = getConsumedTheme(); - - expect(consumedTheme.brand).toEqual(myTheme.brand); - expect(consumedTheme.bgDefault).toEqual(myTheme.bgDefault); - }); - }); - test('getKeys()', () => { - const keys_1 = ThemeFactory.getKeys(TestTheme); - const keys_2 = ThemeFactory.getKeys(BasicTheme); - - expect(keys_1).toEqual(keys_2); - }); - }); - +describe('ThemeHelpers', () => { describe('exposeGetters', () => { const theme = class extends (class {} as typeof BasicThemeClass) { public static get errorText() { @@ -136,7 +73,7 @@ describe('Theming', () => { return this.black + this.blue; } }, - { prototypeTheme: BasicTheme, themeMarkers: [markAsDarkTheme, markThemeVersion('1_0')] }, + { prototypeTheme: BasicTheme, themeMarkers: [markAsDarkTheme, markThemeVersion(1, 0)] }, ); test('should inherit prototype theme', () => { @@ -152,36 +89,36 @@ describe('Theming', () => { expect(isDarkTheme(theme)).toBe(true); }); test('theme version', () => { - expect(isThemeVersionGTE(theme, '1_0')).toBe(true); + expect(isThemeVersionGTE(theme, 1, 0)).toBe(true); }); }); }); describe('applyMarker', () => { test('test marker should mark custom theme', () => { - const theme = applyMarkers(ThemeFactory.create(myTheme), [markAsTest]); + const theme = applyMarkers(ThemeFactory.create(TestTheme), [markAsTest]); expect(isTestTheme(theme)).toBeTruthy(); }); describe('isThemeVersionGTE', () => { - const theme5_1 = applyMarkers(ThemeFactory.create(myTheme), [markThemeVersion('5_1')]); + const theme5_1 = applyMarkers(ThemeFactory.create(TestTheme), [markThemeVersion(5, 1)]); test('5_1 should BE greater or equal that 5_0', () => { - expect(isThemeVersionGTE(theme5_1, '5_0')).toBe(true); + expect(isThemeVersionGTE(theme5_1, 5, 0)).toBe(true); }); test('5_1 should BE greater or equal that 5_1', () => { - expect(isThemeVersionGTE(theme5_1, '5_1')).toBe(true); + expect(isThemeVersionGTE(theme5_1, 5, 1)).toBe(true); }); test('5_1 should NOT BE greater or equal that 5_2', () => { - expect(isThemeVersionGTE(theme5_1, '5_2')).toBe(false); + expect(isThemeVersionGTE(theme5_1, 5, 2)).toBe(false); }); }); describe('isDarkTheme', () => { - const lightTheme = applyMarkers(ThemeFactory.create(myTheme), []); - const darkTheme = applyMarkers(ThemeFactory.create(myTheme), [markAsDarkTheme]); + const lightTheme = applyMarkers(ThemeFactory.create(TestTheme), []); + const darkTheme = applyMarkers(ThemeFactory.create(TestTheme), [markAsDarkTheme]); test('light theme should NOT BE dark', () => { expect(isDarkTheme(lightTheme)).toBe(false); @@ -192,4 +129,39 @@ describe('Theming', () => { }); }); }); + + describe('isThemeVersionGTE', () => { + const themeWithoutVersion = ThemeFactory.create(TestTheme); + const theme5_5 = applyMarkers(ThemeFactory.create(TestTheme), [markThemeVersion(5, 5)]); + + test('no version should always return false', () => { + expect(isThemeVersionGTE(themeWithoutVersion, 5, 0)).toBe(false); + expect(isThemeVersionGTE(themeWithoutVersion, 0, 0)).toBe(false); + expect(isThemeVersionGTE(themeWithoutVersion, 6, 0)).toBe(false); + }); + + test('5_5 should BE greater or equal that 5_0', () => { + expect(isThemeVersionGTE(theme5_5, 5, 0)).toBe(true); + }); + + test('5_5 should BE greater or equal that 5_5', () => { + expect(isThemeVersionGTE(theme5_5, 5, 5)).toBe(true); + }); + + test('5_5 should BE greater or equal that 4_0', () => { + expect(isThemeVersionGTE(theme5_5, 4, 0)).toBe(true); + }); + + test('5_5 should NOT BE greater or equal that 5_6', () => { + expect(isThemeVersionGTE(theme5_5, 5, 6)).toBe(false); + }); + + test('5_5 should NOT BE greater or equal that 5_10', () => { + expect(isThemeVersionGTE(theme5_5, 5, 10)).toBe(false); + }); + + test('5_5 should NOT BE greater or equal that 6_0', () => { + expect(isThemeVersionGTE(theme5_5, 6, 0)).toBe(false); + }); + }); }); diff --git a/packages/react-ui/lib/theming/themes/__tests__/ThemeVersionsComparator-test.tsx b/packages/react-ui/lib/theming/themes/__tests__/ThemeVersionsComparator-test.tsx deleted file mode 100644 index 293c6fe73cf..00000000000 --- a/packages/react-ui/lib/theming/themes/__tests__/ThemeVersionsComparator-test.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import { isVersionEQ, isVersionGT, isVersionGTE } from '../../ThemeVersionsComparator'; - -describe('ThemeVersionsComparator', () => { - describe('isVersionsEQ', () => { - test.each` - a | b | expected - ${''} | ${''} | ${false} - ${'5_0'} | ${''} | ${false} - ${''} | ${'5_0'} | ${false} - ${'5.0'} | ${'5_0'} | ${false} - ${'5_0'} | ${'5.0'} | ${false} - ${'5_'} | ${'5_0'} | ${false} - ${'5_0'} | ${'5_'} | ${false} - ${'5_0_0'} | ${'5_0'} | ${true} - ${'5_0'} | ${'5'} | ${true} - ${'5'} | ${'5_0_0'} | ${true} - ${'5_00'} | ${'5_0'} | ${true} - ${'5_10'} | ${'5_1'} | ${false} - ${'5_01'} | ${'5_0_1'} | ${false} - ${'5_01'} | ${'5_1'} | ${true} - ${'5_1'} | ${'5_0'} | ${false} - ${'5_0_0'} | ${'5_0_0'} | ${true} - ${'6_0_0'} | ${'5_0_0'} | ${false} - ${'5_0_0'} | ${'6_0_0'} | ${false} - ${'5_1_0'} | ${'5_1_0'} | ${true} - ${'5_1_0'} | ${'5_0_0'} | ${false} - ${'5_0_0'} | ${'5_1_0'} | ${false} - ${'5_0_1'} | ${'5_0_1'} | ${true} - ${'5_0_1'} | ${'5_0_0'} | ${false} - ${'5_0_0'} | ${'5_0_1'} | ${false} - ${'5_0'} | ${'5_0'} | ${true} - ${'6_0'} | ${'5_0'} | ${false} - ${'5_0'} | ${'6_0'} | ${false} - ${'5_1'} | ${'5_1'} | ${true} - ${'5_1'} | ${'5_0'} | ${false} - ${'5_0'} | ${'5_1'} | ${false} - ${'5'} | ${'5'} | ${true} - ${'6'} | ${'5'} | ${false} - ${'5'} | ${'6'} | ${false} - `('isVersionsEQ($a, $b) returns $expected', ({ a, b, expected }) => { - expect(isVersionEQ(a, b)).toBe(expected); - }); - }); - - describe('isVersionsGT', () => { - test.each` - a | b | expected - ${''} | ${''} | ${false} - ${'5_0'} | ${''} | ${false} - ${''} | ${'5_0'} | ${false} - ${'5.0'} | ${'5_0'} | ${false} - ${'5_0'} | ${'5.0'} | ${false} - ${'5_'} | ${'5_0'} | ${false} - ${'5_0'} | ${'5_'} | ${false} - ${'5_0_0'} | ${'5_0'} | ${false} - ${'5_0'} | ${'5'} | ${false} - ${'5'} | ${'5_0_0'} | ${false} - ${'5_00'} | ${'5_0'} | ${false} - ${'5_10'} | ${'5_1'} | ${true} - ${'5_01'} | ${'5_0_1'} | ${true} - ${'5_01'} | ${'5_1'} | ${false} - ${'5_1'} | ${'5_0'} | ${true} - ${'5_0_0'} | ${'5_0_0'} | ${false} - ${'6_0_0'} | ${'5_0_0'} | ${true} - ${'5_0_0'} | ${'6_0_0'} | ${false} - ${'5_1_0'} | ${'5_1_0'} | ${false} - ${'5_1_0'} | ${'5_0_0'} | ${true} - ${'5_0_0'} | ${'5_1_0'} | ${false} - ${'5_0_1'} | ${'5_0_1'} | ${false} - ${'5_0_1'} | ${'5_0_0'} | ${true} - ${'5_0_0'} | ${'5_0_1'} | ${false} - ${'5_0'} | ${'5_0'} | ${false} - ${'6_0'} | ${'5_0'} | ${true} - ${'5_0'} | ${'6_0'} | ${false} - ${'5_1'} | ${'5_1'} | ${false} - ${'5_1'} | ${'5_0'} | ${true} - ${'5_0'} | ${'5_1'} | ${false} - ${'5'} | ${'5'} | ${false} - ${'6'} | ${'5'} | ${true} - ${'5'} | ${'6'} | ${false} - `('isVersionsGT($a, $b) returns $expected', ({ a, b, expected }) => { - expect(isVersionGT(a, b)).toBe(expected); - }); - }); - - describe('isVersionsGTE', () => { - test.each` - a | b | expected - ${''} | ${''} | ${false} - ${'5_0'} | ${''} | ${false} - ${''} | ${'5_0'} | ${false} - ${'5.0'} | ${'5_0'} | ${false} - ${'5_0'} | ${'5.0'} | ${false} - ${'5_'} | ${'5_0'} | ${false} - ${'5_0'} | ${'5_'} | ${false} - ${'5_0_0'} | ${'5_0'} | ${true} - ${'5_0'} | ${'5'} | ${true} - ${'5'} | ${'5_0_0'} | ${true} - ${'5_00'} | ${'5_0'} | ${true} - ${'5_10'} | ${'5_1'} | ${true} - ${'5_01'} | ${'5_0_1'} | ${true} - ${'5_01'} | ${'5_1'} | ${true} - ${'5_1'} | ${'5_0'} | ${true} - ${'5_0_0'} | ${'5_0_0'} | ${true} - ${'6_0_0'} | ${'5_0_0'} | ${true} - ${'5_0_0'} | ${'6_0_0'} | ${false} - ${'5_1_0'} | ${'5_1_0'} | ${true} - ${'5_1_0'} | ${'5_0_0'} | ${true} - ${'5_0_0'} | ${'5_1_0'} | ${false} - ${'5_0_1'} | ${'5_0_1'} | ${true} - ${'5_0_1'} | ${'5_0_0'} | ${true} - ${'5_0_0'} | ${'5_0_1'} | ${false} - ${'5_0'} | ${'5_0'} | ${true} - ${'6_0'} | ${'5_0'} | ${true} - ${'5_0'} | ${'6_0'} | ${false} - ${'5_1'} | ${'5_1'} | ${true} - ${'5_1'} | ${'5_0'} | ${true} - ${'5_0'} | ${'5_1'} | ${false} - ${'5'} | ${'5'} | ${true} - ${'6'} | ${'5'} | ${true} - ${'5'} | ${'6'} | ${false} - `('isVersionsGTE($a, $b) returns $expected', ({ a, b, expected }) => { - expect(isVersionGTE(a, b)).toBe(expected); - }); - }); -}); diff --git a/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx b/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx index 5a13dc9aa88..8301e759bed 100644 --- a/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx +++ b/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx @@ -11,8 +11,8 @@ describe('Themes', () => { describe('LIGHT_THEME_5_0', () => { test('should be 5_0 version', () => { - expect(isThemeVersionGTE(LIGHT_THEME_5_0, '5_0')).toBe(true); - expect(isThemeVersionGTE(LIGHT_THEME_5_0, '5_1')).toBe(false); + expect(isThemeVersionGTE(LIGHT_THEME_5_0, 5, 0)).toBe(true); + expect(isThemeVersionGTE(LIGHT_THEME_5_0, 5, 1)).toBe(false); }); test('should not be dark', () => { expect(isDarkTheme(LIGHT_THEME_5_0)).toBe(false); @@ -21,8 +21,8 @@ describe('Themes', () => { describe('LIGHT_THEME_5_1', () => { test('should be 5_1 version', () => { - expect(isThemeVersionGTE(LIGHT_THEME_5_1, '5_0')).toBe(true); - expect(isThemeVersionGTE(LIGHT_THEME_5_1, '5_1')).toBe(true); + expect(isThemeVersionGTE(LIGHT_THEME_5_1, 5, 0)).toBe(true); + expect(isThemeVersionGTE(LIGHT_THEME_5_1, 5, 1)).toBe(true); }); test('should not be dark', () => { expect(isDarkTheme(LIGHT_THEME_5_1)).toBe(false); @@ -37,8 +37,8 @@ describe('Themes', () => { describe('DARK_THEME_5_0', () => { test('should be 5_0 version', () => { - expect(isThemeVersionGTE(DARK_THEME_5_0, '5_0')).toBe(true); - expect(isThemeVersionGTE(DARK_THEME_5_0, '5_1')).toBe(false); + expect(isThemeVersionGTE(DARK_THEME_5_0, 5, 0)).toBe(true); + expect(isThemeVersionGTE(DARK_THEME_5_0, 5, 1)).toBe(false); }); test('should be dark', () => { expect(isDarkTheme(DARK_THEME_5_0)).toBe(true); @@ -47,8 +47,8 @@ describe('Themes', () => { describe('DARK_THEME_5_1', () => { test('should be 5_1 version', () => { - expect(isThemeVersionGTE(DARK_THEME_5_1, '5_0')).toBe(true); - expect(isThemeVersionGTE(DARK_THEME_5_1, '5_1')).toBe(true); + expect(isThemeVersionGTE(DARK_THEME_5_1, 5, 0)).toBe(true); + expect(isThemeVersionGTE(DARK_THEME_5_1, 5, 1)).toBe(true); }); test('should be dark', () => { expect(isDarkTheme(DARK_THEME_5_1)).toBe(true); From 637f618d510e2ba8474761c3f44ae58d6fda2260 Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Wed, 12 Feb 2025 11:20:54 +0500 Subject: [PATCH 13/27] chore: wip --- .../theming/__tests__/ThemeHelpers-test.tsx | 66 ++++++++----------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx index fd9550dd9d5..db4147ba1d1 100644 --- a/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx +++ b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx @@ -13,16 +13,6 @@ import { ThemeFactory } from '../ThemeFactory'; import { AnyObject } from '../../utils'; import { BasicTheme, BasicThemeClass } from '../../../internal/themes/BasicTheme'; -const TEST_MARKERS = { - test: { - key: '__REACT_UI_TEST_KEY__', - value: true, - }, -}; - -// @ts-expect-error: extension for test -REACT_UI_THEME_MARKERS.test = TEST_MARKERS.test; - const TestTheme = createThemeFromClass( class extends (class {} as typeof BasicThemeClass) { public static bgDefault = 'default'; @@ -31,23 +21,6 @@ const TestTheme = createThemeFromClass( { prototypeTheme: BasicTheme }, ); -// test marker -const markAsTest: Marker = (theme) => { - return Object.create(theme, { - [TEST_MARKERS.test.key]: { - value: TEST_MARKERS.test.value, - writable: false, - enumerable: false, - configurable: false, - }, - }); -}; -const isTestTheme = (theme: AnyObject): boolean => { - return theme[TEST_MARKERS.test.key] === TEST_MARKERS.test.value; -}; - -markAsTest(TestTheme); - describe('ThemeHelpers', () => { describe('exposeGetters', () => { const theme = class extends (class {} as typeof BasicThemeClass) { @@ -95,9 +68,34 @@ describe('ThemeHelpers', () => { }); describe('applyMarker', () => { - test('test marker should mark custom theme', () => { - const theme = applyMarkers(ThemeFactory.create(TestTheme), [markAsTest]); - expect(isTestTheme(theme)).toBeTruthy(); + describe('test marker', () => { + const TEST_MARKER = { + key: '__REACT_UI_TEST_KEY__', + value: true, + }; + + // @ts-expect-error: extension for test + REACT_UI_THEME_MARKERS.test = TEST_MARKER; + + const markAsTest: Marker = (theme) => { + return Object.create(theme, { + [TEST_MARKER.key]: { + value: TEST_MARKER.value, + writable: false, + enumerable: false, + configurable: false, + }, + }); + }; + + const isTestTheme = (theme: AnyObject): boolean => { + return theme[TEST_MARKER.key] === TEST_MARKER.value; + }; + + test('test marker should mark custom theme', () => { + const theme = applyMarkers(ThemeFactory.create(TestTheme), [markAsTest]); + expect(isTestTheme(theme)).toBeTruthy(); + }); }); describe('isThemeVersionGTE', () => { @@ -106,14 +104,6 @@ describe('ThemeHelpers', () => { test('5_1 should BE greater or equal that 5_0', () => { expect(isThemeVersionGTE(theme5_1, 5, 0)).toBe(true); }); - - test('5_1 should BE greater or equal that 5_1', () => { - expect(isThemeVersionGTE(theme5_1, 5, 1)).toBe(true); - }); - - test('5_1 should NOT BE greater or equal that 5_2', () => { - expect(isThemeVersionGTE(theme5_1, 5, 2)).toBe(false); - }); }); describe('isDarkTheme', () => { From bda6a8aafbe199aed083cd8ea699bda31f745b4b Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Wed, 12 Feb 2025 11:28:38 +0500 Subject: [PATCH 14/27] chore: wip --- .../themes/{BasicTheme.ts => AbstractTheme.ts} | 4 ++-- .../react-ui/internal/themes/DarkTheme5_0.ts | 6 +++--- .../react-ui/internal/themes/DarkTheme5_1.ts | 13 ++++++++----- .../react-ui/internal/themes/LightTheme5_0.ts | 13 ++++++++----- .../react-ui/internal/themes/LightTheme5_1.ts | 13 ++++++++----- packages/react-ui/lib/theming/Theme.ts | 6 +++--- packages/react-ui/lib/theming/ThemeFactory.ts | 10 +++++----- .../theming/__tests__/ThemeFactory-test.tsx | 10 +++++----- .../theming/__tests__/ThemeHelpers-test.tsx | 18 +++++++++--------- .../react-ui/lib/theming/themes/LightTheme.ts | 4 ++-- 10 files changed, 53 insertions(+), 44 deletions(-) rename packages/react-ui/internal/themes/{BasicTheme.ts => AbstractTheme.ts} (99%) diff --git a/packages/react-ui/internal/themes/BasicTheme.ts b/packages/react-ui/internal/themes/AbstractTheme.ts similarity index 99% rename from packages/react-ui/internal/themes/BasicTheme.ts rename to packages/react-ui/internal/themes/AbstractTheme.ts index cf13fda528b..7faa2a6dc31 100644 --- a/packages/react-ui/internal/themes/BasicTheme.ts +++ b/packages/react-ui/internal/themes/AbstractTheme.ts @@ -1,7 +1,7 @@ import { createThemeFromClass } from '../../lib/theming/ThemeHelpers'; import * as ColorFunctions from '../../lib/styles/ColorFunctions'; -export class BasicThemeClass { +export class AbstractThemeClass { //#region Common variables public static labGrotesqueBaselineCompensation = '1'; public static brand = '#2291ff'; @@ -2484,4 +2484,4 @@ export class BasicThemeClass { //#endregion } -export const BasicTheme = createThemeFromClass(BasicThemeClass); +export const AbstractTheme = createThemeFromClass(AbstractThemeClass); diff --git a/packages/react-ui/internal/themes/DarkTheme5_0.ts b/packages/react-ui/internal/themes/DarkTheme5_0.ts index 0fa7e4767ce..f5e375b322c 100644 --- a/packages/react-ui/internal/themes/DarkTheme5_0.ts +++ b/packages/react-ui/internal/themes/DarkTheme5_0.ts @@ -1,9 +1,9 @@ import { markAsDarkTheme, createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; -import { BasicTheme, BasicThemeClass } from './BasicTheme'; +import { AbstractTheme, AbstractThemeClass } from './AbstractTheme'; export const DarkTheme5_0 = createThemeFromClass( - class DarkTheme5_0 extends (class {} as typeof BasicThemeClass) { + class DarkTheme5_0 extends (class {} as typeof AbstractThemeClass) { //#region Common variables public static grayXLight = '#313131'; public static gray = 'rgba(255, 255, 255, 0.48)'; @@ -552,7 +552,7 @@ export const DarkTheme5_0 = createThemeFromClass( //#endregion }, { - prototypeTheme: BasicTheme, + prototypeTheme: AbstractTheme, themeMarkers: [markAsDarkTheme, markThemeVersion(5, 0)], }, ); diff --git a/packages/react-ui/internal/themes/DarkTheme5_1.ts b/packages/react-ui/internal/themes/DarkTheme5_1.ts index b25538a1389..b1523335d6f 100644 --- a/packages/react-ui/internal/themes/DarkTheme5_1.ts +++ b/packages/react-ui/internal/themes/DarkTheme5_1.ts @@ -1,9 +1,12 @@ import { createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; -import { BasicThemeClass } from './BasicTheme'; +import { AbstractThemeClass } from './AbstractTheme'; import { DarkTheme5_0 } from './DarkTheme5_0'; -export const DarkTheme5_1 = createThemeFromClass(class DarkTheme5_1 extends (class {} as typeof BasicThemeClass) {}, { - prototypeTheme: DarkTheme5_0, - themeMarkers: [markThemeVersion(5, 1)], -}); +export const DarkTheme5_1 = createThemeFromClass( + class DarkTheme5_1 extends (class {} as typeof AbstractThemeClass) {}, + { + prototypeTheme: DarkTheme5_0, + themeMarkers: [markThemeVersion(5, 1)], + }, +); diff --git a/packages/react-ui/internal/themes/LightTheme5_0.ts b/packages/react-ui/internal/themes/LightTheme5_0.ts index 0d5a9df791b..a851eea9b47 100644 --- a/packages/react-ui/internal/themes/LightTheme5_0.ts +++ b/packages/react-ui/internal/themes/LightTheme5_0.ts @@ -1,8 +1,11 @@ import { createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; -import { BasicTheme, BasicThemeClass } from './BasicTheme'; +import { AbstractTheme, AbstractThemeClass } from './AbstractTheme'; -export const LightTheme5_0 = createThemeFromClass(class LightTheme5_0 extends (class {} as typeof BasicThemeClass) {}, { - prototypeTheme: BasicTheme, - themeMarkers: [markThemeVersion(5, 0)], -}); +export const LightTheme5_0 = createThemeFromClass( + class LightTheme5_0 extends (class {} as typeof AbstractThemeClass) {}, + { + prototypeTheme: AbstractTheme, + themeMarkers: [markThemeVersion(5, 0)], + }, +); diff --git a/packages/react-ui/internal/themes/LightTheme5_1.ts b/packages/react-ui/internal/themes/LightTheme5_1.ts index d89b5e42fae..9426ad18636 100644 --- a/packages/react-ui/internal/themes/LightTheme5_1.ts +++ b/packages/react-ui/internal/themes/LightTheme5_1.ts @@ -1,9 +1,12 @@ import { createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; -import { BasicThemeClass } from './BasicTheme'; +import { AbstractThemeClass } from './AbstractTheme'; import { LightTheme5_0 } from './LightTheme5_0'; -export const LightTheme5_1 = createThemeFromClass(class LightTheme5_1 extends (class {} as typeof BasicThemeClass) {}, { - prototypeTheme: LightTheme5_0, - themeMarkers: [markThemeVersion(5, 1)], -}); +export const LightTheme5_1 = createThemeFromClass( + class LightTheme5_1 extends (class {} as typeof AbstractThemeClass) {}, + { + prototypeTheme: LightTheme5_0, + themeMarkers: [markThemeVersion(5, 1)], + }, +); diff --git a/packages/react-ui/lib/theming/Theme.ts b/packages/react-ui/lib/theming/Theme.ts index cc19058a9fb..a8df0612638 100644 --- a/packages/react-ui/lib/theming/Theme.ts +++ b/packages/react-ui/lib/theming/Theme.ts @@ -1,4 +1,4 @@ -import { BasicThemeClass } from '../../internal/themes/BasicTheme'; +import { AbstractThemeClass } from '../../internal/themes/AbstractTheme'; -export type Theme = Readonly; -export type ThemeIn = Partial; +export type Theme = Readonly; +export type ThemeIn = Partial; diff --git a/packages/react-ui/lib/theming/ThemeFactory.ts b/packages/react-ui/lib/theming/ThemeFactory.ts index 055c8bf8d5c..fd12e1aff1b 100644 --- a/packages/react-ui/lib/theming/ThemeFactory.ts +++ b/packages/react-ui/lib/theming/ThemeFactory.ts @@ -1,26 +1,26 @@ -import { BasicTheme } from '../../internal/themes/BasicTheme'; import { isNonNullable, NoInfer } from '../utils'; import { Theme, ThemeIn } from './Theme'; import { findPropertyDescriptor, REACT_UI_THEME_MARKERS } from './ThemeHelpers'; +import { LIGHT_THEME } from './themes/LightTheme'; export class ThemeFactory { public static create(theme: ThemeIn & NoInfer, baseTheme?: Theme): Readonly> { - const base = baseTheme || BasicTheme; + const base = baseTheme || LIGHT_THEME; return this.constructTheme(base, theme); } public static overrideBaseTheme(theme: Theme) { // copying theme variables - ThemeFactory.getKeys(BasicTheme).forEach((variableName) => { + ThemeFactory.getKeys(LIGHT_THEME).forEach((variableName) => { const descriptor = findPropertyDescriptor(theme, variableName); - Object.defineProperty(BasicTheme, variableName, descriptor); + Object.defineProperty(LIGHT_THEME, variableName, descriptor); }); // copying theme markers Object.values(REACT_UI_THEME_MARKERS).forEach((marker) => { const descriptor = findPropertyDescriptor(theme, marker.key); - Object.defineProperty(BasicTheme, marker.key, descriptor); + Object.defineProperty(LIGHT_THEME, marker.key, descriptor); }); } diff --git a/packages/react-ui/lib/theming/__tests__/ThemeFactory-test.tsx b/packages/react-ui/lib/theming/__tests__/ThemeFactory-test.tsx index 33d86cc8f0b..b7912460a98 100644 --- a/packages/react-ui/lib/theming/__tests__/ThemeFactory-test.tsx +++ b/packages/react-ui/lib/theming/__tests__/ThemeFactory-test.tsx @@ -6,7 +6,7 @@ import { applyMarkers, createThemeFromClass, Marker, REACT_UI_THEME_MARKERS } fr import { ThemeFactory } from '../ThemeFactory'; import { Theme } from '../Theme'; import { AnyObject } from '../../utils'; -import { BasicTheme, BasicThemeClass } from '../../../internal/themes/BasicTheme'; +import { AbstractTheme, AbstractThemeClass } from '../../../internal/themes/AbstractTheme'; const TEST_MARKERS = { test: { @@ -35,11 +35,11 @@ const getConsumedTheme = () => { // test theme const myTheme = { brand: 'custom', bgDefault: 'custom' } as const; const TestTheme = createThemeFromClass( - class extends (class {} as typeof BasicThemeClass) { + class extends (class {} as typeof AbstractThemeClass) { public static bgDefault = 'default'; public static bgSecondary = 'default'; }, - { prototypeTheme: BasicTheme }, + { prototypeTheme: AbstractTheme }, ); // test marker @@ -65,7 +65,7 @@ describe('ThemeFactory', () => { const theme = ThemeFactory.create(myTheme); expect(theme.brand).toEqual(myTheme.brand); - expect(theme.black).toEqual(BasicTheme.black); + expect(theme.black).toEqual(AbstractTheme.black); }); test('with args [theme, baseTheme]', () => { const theme = ThemeFactory.create(myTheme, TestTheme); @@ -95,7 +95,7 @@ describe('ThemeFactory', () => { }); test('getKeys()', () => { const keys_1 = ThemeFactory.getKeys(TestTheme); - const keys_2 = ThemeFactory.getKeys(BasicTheme); + const keys_2 = ThemeFactory.getKeys(AbstractTheme); expect(keys_1).toEqual(keys_2); }); diff --git a/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx index db4147ba1d1..d6488247b73 100644 --- a/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx +++ b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx @@ -11,19 +11,19 @@ import { } from '../ThemeHelpers'; import { ThemeFactory } from '../ThemeFactory'; import { AnyObject } from '../../utils'; -import { BasicTheme, BasicThemeClass } from '../../../internal/themes/BasicTheme'; +import { AbstractTheme, AbstractThemeClass } from '../../../internal/themes/AbstractTheme'; const TestTheme = createThemeFromClass( - class extends (class {} as typeof BasicThemeClass) { + class extends (class {} as typeof AbstractThemeClass) { public static bgDefault = 'default'; public static bgSecondary = 'default'; }, - { prototypeTheme: BasicTheme }, + { prototypeTheme: AbstractTheme }, ); describe('ThemeHelpers', () => { describe('exposeGetters', () => { - const theme = class extends (class {} as typeof BasicThemeClass) { + const theme = class extends (class {} as typeof AbstractThemeClass) { public static get errorText() { return 'red'; } @@ -41,16 +41,16 @@ describe('ThemeHelpers', () => { describe('createThemeFromClass', () => { const theme = createThemeFromClass( - class extends (class {} as typeof BasicThemeClass) { + class extends (class {} as typeof AbstractThemeClass) { public static get errorText() { return this.black + this.blue; } }, - { prototypeTheme: BasicTheme, themeMarkers: [markAsDarkTheme, markThemeVersion(1, 0)] }, + { prototypeTheme: AbstractTheme, themeMarkers: [markAsDarkTheme, markThemeVersion(1, 0)] }, ); test('should inherit prototype theme', () => { - expect(theme.errorText).toBe(BasicTheme.black + BasicTheme.blue); + expect(theme.errorText).toBe(AbstractTheme.black + AbstractTheme.blue); }); test('should expose getters', () => { @@ -121,8 +121,8 @@ describe('ThemeHelpers', () => { }); describe('isThemeVersionGTE', () => { - const themeWithoutVersion = ThemeFactory.create(TestTheme); - const theme5_5 = applyMarkers(ThemeFactory.create(TestTheme), [markThemeVersion(5, 5)]); + const themeWithoutVersion = TestTheme; + const theme5_5 = applyMarkers(ThemeFactory.create({}, TestTheme), [markThemeVersion(5, 5)]); test('no version should always return false', () => { expect(isThemeVersionGTE(themeWithoutVersion, 5, 0)).toBe(false); diff --git a/packages/react-ui/lib/theming/themes/LightTheme.ts b/packages/react-ui/lib/theming/themes/LightTheme.ts index c930410b630..93a8fda0832 100644 --- a/packages/react-ui/lib/theming/themes/LightTheme.ts +++ b/packages/react-ui/lib/theming/themes/LightTheme.ts @@ -2,7 +2,7 @@ import { ThemeFactory } from '../ThemeFactory'; import { LightTheme5_0 } from '../../../internal/themes/LightTheme5_0'; import { LightTheme5_1 } from '../../../internal/themes/LightTheme5_1'; -export const LIGHT_THEME_5_0 = ThemeFactory.create({}, LightTheme5_0); -export const LIGHT_THEME_5_1 = ThemeFactory.create({}, LightTheme5_1); +export const LIGHT_THEME_5_0 = LightTheme5_0; +export const LIGHT_THEME_5_1 = LightTheme5_1; export const LIGHT_THEME = LIGHT_THEME_5_0; From f77d1b9061f42029a90c0ecb3d135e1ef0063c8f Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Wed, 12 Feb 2025 13:22:12 +0500 Subject: [PATCH 15/27] chore: wip --- packages/react-ui/lib/theming/ThemeContext.ts | 4 ++-- packages/react-ui/lib/theming/ThemeFactory.ts | 14 ++++++++++---- packages/react-ui/lib/theming/ThemeHelpers.ts | 4 ++-- packages/react-ui/lib/theming/themes/LightTheme.ts | 1 - 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/react-ui/lib/theming/ThemeContext.ts b/packages/react-ui/lib/theming/ThemeContext.ts index 46052a77d0b..015a32a7f65 100644 --- a/packages/react-ui/lib/theming/ThemeContext.ts +++ b/packages/react-ui/lib/theming/ThemeContext.ts @@ -1,8 +1,8 @@ import React from 'react'; -import { LIGHT_THEME } from './themes/LightTheme'; +import { ThemeFactory } from './ThemeFactory'; -export const ThemeContext = React.createContext(LIGHT_THEME); +export const ThemeContext = React.createContext(ThemeFactory.getDefaultTheme()); ThemeContext.displayName = 'ThemeContext'; ThemeContext.__KONTUR_REACT_UI__ = 'ThemeContext'; diff --git a/packages/react-ui/lib/theming/ThemeFactory.ts b/packages/react-ui/lib/theming/ThemeFactory.ts index fd12e1aff1b..8d637a135c0 100644 --- a/packages/react-ui/lib/theming/ThemeFactory.ts +++ b/packages/react-ui/lib/theming/ThemeFactory.ts @@ -5,22 +5,28 @@ import { findPropertyDescriptor, REACT_UI_THEME_MARKERS } from './ThemeHelpers'; import { LIGHT_THEME } from './themes/LightTheme'; export class ThemeFactory { + public static defaultTheme: Theme = Object.create(LIGHT_THEME); + + public static getDefaultTheme() { + return this.defaultTheme; + } + public static create(theme: ThemeIn & NoInfer, baseTheme?: Theme): Readonly> { - const base = baseTheme || LIGHT_THEME; + const base = baseTheme || this.defaultTheme; return this.constructTheme(base, theme); } public static overrideBaseTheme(theme: Theme) { // copying theme variables - ThemeFactory.getKeys(LIGHT_THEME).forEach((variableName) => { + ThemeFactory.getKeys(this.defaultTheme).forEach((variableName) => { const descriptor = findPropertyDescriptor(theme, variableName); - Object.defineProperty(LIGHT_THEME, variableName, descriptor); + Object.defineProperty(this.defaultTheme, variableName, descriptor); }); // copying theme markers Object.values(REACT_UI_THEME_MARKERS).forEach((marker) => { const descriptor = findPropertyDescriptor(theme, marker.key); - Object.defineProperty(LIGHT_THEME, marker.key, descriptor); + Object.defineProperty(this.defaultTheme, marker.key, descriptor); }); } diff --git a/packages/react-ui/lib/theming/ThemeHelpers.ts b/packages/react-ui/lib/theming/ThemeHelpers.ts index 03d5d194420..8e7faccf4c4 100644 --- a/packages/react-ui/lib/theming/ThemeHelpers.ts +++ b/packages/react-ui/lib/theming/ThemeHelpers.ts @@ -103,7 +103,7 @@ export function createThemeFromClass( prototypeTheme?: P; themeMarkers?: Markers; }, -): T { +) { const { prototypeTheme, themeMarkers = [] } = options || {}; if (prototypeTheme) { @@ -112,5 +112,5 @@ export function createThemeFromClass( const theme = applyMarkers(exposeGetters(themeObject), themeMarkers); - return theme; + return Object.freeze(theme); } diff --git a/packages/react-ui/lib/theming/themes/LightTheme.ts b/packages/react-ui/lib/theming/themes/LightTheme.ts index 93a8fda0832..445c1b9cb8f 100644 --- a/packages/react-ui/lib/theming/themes/LightTheme.ts +++ b/packages/react-ui/lib/theming/themes/LightTheme.ts @@ -1,4 +1,3 @@ -import { ThemeFactory } from '../ThemeFactory'; import { LightTheme5_0 } from '../../../internal/themes/LightTheme5_0'; import { LightTheme5_1 } from '../../../internal/themes/LightTheme5_1'; From 7f0102d85206b4b9f74c25cd68d71e4b07bfd3a3 Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Wed, 12 Feb 2025 15:56:42 +0500 Subject: [PATCH 16/27] chore: wip --- ...ThemeFactory-test.tsx => Theming-test.tsx} | 67 +++++++++++-------- .../react-ui/lib/theming/themes/DarkTheme.ts | 5 +- 2 files changed, 40 insertions(+), 32 deletions(-) rename packages/react-ui/lib/theming/__tests__/{ThemeFactory-test.tsx => Theming-test.tsx} (50%) diff --git a/packages/react-ui/lib/theming/__tests__/ThemeFactory-test.tsx b/packages/react-ui/lib/theming/__tests__/Theming-test.tsx similarity index 50% rename from packages/react-ui/lib/theming/__tests__/ThemeFactory-test.tsx rename to packages/react-ui/lib/theming/__tests__/Theming-test.tsx index b7912460a98..a340a1652ac 100644 --- a/packages/react-ui/lib/theming/__tests__/ThemeFactory-test.tsx +++ b/packages/react-ui/lib/theming/__tests__/Theming-test.tsx @@ -59,44 +59,53 @@ const isTestTheme = (theme: AnyObject): boolean => { markAsTest(TestTheme); -describe('ThemeFactory', () => { - describe('create() should return inherited theme', () => { - test('with args [theme]', () => { - const theme = ThemeFactory.create(myTheme); - - expect(theme.brand).toEqual(myTheme.brand); - expect(theme.black).toEqual(AbstractTheme.black); +describe('Theming', () => { + describe('ThemeFactory', () => { + describe('create() should return inherited theme', () => { + test('with args [theme]', () => { + const theme = ThemeFactory.create(myTheme); + + expect(theme.brand).toEqual(myTheme.brand); + expect(theme.black).toEqual(AbstractTheme.black); + }); + test('with args [theme, baseTheme]', () => { + const theme = ThemeFactory.create(myTheme, TestTheme); + + expect(theme.brand).toEqual(myTheme.brand); + expect(theme.bgSecondary).toEqual(TestTheme.bgSecondary); + }); }); - test('with args [theme, baseTheme]', () => { - const theme = ThemeFactory.create(myTheme, TestTheme); + describe('overrideDefaultTheme()', () => { + test('markers should be overridden', () => { + const theme = applyMarkers(ThemeFactory.create(myTheme, TestTheme), [markAsTest]); - expect(theme.brand).toEqual(myTheme.brand); - expect(theme.bgSecondary).toEqual(TestTheme.bgSecondary); - }); - }); - describe('overrideDefaultTheme()', () => { - test('markers should be overridden', () => { - const theme = applyMarkers(ThemeFactory.create(myTheme, TestTheme), [markAsTest]); + ThemeFactory.overrideBaseTheme(theme); - ThemeFactory.overrideBaseTheme(theme); + const consumedTheme = getConsumedTheme(); - const consumedTheme = getConsumedTheme(); + expect(isTestTheme(consumedTheme)).toBeTruthy(); + }); + test('variables should be overridden', () => { + ThemeFactory.overrideBaseTheme(ThemeFactory.create(myTheme)); - expect(isTestTheme(consumedTheme)).toBeTruthy(); - }); - test('variables should be overridden', () => { - ThemeFactory.overrideBaseTheme(ThemeFactory.create(myTheme)); + const consumedTheme = getConsumedTheme(); - const consumedTheme = getConsumedTheme(); + expect(consumedTheme.brand).toEqual(myTheme.brand); + expect(consumedTheme.bgDefault).toEqual(myTheme.bgDefault); + }); + }); + test('getKeys()', () => { + const keys_1 = ThemeFactory.getKeys(TestTheme); + const keys_2 = ThemeFactory.getKeys(AbstractTheme); - expect(consumedTheme.brand).toEqual(myTheme.brand); - expect(consumedTheme.bgDefault).toEqual(myTheme.bgDefault); + expect(keys_1).toEqual(keys_2); }); }); - test('getKeys()', () => { - const keys_1 = ThemeFactory.getKeys(TestTheme); - const keys_2 = ThemeFactory.getKeys(AbstractTheme); + describe('applyMarkers() should mark theme', () => { + test('should mark custom theme', () => { + const theme = applyMarkers(ThemeFactory.create(myTheme), [markAsTest]); - expect(keys_1).toEqual(keys_2); + expect(isTestTheme(theme)).toBeTruthy(); + }); }); }); diff --git a/packages/react-ui/lib/theming/themes/DarkTheme.ts b/packages/react-ui/lib/theming/themes/DarkTheme.ts index e37d5ae2421..e1a71ba8c24 100644 --- a/packages/react-ui/lib/theming/themes/DarkTheme.ts +++ b/packages/react-ui/lib/theming/themes/DarkTheme.ts @@ -1,8 +1,7 @@ -import { ThemeFactory } from '../ThemeFactory'; import { DarkTheme5_0 } from '../../../internal/themes/DarkTheme5_0'; import { DarkTheme5_1 } from '../../../internal/themes/DarkTheme5_1'; -export const DARK_THEME_5_0 = ThemeFactory.create({}, DarkTheme5_0); -export const DARK_THEME_5_1 = ThemeFactory.create({}, DarkTheme5_1); +export const DARK_THEME_5_0 = DarkTheme5_0; +export const DARK_THEME_5_1 = DarkTheme5_1; export const DARK_THEME = DARK_THEME_5_0; From 848044af14d5fd9d2edf3757f460ddca7951ffc6 Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Fri, 14 Feb 2025 11:22:20 +0500 Subject: [PATCH 17/27] chore: wip --- packages/react-ui/internal/themes/AbstractTheme.ts | 2 ++ .../react-ui/internal/themes/BasicLightTheme.ts | 5 +++++ packages/react-ui/internal/themes/DarkTheme5_0.ts | 4 ++-- packages/react-ui/internal/themes/DarkTheme5_1.ts | 13 +++++-------- packages/react-ui/internal/themes/LightTheme5_0.ts | 13 +++++-------- packages/react-ui/internal/themes/LightTheme5_1.ts | 13 +++++-------- packages/react-ui/lib/theming/Theme.ts | 4 +++- .../lib/theming/__tests__/ThemeHelpers-test.tsx | 8 ++++---- .../react-ui/lib/theming/__tests__/Theming-test.tsx | 4 ++-- 9 files changed, 33 insertions(+), 33 deletions(-) create mode 100644 packages/react-ui/internal/themes/BasicLightTheme.ts diff --git a/packages/react-ui/internal/themes/AbstractTheme.ts b/packages/react-ui/internal/themes/AbstractTheme.ts index 7faa2a6dc31..d7331868088 100644 --- a/packages/react-ui/internal/themes/AbstractTheme.ts +++ b/packages/react-ui/internal/themes/AbstractTheme.ts @@ -2485,3 +2485,5 @@ export class AbstractThemeClass { } export const AbstractTheme = createThemeFromClass(AbstractThemeClass); + +export const BasicThemeToExtend = class {} as typeof AbstractThemeClass; diff --git a/packages/react-ui/internal/themes/BasicLightTheme.ts b/packages/react-ui/internal/themes/BasicLightTheme.ts new file mode 100644 index 00000000000..39b21d6f61b --- /dev/null +++ b/packages/react-ui/internal/themes/BasicLightTheme.ts @@ -0,0 +1,5 @@ +/** + * @deprecated use AbstractThemeClass or BasicThemeToExtend + */ + +export { AbstractThemeClass as BasicLightThemeInternal } from './AbstractTheme'; diff --git a/packages/react-ui/internal/themes/DarkTheme5_0.ts b/packages/react-ui/internal/themes/DarkTheme5_0.ts index f5e375b322c..c6f66984c61 100644 --- a/packages/react-ui/internal/themes/DarkTheme5_0.ts +++ b/packages/react-ui/internal/themes/DarkTheme5_0.ts @@ -1,9 +1,9 @@ import { markAsDarkTheme, createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; -import { AbstractTheme, AbstractThemeClass } from './AbstractTheme'; +import { AbstractTheme, BasicThemeToExtend } from './AbstractTheme'; export const DarkTheme5_0 = createThemeFromClass( - class DarkTheme5_0 extends (class {} as typeof AbstractThemeClass) { + class DarkTheme5_0 extends BasicThemeToExtend { //#region Common variables public static grayXLight = '#313131'; public static gray = 'rgba(255, 255, 255, 0.48)'; diff --git a/packages/react-ui/internal/themes/DarkTheme5_1.ts b/packages/react-ui/internal/themes/DarkTheme5_1.ts index b1523335d6f..d266656cff2 100644 --- a/packages/react-ui/internal/themes/DarkTheme5_1.ts +++ b/packages/react-ui/internal/themes/DarkTheme5_1.ts @@ -1,12 +1,9 @@ import { createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; -import { AbstractThemeClass } from './AbstractTheme'; +import { BasicThemeToExtend } from './AbstractTheme'; import { DarkTheme5_0 } from './DarkTheme5_0'; -export const DarkTheme5_1 = createThemeFromClass( - class DarkTheme5_1 extends (class {} as typeof AbstractThemeClass) {}, - { - prototypeTheme: DarkTheme5_0, - themeMarkers: [markThemeVersion(5, 1)], - }, -); +export const DarkTheme5_1 = createThemeFromClass(class DarkTheme5_1 extends BasicThemeToExtend {}, { + prototypeTheme: DarkTheme5_0, + themeMarkers: [markThemeVersion(5, 1)], +}); diff --git a/packages/react-ui/internal/themes/LightTheme5_0.ts b/packages/react-ui/internal/themes/LightTheme5_0.ts index a851eea9b47..fcf8377833f 100644 --- a/packages/react-ui/internal/themes/LightTheme5_0.ts +++ b/packages/react-ui/internal/themes/LightTheme5_0.ts @@ -1,11 +1,8 @@ import { createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; -import { AbstractTheme, AbstractThemeClass } from './AbstractTheme'; +import { AbstractTheme, BasicThemeToExtend } from './AbstractTheme'; -export const LightTheme5_0 = createThemeFromClass( - class LightTheme5_0 extends (class {} as typeof AbstractThemeClass) {}, - { - prototypeTheme: AbstractTheme, - themeMarkers: [markThemeVersion(5, 0)], - }, -); +export const LightTheme5_0 = createThemeFromClass(class LightTheme5_0 extends BasicThemeToExtend {}, { + prototypeTheme: AbstractTheme, + themeMarkers: [markThemeVersion(5, 0)], +}); diff --git a/packages/react-ui/internal/themes/LightTheme5_1.ts b/packages/react-ui/internal/themes/LightTheme5_1.ts index 9426ad18636..5481fab9751 100644 --- a/packages/react-ui/internal/themes/LightTheme5_1.ts +++ b/packages/react-ui/internal/themes/LightTheme5_1.ts @@ -1,12 +1,9 @@ import { createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; -import { AbstractThemeClass } from './AbstractTheme'; +import { BasicThemeToExtend } from './AbstractTheme'; import { LightTheme5_0 } from './LightTheme5_0'; -export const LightTheme5_1 = createThemeFromClass( - class LightTheme5_1 extends (class {} as typeof AbstractThemeClass) {}, - { - prototypeTheme: LightTheme5_0, - themeMarkers: [markThemeVersion(5, 1)], - }, -); +export const LightTheme5_1 = createThemeFromClass(class LightTheme5_1 extends BasicThemeToExtend {}, { + prototypeTheme: LightTheme5_0, + themeMarkers: [markThemeVersion(5, 1)], +}); diff --git a/packages/react-ui/lib/theming/Theme.ts b/packages/react-ui/lib/theming/Theme.ts index a8df0612638..e33522b18e2 100644 --- a/packages/react-ui/lib/theming/Theme.ts +++ b/packages/react-ui/lib/theming/Theme.ts @@ -1,4 +1,6 @@ -import { AbstractThemeClass } from '../../internal/themes/AbstractTheme'; +import { AbstractThemeClass, BasicThemeToExtend } from '../../internal/themes/AbstractTheme'; export type Theme = Readonly; export type ThemeIn = Partial; + +export { BasicThemeToExtend }; diff --git a/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx index d6488247b73..39758d9c613 100644 --- a/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx +++ b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx @@ -11,10 +11,10 @@ import { } from '../ThemeHelpers'; import { ThemeFactory } from '../ThemeFactory'; import { AnyObject } from '../../utils'; -import { AbstractTheme, AbstractThemeClass } from '../../../internal/themes/AbstractTheme'; +import { AbstractTheme, BasicThemeToExtend } from '../../../internal/themes/AbstractTheme'; const TestTheme = createThemeFromClass( - class extends (class {} as typeof AbstractThemeClass) { + class extends BasicThemeToExtend { public static bgDefault = 'default'; public static bgSecondary = 'default'; }, @@ -23,7 +23,7 @@ const TestTheme = createThemeFromClass( describe('ThemeHelpers', () => { describe('exposeGetters', () => { - const theme = class extends (class {} as typeof AbstractThemeClass) { + const theme = class extends BasicThemeToExtend { public static get errorText() { return 'red'; } @@ -41,7 +41,7 @@ describe('ThemeHelpers', () => { describe('createThemeFromClass', () => { const theme = createThemeFromClass( - class extends (class {} as typeof AbstractThemeClass) { + class extends BasicThemeToExtend { public static get errorText() { return this.black + this.blue; } diff --git a/packages/react-ui/lib/theming/__tests__/Theming-test.tsx b/packages/react-ui/lib/theming/__tests__/Theming-test.tsx index a340a1652ac..8e3dc18a248 100644 --- a/packages/react-ui/lib/theming/__tests__/Theming-test.tsx +++ b/packages/react-ui/lib/theming/__tests__/Theming-test.tsx @@ -6,7 +6,7 @@ import { applyMarkers, createThemeFromClass, Marker, REACT_UI_THEME_MARKERS } fr import { ThemeFactory } from '../ThemeFactory'; import { Theme } from '../Theme'; import { AnyObject } from '../../utils'; -import { AbstractTheme, AbstractThemeClass } from '../../../internal/themes/AbstractTheme'; +import { AbstractTheme, BasicThemeToExtend } from '../../../internal/themes/AbstractTheme'; const TEST_MARKERS = { test: { @@ -35,7 +35,7 @@ const getConsumedTheme = () => { // test theme const myTheme = { brand: 'custom', bgDefault: 'custom' } as const; const TestTheme = createThemeFromClass( - class extends (class {} as typeof AbstractThemeClass) { + class extends BasicThemeToExtend { public static bgDefault = 'default'; public static bgSecondary = 'default'; }, From 16b7f0d8d6ce253ce0aacf021b98722719923fc0 Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Fri, 14 Feb 2025 11:53:42 +0500 Subject: [PATCH 18/27] chore: wip --- .../react-ui/internal/themes/BasicLightTheme.ts | 5 +++-- .../themes/{AbstractTheme.ts => BasicTheme.ts} | 6 +++--- packages/react-ui/internal/themes/DarkTheme5_0.ts | 6 +++--- packages/react-ui/internal/themes/DarkTheme5_1.ts | 4 ++-- packages/react-ui/internal/themes/LightTheme5_0.ts | 6 +++--- packages/react-ui/internal/themes/LightTheme5_1.ts | 4 ++-- packages/react-ui/lib/theming/Theme.ts | 8 ++++---- .../lib/theming/__tests__/ThemeHelpers-test.tsx | 14 +++++++------- .../lib/theming/__tests__/Theming-test.tsx | 10 +++++----- 9 files changed, 32 insertions(+), 31 deletions(-) rename packages/react-ui/internal/themes/{AbstractTheme.ts => BasicTheme.ts} (99%) diff --git a/packages/react-ui/internal/themes/BasicLightTheme.ts b/packages/react-ui/internal/themes/BasicLightTheme.ts index 39b21d6f61b..725a249c2d5 100644 --- a/packages/react-ui/internal/themes/BasicLightTheme.ts +++ b/packages/react-ui/internal/themes/BasicLightTheme.ts @@ -1,5 +1,6 @@ /** - * @deprecated use AbstractThemeClass or BasicThemeToExtend + * This file is for backward compatibility only. Remove in 6.0.0. + * @deprecated use BasicThemeClass or BasicThemeClassForExtension */ -export { AbstractThemeClass as BasicLightThemeInternal } from './AbstractTheme'; +export { BasicThemeClass as BasicLightThemeInternal } from './BasicTheme'; diff --git a/packages/react-ui/internal/themes/AbstractTheme.ts b/packages/react-ui/internal/themes/BasicTheme.ts similarity index 99% rename from packages/react-ui/internal/themes/AbstractTheme.ts rename to packages/react-ui/internal/themes/BasicTheme.ts index d7331868088..a79498d7fd6 100644 --- a/packages/react-ui/internal/themes/AbstractTheme.ts +++ b/packages/react-ui/internal/themes/BasicTheme.ts @@ -1,7 +1,7 @@ import { createThemeFromClass } from '../../lib/theming/ThemeHelpers'; import * as ColorFunctions from '../../lib/styles/ColorFunctions'; -export class AbstractThemeClass { +export class BasicThemeClass { //#region Common variables public static labGrotesqueBaselineCompensation = '1'; public static brand = '#2291ff'; @@ -2484,6 +2484,6 @@ export class AbstractThemeClass { //#endregion } -export const AbstractTheme = createThemeFromClass(AbstractThemeClass); +export const BasicTheme = createThemeFromClass(BasicThemeClass); -export const BasicThemeToExtend = class {} as typeof AbstractThemeClass; +export const BasicThemeClassForExtension = class {} as typeof BasicThemeClass; diff --git a/packages/react-ui/internal/themes/DarkTheme5_0.ts b/packages/react-ui/internal/themes/DarkTheme5_0.ts index c6f66984c61..c671f73c8b6 100644 --- a/packages/react-ui/internal/themes/DarkTheme5_0.ts +++ b/packages/react-ui/internal/themes/DarkTheme5_0.ts @@ -1,9 +1,9 @@ import { markAsDarkTheme, createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; -import { AbstractTheme, BasicThemeToExtend } from './AbstractTheme'; +import { BasicTheme, BasicThemeClassForExtension } from './BasicTheme'; export const DarkTheme5_0 = createThemeFromClass( - class DarkTheme5_0 extends BasicThemeToExtend { + class DarkTheme5_0 extends BasicThemeClassForExtension { //#region Common variables public static grayXLight = '#313131'; public static gray = 'rgba(255, 255, 255, 0.48)'; @@ -552,7 +552,7 @@ export const DarkTheme5_0 = createThemeFromClass( //#endregion }, { - prototypeTheme: AbstractTheme, + prototypeTheme: BasicTheme, themeMarkers: [markAsDarkTheme, markThemeVersion(5, 0)], }, ); diff --git a/packages/react-ui/internal/themes/DarkTheme5_1.ts b/packages/react-ui/internal/themes/DarkTheme5_1.ts index d266656cff2..24be02a07de 100644 --- a/packages/react-ui/internal/themes/DarkTheme5_1.ts +++ b/packages/react-ui/internal/themes/DarkTheme5_1.ts @@ -1,9 +1,9 @@ import { createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; -import { BasicThemeToExtend } from './AbstractTheme'; +import { BasicThemeClassForExtension } from './BasicTheme'; import { DarkTheme5_0 } from './DarkTheme5_0'; -export const DarkTheme5_1 = createThemeFromClass(class DarkTheme5_1 extends BasicThemeToExtend {}, { +export const DarkTheme5_1 = createThemeFromClass(class DarkTheme5_1 extends BasicThemeClassForExtension {}, { prototypeTheme: DarkTheme5_0, themeMarkers: [markThemeVersion(5, 1)], }); diff --git a/packages/react-ui/internal/themes/LightTheme5_0.ts b/packages/react-ui/internal/themes/LightTheme5_0.ts index fcf8377833f..9b9505b2b83 100644 --- a/packages/react-ui/internal/themes/LightTheme5_0.ts +++ b/packages/react-ui/internal/themes/LightTheme5_0.ts @@ -1,8 +1,8 @@ import { createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; -import { AbstractTheme, BasicThemeToExtend } from './AbstractTheme'; +import { BasicTheme, BasicThemeClassForExtension } from './BasicTheme'; -export const LightTheme5_0 = createThemeFromClass(class LightTheme5_0 extends BasicThemeToExtend {}, { - prototypeTheme: AbstractTheme, +export const LightTheme5_0 = createThemeFromClass(class LightTheme5_0 extends BasicThemeClassForExtension {}, { + prototypeTheme: BasicTheme, themeMarkers: [markThemeVersion(5, 0)], }); diff --git a/packages/react-ui/internal/themes/LightTheme5_1.ts b/packages/react-ui/internal/themes/LightTheme5_1.ts index 5481fab9751..c64a2355803 100644 --- a/packages/react-ui/internal/themes/LightTheme5_1.ts +++ b/packages/react-ui/internal/themes/LightTheme5_1.ts @@ -1,9 +1,9 @@ import { createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; -import { BasicThemeToExtend } from './AbstractTheme'; +import { BasicThemeClassForExtension } from './BasicTheme'; import { LightTheme5_0 } from './LightTheme5_0'; -export const LightTheme5_1 = createThemeFromClass(class LightTheme5_1 extends BasicThemeToExtend {}, { +export const LightTheme5_1 = createThemeFromClass(class LightTheme5_1 extends BasicThemeClassForExtension {}, { prototypeTheme: LightTheme5_0, themeMarkers: [markThemeVersion(5, 1)], }); diff --git a/packages/react-ui/lib/theming/Theme.ts b/packages/react-ui/lib/theming/Theme.ts index e33522b18e2..12179160446 100644 --- a/packages/react-ui/lib/theming/Theme.ts +++ b/packages/react-ui/lib/theming/Theme.ts @@ -1,6 +1,6 @@ -import { AbstractThemeClass, BasicThemeToExtend } from '../../internal/themes/AbstractTheme'; +import { BasicThemeClass, BasicThemeClassForExtension } from '../../internal/themes/BasicTheme'; -export type Theme = Readonly; -export type ThemeIn = Partial; +export type Theme = Readonly; +export type ThemeIn = Partial; -export { BasicThemeToExtend }; +export { BasicThemeClassForExtension }; diff --git a/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx index 39758d9c613..34d00e411a3 100644 --- a/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx +++ b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx @@ -11,19 +11,19 @@ import { } from '../ThemeHelpers'; import { ThemeFactory } from '../ThemeFactory'; import { AnyObject } from '../../utils'; -import { AbstractTheme, BasicThemeToExtend } from '../../../internal/themes/AbstractTheme'; +import { BasicTheme, BasicThemeClassForExtension } from '../../../internal/themes/BasicTheme'; const TestTheme = createThemeFromClass( - class extends BasicThemeToExtend { + class extends BasicThemeClassForExtension { public static bgDefault = 'default'; public static bgSecondary = 'default'; }, - { prototypeTheme: AbstractTheme }, + { prototypeTheme: BasicTheme }, ); describe('ThemeHelpers', () => { describe('exposeGetters', () => { - const theme = class extends BasicThemeToExtend { + const theme = class extends BasicThemeClassForExtension { public static get errorText() { return 'red'; } @@ -41,16 +41,16 @@ describe('ThemeHelpers', () => { describe('createThemeFromClass', () => { const theme = createThemeFromClass( - class extends BasicThemeToExtend { + class extends BasicThemeClassForExtension { public static get errorText() { return this.black + this.blue; } }, - { prototypeTheme: AbstractTheme, themeMarkers: [markAsDarkTheme, markThemeVersion(1, 0)] }, + { prototypeTheme: BasicTheme, themeMarkers: [markAsDarkTheme, markThemeVersion(1, 0)] }, ); test('should inherit prototype theme', () => { - expect(theme.errorText).toBe(AbstractTheme.black + AbstractTheme.blue); + expect(theme.errorText).toBe(BasicTheme.black + BasicTheme.blue); }); test('should expose getters', () => { diff --git a/packages/react-ui/lib/theming/__tests__/Theming-test.tsx b/packages/react-ui/lib/theming/__tests__/Theming-test.tsx index 8e3dc18a248..2fe385d989a 100644 --- a/packages/react-ui/lib/theming/__tests__/Theming-test.tsx +++ b/packages/react-ui/lib/theming/__tests__/Theming-test.tsx @@ -6,7 +6,7 @@ import { applyMarkers, createThemeFromClass, Marker, REACT_UI_THEME_MARKERS } fr import { ThemeFactory } from '../ThemeFactory'; import { Theme } from '../Theme'; import { AnyObject } from '../../utils'; -import { AbstractTheme, BasicThemeToExtend } from '../../../internal/themes/AbstractTheme'; +import { BasicTheme, BasicThemeClassForExtension } from '../../../internal/themes/BasicTheme'; const TEST_MARKERS = { test: { @@ -35,11 +35,11 @@ const getConsumedTheme = () => { // test theme const myTheme = { brand: 'custom', bgDefault: 'custom' } as const; const TestTheme = createThemeFromClass( - class extends BasicThemeToExtend { + class extends BasicThemeClassForExtension { public static bgDefault = 'default'; public static bgSecondary = 'default'; }, - { prototypeTheme: AbstractTheme }, + { prototypeTheme: BasicTheme }, ); // test marker @@ -66,7 +66,7 @@ describe('Theming', () => { const theme = ThemeFactory.create(myTheme); expect(theme.brand).toEqual(myTheme.brand); - expect(theme.black).toEqual(AbstractTheme.black); + expect(theme.black).toEqual(BasicTheme.black); }); test('with args [theme, baseTheme]', () => { const theme = ThemeFactory.create(myTheme, TestTheme); @@ -96,7 +96,7 @@ describe('Theming', () => { }); test('getKeys()', () => { const keys_1 = ThemeFactory.getKeys(TestTheme); - const keys_2 = ThemeFactory.getKeys(AbstractTheme); + const keys_2 = ThemeFactory.getKeys(BasicTheme); expect(keys_1).toEqual(keys_2); }); From 5d69eb2dde9efed6212d5d6b43bd8b359339c44a Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Mon, 17 Feb 2025 14:51:31 +0500 Subject: [PATCH 19/27] chore: wip --- packages/react-ui/lib/theming/ThemeFactory.ts | 7 +++++++ packages/react-ui/lib/theming/ThemeHelpers.ts | 6 +----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/react-ui/lib/theming/ThemeFactory.ts b/packages/react-ui/lib/theming/ThemeFactory.ts index 8d637a135c0..d2237f01dbc 100644 --- a/packages/react-ui/lib/theming/ThemeFactory.ts +++ b/packages/react-ui/lib/theming/ThemeFactory.ts @@ -5,6 +5,13 @@ import { findPropertyDescriptor, REACT_UI_THEME_MARKERS } from './ThemeHelpers'; import { LIGHT_THEME } from './themes/LightTheme'; export class ThemeFactory { + /** + * "defaultTheme" created with Object.create() is needed for the "overrideBaseTheme" to work. + * + * Themes should be frozen objects. But it is not possible to define properties on frozen objects. + * So, an object created by Object.create({ *frozen_object* }) acts as frozen too, + * but allows to apply Object.defineProperty to his own properties. + */ public static defaultTheme: Theme = Object.create(LIGHT_THEME); public static getDefaultTheme() { diff --git a/packages/react-ui/lib/theming/ThemeHelpers.ts b/packages/react-ui/lib/theming/ThemeHelpers.ts index 8e7faccf4c4..e85e6cf1616 100644 --- a/packages/react-ui/lib/theming/ThemeHelpers.ts +++ b/packages/react-ui/lib/theming/ThemeHelpers.ts @@ -22,13 +22,9 @@ export const REACT_UI_THEME_MARKERS = { key: '__IS_REACT_UI_DARK_THEME__', value: true, }, - theme2022: { - key: '__IS_REACT_UI_THEME_2022__', - value: true, - }, themeVersion: { key: '__REACT_UI_THEME_VERSION__', - value: 0, + value: { major: 0, minor: 0 }, }, }; From ec0ccbe55bd6538aba92b738b0a8bd2049b46022 Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Tue, 18 Feb 2025 17:07:27 +0500 Subject: [PATCH 20/27] chore: fix review --- .../Test/{Base Theme => Basic Theme}/chrome2022.png | 0 .../__stories__/ThemeVersioning.stories.tsx | 8 ++++---- packages/react-ui/lib/theming/ThemeContext.ts | 2 +- packages/react-ui/lib/theming/ThemeFactory.ts | 6 ++---- .../react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx | 4 ---- 5 files changed, 7 insertions(+), 13 deletions(-) rename packages/react-ui/.creevey/images/ThemeVersions/Test/{Base Theme => Basic Theme}/chrome2022.png (100%) diff --git a/packages/react-ui/.creevey/images/ThemeVersions/Test/Base Theme/chrome2022.png b/packages/react-ui/.creevey/images/ThemeVersions/Test/Basic Theme/chrome2022.png similarity index 100% rename from packages/react-ui/.creevey/images/ThemeVersions/Test/Base Theme/chrome2022.png rename to packages/react-ui/.creevey/images/ThemeVersions/Test/Basic Theme/chrome2022.png diff --git a/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx index c227f8070e6..45de567a73d 100644 --- a/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx +++ b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx @@ -23,14 +23,14 @@ class TestThemeClass { type TestThemeIn = Partial; -const TEST_THEME_BASE = createThemeFromClass(TestThemeClass); +const TEST_THEME_BASIC = createThemeFromClass(TestThemeClass); const TEST_THEME_1_0 = createThemeFromClass( class extends (class {} as typeof TestThemeClass) { public static color = 'red'; public static textTransform = 'lowercase'; }, - { themeMarkers: [markThemeVersion(1, 0)], prototypeTheme: TEST_THEME_BASE }, + { themeMarkers: [markThemeVersion(1, 0)], prototypeTheme: TEST_THEME_BASIC }, ); const TEST_THEME_1_1 = createThemeFromClass( @@ -52,7 +52,7 @@ const Component = ({ theme }: { theme: TestThemeIn }) => { '1_0': isThemeVersionGTE(theme, 1, 0), '1_1': isThemeVersionGTE(theme, 1, 1), }) - .filter(([_, isDetected]) => isDetected === true) + .filter(([_, isDetected]) => isDetected) .map(([version]) =>
  • {version}
  • ); return ( @@ -72,7 +72,7 @@ const Component = ({ theme }: { theme: TestThemeIn }) => { ); }; -export const BaseTheme: Story = () => ; +export const BasicTheme: Story = () => ; export const Theme1_0: Story = () => ; Theme1_0.storyName = 'Theme 1_0'; diff --git a/packages/react-ui/lib/theming/ThemeContext.ts b/packages/react-ui/lib/theming/ThemeContext.ts index 015a32a7f65..cf31bd9f498 100644 --- a/packages/react-ui/lib/theming/ThemeContext.ts +++ b/packages/react-ui/lib/theming/ThemeContext.ts @@ -2,7 +2,7 @@ import React from 'react'; import { ThemeFactory } from './ThemeFactory'; -export const ThemeContext = React.createContext(ThemeFactory.getDefaultTheme()); +export const ThemeContext = React.createContext(ThemeFactory.defaultTheme); ThemeContext.displayName = 'ThemeContext'; ThemeContext.__KONTUR_REACT_UI__ = 'ThemeContext'; diff --git a/packages/react-ui/lib/theming/ThemeFactory.ts b/packages/react-ui/lib/theming/ThemeFactory.ts index d2237f01dbc..f0ffc49462d 100644 --- a/packages/react-ui/lib/theming/ThemeFactory.ts +++ b/packages/react-ui/lib/theming/ThemeFactory.ts @@ -12,10 +12,8 @@ export class ThemeFactory { * So, an object created by Object.create({ *frozen_object* }) acts as frozen too, * but allows to apply Object.defineProperty to his own properties. */ - public static defaultTheme: Theme = Object.create(LIGHT_THEME); - - public static getDefaultTheme() { - return this.defaultTheme; + public static get defaultTheme(): Theme { + return Object.create(LIGHT_THEME); } public static create(theme: ThemeIn & NoInfer, baseTheme?: Theme): Readonly> { diff --git a/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx index 34d00e411a3..53b312b9957 100644 --- a/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx +++ b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx @@ -146,10 +146,6 @@ describe('ThemeHelpers', () => { expect(isThemeVersionGTE(theme5_5, 5, 6)).toBe(false); }); - test('5_5 should NOT BE greater or equal that 5_10', () => { - expect(isThemeVersionGTE(theme5_5, 5, 10)).toBe(false); - }); - test('5_5 should NOT BE greater or equal that 6_0', () => { expect(isThemeVersionGTE(theme5_5, 6, 0)).toBe(false); }); From 5c007547fe5eba29c24386aafec1bf5acf44b6e9 Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Tue, 18 Feb 2025 17:55:23 +0500 Subject: [PATCH 21/27] chore: fix default theme getter --- packages/react-ui/lib/theming/ThemeFactory.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/react-ui/lib/theming/ThemeFactory.ts b/packages/react-ui/lib/theming/ThemeFactory.ts index f0ffc49462d..dfe63469ab9 100644 --- a/packages/react-ui/lib/theming/ThemeFactory.ts +++ b/packages/react-ui/lib/theming/ThemeFactory.ts @@ -6,14 +6,16 @@ import { LIGHT_THEME } from './themes/LightTheme'; export class ThemeFactory { /** - * "defaultTheme" created with Object.create() is needed for the "overrideBaseTheme" to work. + * The default theme created with Object.create() is needed for the "overrideBaseTheme" to work. * * Themes should be frozen objects. But it is not possible to define properties on frozen objects. * So, an object created by Object.create({ *frozen_object* }) acts as frozen too, * but allows to apply Object.defineProperty to his own properties. */ + private static readonly overridableDefaultTheme: Theme = Object.create(LIGHT_THEME); + public static get defaultTheme(): Theme { - return Object.create(LIGHT_THEME); + return this.overridableDefaultTheme; } public static create(theme: ThemeIn & NoInfer, baseTheme?: Theme): Readonly> { From 9d4583c632d494a5affe4afea8c724fa32c4b147 Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Tue, 25 Feb 2025 13:02:07 +0500 Subject: [PATCH 22/27] test(themes): test public themes requirements --- .../theming/themes/__tests__/Themes-test.tsx | 211 ++++++++++++++---- 1 file changed, 173 insertions(+), 38 deletions(-) diff --git a/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx b/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx index 8301e759bed..4da29f4a1c1 100644 --- a/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx +++ b/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx @@ -1,57 +1,192 @@ import { isDarkTheme, isThemeVersionGTE } from '../../ThemeHelpers'; -import { DARK_THEME, DARK_THEME_5_0, DARK_THEME_5_1 } from '../DarkTheme'; -import { LIGHT_THEME, LIGHT_THEME_5_0, LIGHT_THEME_5_1 } from '../LightTheme'; +import { DARK_THEME } from '../DarkTheme'; +import { LIGHT_THEME } from '../LightTheme'; +import * as DarkThemeImports from '../DarkTheme'; +import * as LightThemeImports from '../LightTheme'; +import { Theme } from '../../Theme'; -describe('Themes', () => { - describe('LIGHT_THEME', () => { - test('should be equal LIGHT_THEME_5_0', () => { - expect(LIGHT_THEME).toBe(LIGHT_THEME_5_0); +interface ThemeVersion { + major: number; + minor: number; +} + +interface ThemeWithNameAndVersion { + theme: Theme; + name: string; + version: ThemeVersion | null; +} + +describe('themes', () => { + const ALL_LIGHT_THEMES = getThemesFromImports(LightThemeImports); + const ALL_DARK_THEMES = getThemesFromImports(DarkThemeImports); + const ALL_THEMES = [...ALL_LIGHT_THEMES, ...ALL_DARK_THEMES]; + const THEMES_WITH_VERSIONS_IN_NAMES = ALL_THEMES.filter( + ({ name }) => name !== 'LIGHT_THEME' && name !== 'DARK_THEME', + ); + + describe('versions', () => { + test.each(ALL_THEMES)('$name has a version', ({ theme }) => { + expect(isThemeVersionGTE(theme, 0, 0)).toBe(true); }); - }); - describe('LIGHT_THEME_5_0', () => { - test('should be 5_0 version', () => { - expect(isThemeVersionGTE(LIGHT_THEME_5_0, 5, 0)).toBe(true); - expect(isThemeVersionGTE(LIGHT_THEME_5_0, 5, 1)).toBe(false); + test.each(THEMES_WITH_VERSIONS_IN_NAMES)('name of $name matches its version: $version', ({ theme, version }) => { + expect(version).toBeTruthy(); + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const { major, minor } = version!; + + expect(isThemeVersionGTE(theme, major, minor)).toBe(true); + expect(isThemeVersionGTE(theme, major, minor + 1)).toBe(false); }); - test('should not be dark', () => { - expect(isDarkTheme(LIGHT_THEME_5_0)).toBe(false); + + describe('latests', () => { + const LATEST_LIGHT_THEME = getLatestTheme(ALL_LIGHT_THEMES); + const LATEST_DARK_THEME = getLatestTheme(ALL_DARK_THEMES); + + test.each` + defaultTheme | defaultThemeName | latestTheme | latestThemeName + ${LIGHT_THEME} | ${'LIGHT_THEME'} | ${LATEST_LIGHT_THEME.theme} | ${LATEST_LIGHT_THEME.name} + ${DARK_THEME} | ${'DARK_THEME'} | ${LATEST_DARK_THEME.theme} | ${LATEST_DARK_THEME.name} + `('$defaultThemeName should be equal to the latest theme: $latestThemeName', ({ defaultTheme, latestTheme }) => { + expect(latestTheme).toBeDefined(); + expect(defaultTheme).toBe(latestTheme); + }); }); }); - describe('LIGHT_THEME_5_1', () => { - test('should be 5_1 version', () => { - expect(isThemeVersionGTE(LIGHT_THEME_5_1, 5, 0)).toBe(true); - expect(isThemeVersionGTE(LIGHT_THEME_5_1, 5, 1)).toBe(true); + describe('lightness and darkness', () => { + test.each(ALL_LIGHT_THEMES)('$name should be light', ({ theme, name }) => { + expect(isDarkTheme(theme)).toBe(false); + expect(name).toMatch(/LIGHT_THEME/); + expect(name).not.toMatch(/DARK_THEME/); }); - test('should not be dark', () => { - expect(isDarkTheme(LIGHT_THEME_5_1)).toBe(false); - }); - }); - describe('DARK_THEME', () => { - test('should be equal DARK_THEME_5_0', () => { - expect(DARK_THEME).toBe(DARK_THEME_5_0); + test.each(ALL_DARK_THEMES)('$name should be dark', ({ theme, name }) => { + expect(isDarkTheme(theme)).toBe(true); + expect(name).not.toMatch(/LIGHT_THEME/); + expect(name).toMatch(/DARK_THEME/); }); }); - describe('DARK_THEME_5_0', () => { - test('should be 5_0 version', () => { - expect(isThemeVersionGTE(DARK_THEME_5_0, 5, 0)).toBe(true); - expect(isThemeVersionGTE(DARK_THEME_5_0, 5, 1)).toBe(false); + describe('test utils', () => { + describe('getVersionFromThemeName', () => { + test.each` + name | version + ${'LIGHT_THEME'} | ${null} + ${'LIGHT_THEME_1'} | ${null} + ${'LIGHT_THEME_1_0'} | ${{ major: 1, minor: 0 }} + ${'LIGHT_THEME_1_0_0'} | ${{ major: 0, minor: 0 }} + ${'LIGHT_THEME_10_10'} | ${{ major: 10, minor: 10 }} + ${'LIGHT_THEME_00_00'} | ${{ major: 0, minor: 0 }} + `('$name should result to $version', ({ name, version }) => { + expect(getVersionFromThemeName(name)).toStrictEqual(version); + }); }); - test('should be dark', () => { - expect(isDarkTheme(DARK_THEME_5_0)).toBe(true); - }); - }); - describe('DARK_THEME_5_1', () => { - test('should be 5_1 version', () => { - expect(isThemeVersionGTE(DARK_THEME_5_1, 5, 0)).toBe(true); - expect(isThemeVersionGTE(DARK_THEME_5_1, 5, 1)).toBe(true); + describe('getThemesFromImports', () => { + const mockTheme = {} as Theme; + const mockThemesImports = { + LIGHT_THEME: mockTheme, + LIGHT_THEME_1_0: mockTheme, + LIGHT_THEME_1_1: mockTheme, + DARK_THEME: mockTheme, + DARK_THEME_1_0: mockTheme, + DARK_THEME_1_1: mockTheme, + }; + + test('should remove __esModule flag', () => { + const THEMES = getThemesFromImports({ + ...mockThemesImports, + //@ts-expect-error + __esModule: true, + }); + + expect(THEMES.length).toBe(Object.entries(mockThemesImports).length); + expect(THEMES.find(({ name }) => name === 'LIGHT_THEME')).toBeDefined(); + expect(THEMES.find(({ name }) => name === '__esModule')).not.toBeDefined(); + }); + + test.each(Object.keys(mockThemesImports))('%s should present', (themeName) => { + const THEMES = getThemesFromImports(mockThemesImports); + expect(THEMES.find(({ name }) => name === themeName)).toBeDefined(); + }); + + test.each(getThemesFromImports(mockThemesImports))('$name version should be $version', ({ name, version }) => { + expect(version).toStrictEqual(getVersionFromThemeName(name)); + }); }); - test('should be dark', () => { - expect(isDarkTheme(DARK_THEME_5_1)).toBe(true); + + describe('getLatestTheme', () => { + const mockTheme = {} as Theme; + const mockLightThemesImports = { + LIGHT_THEME_0_0: mockTheme, + LIGHT_THEME_20_0: mockTheme, + LIGHT_THEME: mockTheme, + LIGHT_THEME_1_0: mockTheme, + LIGHT_THEME_1_1: mockTheme, + LIGHT_THEME_0_9: mockTheme, + }; + const mockDarkThemesImports = { + DARK_THEME_0_9: mockTheme, + DARK_THEME_2_1: mockTheme, + DARK_THEME_0_0: mockTheme, + DARK_THEME_10_0: mockTheme, + DARK_THEME_1_0: mockTheme, + DARK_THEME: mockTheme, + }; + + const LIGHT_THEMES = getThemesFromImports(mockLightThemesImports); + const DARK_THEMES = getThemesFromImports(mockDarkThemesImports); + + test.each` + themes | latestThemeName + ${LIGHT_THEMES} | ${'LIGHT_THEME_20_0'} + ${DARK_THEMES} | ${'DARK_THEME_10_0'} + `('the latest theme is $latestThemeName', ({ themes, latestThemeName }) => { + const { name } = getLatestTheme(themes); + expect(name).toBe(latestThemeName); + }); }); }); }); + +function getVersionFromThemeName(name: string): ThemeVersion | null { + const versionString = name.match(/\d+_\d+$/g); + + if (!versionString) { + return null; + } + + const [major, minor] = versionString[0].split('_').map(Number); + + return { major, minor }; +} + +function getThemesFromImports(themes: Record): ThemeWithNameAndVersion[] { + return Object.entries(themes) + .filter(([key]) => key !== '__esModule') + .map(([name, theme]) => ({ theme, name, version: getVersionFromThemeName(name) })); +} + +function getLatestTheme(themes: ThemeWithNameAndVersion[]): ThemeWithNameAndVersion { + let latestTheme = themes[0]; + + for (const theme of themes) { + const { version: themeVersion } = theme; + const { version: latestThemeVersion } = latestTheme; + + if (!themeVersion) { + continue; + } + + if ( + !latestThemeVersion || + latestThemeVersion.major < themeVersion.major || + (latestThemeVersion.major === themeVersion.major && latestThemeVersion.minor < themeVersion.minor) + ) { + latestTheme = theme; + } + } + + return latestTheme; +} From 229e95e61f444e4471efb4f046fcfae05d56ff49 Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Tue, 25 Feb 2025 13:03:03 +0500 Subject: [PATCH 23/27] chore: actualize default themes versions --- packages/react-ui/lib/theming/themes/DarkTheme.ts | 2 +- packages/react-ui/lib/theming/themes/LightTheme.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-ui/lib/theming/themes/DarkTheme.ts b/packages/react-ui/lib/theming/themes/DarkTheme.ts index e1a71ba8c24..efae91c68a7 100644 --- a/packages/react-ui/lib/theming/themes/DarkTheme.ts +++ b/packages/react-ui/lib/theming/themes/DarkTheme.ts @@ -4,4 +4,4 @@ import { DarkTheme5_1 } from '../../../internal/themes/DarkTheme5_1'; export const DARK_THEME_5_0 = DarkTheme5_0; export const DARK_THEME_5_1 = DarkTheme5_1; -export const DARK_THEME = DARK_THEME_5_0; +export const DARK_THEME = DARK_THEME_5_1; diff --git a/packages/react-ui/lib/theming/themes/LightTheme.ts b/packages/react-ui/lib/theming/themes/LightTheme.ts index 445c1b9cb8f..5afe7e0429d 100644 --- a/packages/react-ui/lib/theming/themes/LightTheme.ts +++ b/packages/react-ui/lib/theming/themes/LightTheme.ts @@ -4,4 +4,4 @@ import { LightTheme5_1 } from '../../../internal/themes/LightTheme5_1'; export const LIGHT_THEME_5_0 = LightTheme5_0; export const LIGHT_THEME_5_1 = LightTheme5_1; -export const LIGHT_THEME = LIGHT_THEME_5_0; +export const LIGHT_THEME = LIGHT_THEME_5_1; From 61da80a6eb408c2389b0f3b0774e4177c9485c41 Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Tue, 25 Feb 2025 17:28:54 +0500 Subject: [PATCH 24/27] refactor: turn createThemeFromClass to createTheme --- .../__stories__/ThemeVersioning.stories.tsx | 25 +++++++++++-------- .../react-ui/internal/themes/BasicTheme.ts | 4 +-- .../react-ui/internal/themes/DarkTheme5_0.ts | 14 +++++------ .../react-ui/internal/themes/DarkTheme5_1.ts | 5 ++-- .../react-ui/internal/themes/LightTheme5_0.ts | 5 ++-- .../react-ui/internal/themes/LightTheme5_1.ts | 5 ++-- packages/react-ui/lib/theming/ThemeHelpers.ts | 18 ++++++------- .../theming/__tests__/ThemeHelpers-test.tsx | 21 ++++++++-------- .../lib/theming/__tests__/Theming-test.tsx | 10 ++++---- 9 files changed, 55 insertions(+), 52 deletions(-) diff --git a/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx index 45de567a73d..dd2f69914d6 100644 --- a/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx +++ b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx @@ -2,7 +2,8 @@ import React, { CSSProperties } from 'react'; import { Story, Meta } from '../../../typings/stories'; -import { createThemeFromClass, isThemeVersionGTE, markThemeVersion } from '../../../lib/theming/ThemeHelpers'; +import { createTheme, isThemeVersionGTE, markThemeVersion } from '../../../lib/theming/ThemeHelpers'; +import { BasicThemeClassForExtension } from '../../../internal/themes/BasicTheme'; export default { title: 'ThemeVersions/Test', @@ -15,7 +16,7 @@ export default { }, } as Meta; -class TestThemeClass { +class TestThemeClass extends BasicThemeClassForExtension { public static color = 'initial'; public static textTransform = 'none'; public static fontStyle = 'normal'; @@ -23,23 +24,25 @@ class TestThemeClass { type TestThemeIn = Partial; -const TEST_THEME_BASIC = createThemeFromClass(TestThemeClass); +const TEST_THEME_BASIC = createTheme({ themeClass: TestThemeClass }); -const TEST_THEME_1_0 = createThemeFromClass( - class extends (class {} as typeof TestThemeClass) { +const TEST_THEME_1_0 = createTheme({ + themeClass: class extends (class {} as typeof TestThemeClass) { public static color = 'red'; public static textTransform = 'lowercase'; }, - { themeMarkers: [markThemeVersion(1, 0)], prototypeTheme: TEST_THEME_BASIC }, -); + themeMarkers: [markThemeVersion(1, 0)], + prototypeTheme: TEST_THEME_BASIC, +}); -const TEST_THEME_1_1 = createThemeFromClass( - class extends (class {} as typeof TestThemeClass) { +const TEST_THEME_1_1 = createTheme({ + themeClass: class extends (class {} as typeof TestThemeClass) { public static color = 'green'; public static fontStyle = 'italic'; }, - { themeMarkers: [markThemeVersion(1, 1)], prototypeTheme: TEST_THEME_1_0 }, -); + themeMarkers: [markThemeVersion(1, 1)], + prototypeTheme: TEST_THEME_1_0, +}); const Component = ({ theme }: { theme: TestThemeIn }) => { const styles = { diff --git a/packages/react-ui/internal/themes/BasicTheme.ts b/packages/react-ui/internal/themes/BasicTheme.ts index a79498d7fd6..6babc6238cf 100644 --- a/packages/react-ui/internal/themes/BasicTheme.ts +++ b/packages/react-ui/internal/themes/BasicTheme.ts @@ -1,4 +1,4 @@ -import { createThemeFromClass } from '../../lib/theming/ThemeHelpers'; +import { createTheme } from '../../lib/theming/ThemeHelpers'; import * as ColorFunctions from '../../lib/styles/ColorFunctions'; export class BasicThemeClass { @@ -2484,6 +2484,6 @@ export class BasicThemeClass { //#endregion } -export const BasicTheme = createThemeFromClass(BasicThemeClass); +export const BasicTheme = createTheme({ themeClass: BasicThemeClass }); export const BasicThemeClassForExtension = class {} as typeof BasicThemeClass; diff --git a/packages/react-ui/internal/themes/DarkTheme5_0.ts b/packages/react-ui/internal/themes/DarkTheme5_0.ts index c671f73c8b6..fc83fb2c6d5 100644 --- a/packages/react-ui/internal/themes/DarkTheme5_0.ts +++ b/packages/react-ui/internal/themes/DarkTheme5_0.ts @@ -1,9 +1,9 @@ -import { markAsDarkTheme, createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; +import { markAsDarkTheme, createTheme, markThemeVersion } from '../../lib/theming/ThemeHelpers'; import { BasicTheme, BasicThemeClassForExtension } from './BasicTheme'; -export const DarkTheme5_0 = createThemeFromClass( - class DarkTheme5_0 extends BasicThemeClassForExtension { +export const DarkTheme5_0 = createTheme({ + themeClass: class DarkTheme5_0 extends BasicThemeClassForExtension { //#region Common variables public static grayXLight = '#313131'; public static gray = 'rgba(255, 255, 255, 0.48)'; @@ -551,8 +551,6 @@ export const DarkTheme5_0 = createThemeFromClass( public static validationsTextColorWarning = '#fdd481'; //#endregion }, - { - prototypeTheme: BasicTheme, - themeMarkers: [markAsDarkTheme, markThemeVersion(5, 0)], - }, -); + prototypeTheme: BasicTheme, + themeMarkers: [markAsDarkTheme, markThemeVersion(5, 0)], +}); diff --git a/packages/react-ui/internal/themes/DarkTheme5_1.ts b/packages/react-ui/internal/themes/DarkTheme5_1.ts index 24be02a07de..e456544c330 100644 --- a/packages/react-ui/internal/themes/DarkTheme5_1.ts +++ b/packages/react-ui/internal/themes/DarkTheme5_1.ts @@ -1,9 +1,10 @@ -import { createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; +import { createTheme, markThemeVersion } from '../../lib/theming/ThemeHelpers'; import { BasicThemeClassForExtension } from './BasicTheme'; import { DarkTheme5_0 } from './DarkTheme5_0'; -export const DarkTheme5_1 = createThemeFromClass(class DarkTheme5_1 extends BasicThemeClassForExtension {}, { +export const DarkTheme5_1 = createTheme({ + themeClass: class DarkTheme5_1 extends BasicThemeClassForExtension {}, prototypeTheme: DarkTheme5_0, themeMarkers: [markThemeVersion(5, 1)], }); diff --git a/packages/react-ui/internal/themes/LightTheme5_0.ts b/packages/react-ui/internal/themes/LightTheme5_0.ts index 9b9505b2b83..dc6cc2d3da4 100644 --- a/packages/react-ui/internal/themes/LightTheme5_0.ts +++ b/packages/react-ui/internal/themes/LightTheme5_0.ts @@ -1,8 +1,9 @@ -import { createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; +import { createTheme, markThemeVersion } from '../../lib/theming/ThemeHelpers'; import { BasicTheme, BasicThemeClassForExtension } from './BasicTheme'; -export const LightTheme5_0 = createThemeFromClass(class LightTheme5_0 extends BasicThemeClassForExtension {}, { +export const LightTheme5_0 = createTheme({ + themeClass: class LightTheme5_0 extends BasicThemeClassForExtension {}, prototypeTheme: BasicTheme, themeMarkers: [markThemeVersion(5, 0)], }); diff --git a/packages/react-ui/internal/themes/LightTheme5_1.ts b/packages/react-ui/internal/themes/LightTheme5_1.ts index c64a2355803..7ea67eae035 100644 --- a/packages/react-ui/internal/themes/LightTheme5_1.ts +++ b/packages/react-ui/internal/themes/LightTheme5_1.ts @@ -1,9 +1,10 @@ -import { createThemeFromClass, markThemeVersion } from '../../lib/theming/ThemeHelpers'; +import { createTheme, markThemeVersion } from '../../lib/theming/ThemeHelpers'; import { BasicThemeClassForExtension } from './BasicTheme'; import { LightTheme5_0 } from './LightTheme5_0'; -export const LightTheme5_1 = createThemeFromClass(class LightTheme5_1 extends BasicThemeClassForExtension {}, { +export const LightTheme5_1 = createTheme({ + themeClass: class LightTheme5_1 extends BasicThemeClassForExtension {}, prototypeTheme: LightTheme5_0, themeMarkers: [markThemeVersion(5, 1)], }); diff --git a/packages/react-ui/lib/theming/ThemeHelpers.ts b/packages/react-ui/lib/theming/ThemeHelpers.ts index e85e6cf1616..31028d6aeca 100644 --- a/packages/react-ui/lib/theming/ThemeHelpers.ts +++ b/packages/react-ui/lib/theming/ThemeHelpers.ts @@ -93,20 +93,18 @@ export function applyMarkers(theme: T, markers: Markers): T { }, Object.create(theme)); } -export function createThemeFromClass( - themeObject: T, - options?: { - prototypeTheme?: P; - themeMarkers?: Markers; - }, -) { - const { prototypeTheme, themeMarkers = [] } = options || {}; +export function createTheme(options: { + themeClass: Theme; + prototypeTheme?: Theme; + themeMarkers?: Markers; +}): Readonly { + const { themeClass, prototypeTheme, themeMarkers = [] } = options; if (prototypeTheme) { - Object.setPrototypeOf(themeObject, prototypeTheme); + Object.setPrototypeOf(themeClass, prototypeTheme); } - const theme = applyMarkers(exposeGetters(themeObject), themeMarkers); + const theme = applyMarkers(exposeGetters(themeClass), themeMarkers); return Object.freeze(theme); } diff --git a/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx index 53b312b9957..b82be42b436 100644 --- a/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx +++ b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx @@ -1,6 +1,6 @@ import { applyMarkers, - createThemeFromClass, + createTheme, exposeGetters, isDarkTheme, isThemeVersionGTE, @@ -13,13 +13,13 @@ import { ThemeFactory } from '../ThemeFactory'; import { AnyObject } from '../../utils'; import { BasicTheme, BasicThemeClassForExtension } from '../../../internal/themes/BasicTheme'; -const TestTheme = createThemeFromClass( - class extends BasicThemeClassForExtension { +const TestTheme = createTheme({ + themeClass: class extends BasicThemeClassForExtension { public static bgDefault = 'default'; public static bgSecondary = 'default'; }, - { prototypeTheme: BasicTheme }, -); + prototypeTheme: BasicTheme, +}); describe('ThemeHelpers', () => { describe('exposeGetters', () => { @@ -39,15 +39,16 @@ describe('ThemeHelpers', () => { }); }); - describe('createThemeFromClass', () => { - const theme = createThemeFromClass( - class extends BasicThemeClassForExtension { + describe('createTheme', () => { + const theme = createTheme({ + themeClass: class extends BasicThemeClassForExtension { public static get errorText() { return this.black + this.blue; } }, - { prototypeTheme: BasicTheme, themeMarkers: [markAsDarkTheme, markThemeVersion(1, 0)] }, - ); + prototypeTheme: BasicTheme, + themeMarkers: [markAsDarkTheme, markThemeVersion(5, 0)], + }); test('should inherit prototype theme', () => { expect(theme.errorText).toBe(BasicTheme.black + BasicTheme.blue); diff --git a/packages/react-ui/lib/theming/__tests__/Theming-test.tsx b/packages/react-ui/lib/theming/__tests__/Theming-test.tsx index 2fe385d989a..836fa2d51d1 100644 --- a/packages/react-ui/lib/theming/__tests__/Theming-test.tsx +++ b/packages/react-ui/lib/theming/__tests__/Theming-test.tsx @@ -2,7 +2,7 @@ import { render } from '@testing-library/react'; import React from 'react'; import { ThemeContext } from '../ThemeContext'; -import { applyMarkers, createThemeFromClass, Marker, REACT_UI_THEME_MARKERS } from '../ThemeHelpers'; +import { applyMarkers, createTheme, Marker, REACT_UI_THEME_MARKERS } from '../ThemeHelpers'; import { ThemeFactory } from '../ThemeFactory'; import { Theme } from '../Theme'; import { AnyObject } from '../../utils'; @@ -34,13 +34,13 @@ const getConsumedTheme = () => { // test theme const myTheme = { brand: 'custom', bgDefault: 'custom' } as const; -const TestTheme = createThemeFromClass( - class extends BasicThemeClassForExtension { +const TestTheme = createTheme({ + themeClass: class extends BasicThemeClassForExtension { public static bgDefault = 'default'; public static bgSecondary = 'default'; }, - { prototypeTheme: BasicTheme }, -); + prototypeTheme: BasicTheme, +}); // test marker const markAsTest: Marker = (theme) => { From 55dc851022212820e071512e1e530b3abd3f8c29 Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Thu, 27 Feb 2025 16:09:31 +0500 Subject: [PATCH 25/27] refactor: isThemeVersionGTE(theme, 5, 0) to isThemeGTE(theme, '5.0') --- .../__stories__/ThemeVersioning.stories.tsx | 15 ++-- .../react-ui/internal/themes/DarkTheme5_0.ts | 2 +- .../react-ui/internal/themes/DarkTheme5_1.ts | 2 +- .../react-ui/internal/themes/LightTheme5_0.ts | 2 +- .../react-ui/internal/themes/LightTheme5_1.ts | 2 +- packages/react-ui/lib/theming/ThemeHelpers.ts | 81 ++++++++++++------- .../theming/__tests__/ThemeHelpers-test.tsx | 70 ++++++++++++---- .../theming/themes/__tests__/Themes-test.tsx | 9 ++- 8 files changed, 124 insertions(+), 59 deletions(-) diff --git a/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx index dd2f69914d6..6e97d10358f 100644 --- a/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx +++ b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx @@ -2,7 +2,7 @@ import React, { CSSProperties } from 'react'; import { Story, Meta } from '../../../typings/stories'; -import { createTheme, isThemeVersionGTE, markThemeVersion } from '../../../lib/theming/ThemeHelpers'; +import { createTheme, isThemeVersion, markThemeVersion } from '../../../lib/theming/ThemeHelpers'; import { BasicThemeClassForExtension } from '../../../internal/themes/BasicTheme'; export default { @@ -23,6 +23,7 @@ class TestThemeClass extends BasicThemeClassForExtension { } type TestThemeIn = Partial; +type TestVersions = '1.0' | '1.1'; const TEST_THEME_BASIC = createTheme({ themeClass: TestThemeClass }); @@ -31,7 +32,7 @@ const TEST_THEME_1_0 = createTheme({ public static color = 'red'; public static textTransform = 'lowercase'; }, - themeMarkers: [markThemeVersion(1, 0)], + themeMarkers: [markThemeVersion('1.0')], prototypeTheme: TEST_THEME_BASIC, }); @@ -40,7 +41,7 @@ const TEST_THEME_1_1 = createTheme({ public static color = 'green'; public static fontStyle = 'italic'; }, - themeMarkers: [markThemeVersion(1, 1)], + themeMarkers: [markThemeVersion('1.1')], prototypeTheme: TEST_THEME_1_0, }); @@ -52,8 +53,8 @@ const Component = ({ theme }: { theme: TestThemeIn }) => { }; const themeVersionList = Object.entries({ - '1_0': isThemeVersionGTE(theme, 1, 0), - '1_1': isThemeVersionGTE(theme, 1, 1), + '1_0': isThemeVersion(theme, '1.0'), + '1_1': isThemeVersion(theme, '1.1'), }) .filter(([_, isDetected]) => isDetected) .map(([version]) =>
  • {version}
  • ); @@ -65,9 +66,9 @@ const Component = ({ theme }: { theme: TestThemeIn }) => {
    Detected theme versions: {themeVersionList.length === 0 && 'none'} - {isThemeVersionGTE(theme, 1, 1) ? ( + {isThemeVersion(theme, '1.1') ? (
      {themeVersionList}
    - ) : isThemeVersionGTE(theme, 1, 0) ? ( + ) : isThemeVersion(theme, '1.0') ? (
      {themeVersionList}
    ) : null}
    diff --git a/packages/react-ui/internal/themes/DarkTheme5_0.ts b/packages/react-ui/internal/themes/DarkTheme5_0.ts index fc83fb2c6d5..1cbbb82c941 100644 --- a/packages/react-ui/internal/themes/DarkTheme5_0.ts +++ b/packages/react-ui/internal/themes/DarkTheme5_0.ts @@ -552,5 +552,5 @@ export const DarkTheme5_0 = createTheme({ //#endregion }, prototypeTheme: BasicTheme, - themeMarkers: [markAsDarkTheme, markThemeVersion(5, 0)], + themeMarkers: [markAsDarkTheme, markThemeVersion('5.0')], }); diff --git a/packages/react-ui/internal/themes/DarkTheme5_1.ts b/packages/react-ui/internal/themes/DarkTheme5_1.ts index e456544c330..be6bda01aef 100644 --- a/packages/react-ui/internal/themes/DarkTheme5_1.ts +++ b/packages/react-ui/internal/themes/DarkTheme5_1.ts @@ -6,5 +6,5 @@ import { DarkTheme5_0 } from './DarkTheme5_0'; export const DarkTheme5_1 = createTheme({ themeClass: class DarkTheme5_1 extends BasicThemeClassForExtension {}, prototypeTheme: DarkTheme5_0, - themeMarkers: [markThemeVersion(5, 1)], + themeMarkers: [markThemeVersion('5.1')], }); diff --git a/packages/react-ui/internal/themes/LightTheme5_0.ts b/packages/react-ui/internal/themes/LightTheme5_0.ts index dc6cc2d3da4..3d9092e037c 100644 --- a/packages/react-ui/internal/themes/LightTheme5_0.ts +++ b/packages/react-ui/internal/themes/LightTheme5_0.ts @@ -5,5 +5,5 @@ import { BasicTheme, BasicThemeClassForExtension } from './BasicTheme'; export const LightTheme5_0 = createTheme({ themeClass: class LightTheme5_0 extends BasicThemeClassForExtension {}, prototypeTheme: BasicTheme, - themeMarkers: [markThemeVersion(5, 0)], + themeMarkers: [markThemeVersion('5.0')], }); diff --git a/packages/react-ui/internal/themes/LightTheme5_1.ts b/packages/react-ui/internal/themes/LightTheme5_1.ts index 7ea67eae035..2b03be8a70d 100644 --- a/packages/react-ui/internal/themes/LightTheme5_1.ts +++ b/packages/react-ui/internal/themes/LightTheme5_1.ts @@ -6,5 +6,5 @@ import { LightTheme5_0 } from './LightTheme5_0'; export const LightTheme5_1 = createTheme({ themeClass: class LightTheme5_1 extends BasicThemeClassForExtension {}, prototypeTheme: LightTheme5_0, - themeMarkers: [markThemeVersion(5, 1)], + themeMarkers: [markThemeVersion('5.1')], }); diff --git a/packages/react-ui/lib/theming/ThemeHelpers.ts b/packages/react-ui/lib/theming/ThemeHelpers.ts index 31028d6aeca..9706c0e4ec8 100644 --- a/packages/react-ui/lib/theming/ThemeHelpers.ts +++ b/packages/react-ui/lib/theming/ThemeHelpers.ts @@ -1,9 +1,13 @@ +import { Nullable } from '../../typings/utility-types'; import { isNonNullable } from '../utils'; import { Theme, ThemeIn } from './Theme'; export type Marker = (theme: Readonly) => Readonly; export type Markers = Marker[]; +export const ThemeVersionSeparator = '.' as const; +export type ThemeVersions = '5.0' | '5.1'; +export type ThemeVersionType = `${bigint}${typeof ThemeVersionSeparator}${bigint}`; export const exposeGetters = >(theme: T): T => { const descriptors = Object.getOwnPropertyDescriptors(theme); @@ -17,14 +21,23 @@ export const exposeGetters = >(theme: T): T => { return theme; }; -export const REACT_UI_THEME_MARKERS = { +export const REACT_UI_THEME_MARKERS: { + darkTheme: { + key: string; + value: true; + }; + themeVersion: { + key: string; + value: ThemeVersionType; + }; +} = { darkTheme: { key: '__IS_REACT_UI_DARK_THEME__', value: true, }, themeVersion: { key: '__REACT_UI_THEME_VERSION__', - value: { major: 0, minor: 0 }, + value: '0.0', }, }; @@ -47,32 +60,27 @@ export const markAsDarkTheme: Marker = (theme) => { }); }; -export const markThemeVersion: (major: number, minor: number) => Marker = (major: number, minor: number) => (theme) => { - return Object.create(theme, { - [REACT_UI_THEME_MARKERS.themeVersion.key]: { - value: { major, minor }, - writable: false, - enumerable: false, - configurable: false, - }, - }); -}; - -export const isThemeVersionGTE = (theme: Theme | ThemeIn, major: number, minor: number): boolean => { - // @ts-expect-error: internal value. - const themeVersion: { major: number; minor: number } | undefined = theme[REACT_UI_THEME_MARKERS.themeVersion.key]; - - if (!themeVersion) { - return false; - } - - if (themeVersion.major > major) { - return true; - } else if (themeVersion.major === major) { - return themeVersion.minor >= minor; - } - - return false; +export const markThemeVersion: (version: T) => Marker = + (version) => (theme) => { + return Object.create(theme, { + [REACT_UI_THEME_MARKERS.themeVersion.key]: { + value: version || REACT_UI_THEME_MARKERS.themeVersion.value, + writable: false, + enumerable: false, + configurable: false, + }, + }); + }; + +export const isThemeVersion = ( + theme: Theme | ThemeIn, + version: T, +): boolean => { + const themeVersion: T | undefined = + // @ts-expect-error: internal value. + theme[REACT_UI_THEME_MARKERS.themeVersion.key]; + + return isVersionGTE(themeVersion, version); }; export function findPropertyDescriptor(theme: Theme, propName: string) { @@ -108,3 +116,20 @@ export function createTheme(options: { return Object.freeze(theme); } + +export const isVersionGTE = (v1: Nullable, v2: Nullable): boolean => { + if (!v1 || !v2) { + return false; + } + + const [majorV1, minorV1] = v1.split(ThemeVersionSeparator).map(Number); + const [majorV2, minorV2] = v2.split(ThemeVersionSeparator).map(Number); + + if (majorV1 > majorV2) { + return true; + } else if (majorV1 === majorV2) { + return minorV1 >= minorV2; + } + + return false; +}; diff --git a/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx index b82be42b436..cf7a0533f36 100644 --- a/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx +++ b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx @@ -3,7 +3,8 @@ import { createTheme, exposeGetters, isDarkTheme, - isThemeVersionGTE, + isThemeVersion, + isVersionGTE, markAsDarkTheme, Marker, markThemeVersion, @@ -47,7 +48,7 @@ describe('ThemeHelpers', () => { } }, prototypeTheme: BasicTheme, - themeMarkers: [markAsDarkTheme, markThemeVersion(5, 0)], + themeMarkers: [markAsDarkTheme, markThemeVersion('5.0')], }); test('should inherit prototype theme', () => { @@ -63,7 +64,7 @@ describe('ThemeHelpers', () => { expect(isDarkTheme(theme)).toBe(true); }); test('theme version', () => { - expect(isThemeVersionGTE(theme, 1, 0)).toBe(true); + expect(isThemeVersion(theme, '1.0')).toBe(true); }); }); }); @@ -99,11 +100,11 @@ describe('ThemeHelpers', () => { }); }); - describe('isThemeVersionGTE', () => { - const theme5_1 = applyMarkers(ThemeFactory.create(TestTheme), [markThemeVersion(5, 1)]); + describe('isThemeVersion', () => { + const theme5_1 = applyMarkers(ThemeFactory.create(TestTheme), [markThemeVersion('5.1')]); test('5_1 should BE greater or equal that 5_0', () => { - expect(isThemeVersionGTE(theme5_1, 5, 0)).toBe(true); + expect(isThemeVersion(theme5_1, '5.0')).toBe(true); }); }); @@ -121,34 +122,71 @@ describe('ThemeHelpers', () => { }); }); - describe('isThemeVersionGTE', () => { + describe('isThemeVersion', () => { const themeWithoutVersion = TestTheme; - const theme5_5 = applyMarkers(ThemeFactory.create({}, TestTheme), [markThemeVersion(5, 5)]); + const theme5_5 = applyMarkers(ThemeFactory.create({}, TestTheme), [markThemeVersion('5.5')]); test('no version should always return false', () => { - expect(isThemeVersionGTE(themeWithoutVersion, 5, 0)).toBe(false); - expect(isThemeVersionGTE(themeWithoutVersion, 0, 0)).toBe(false); - expect(isThemeVersionGTE(themeWithoutVersion, 6, 0)).toBe(false); + expect(isThemeVersion(themeWithoutVersion, '5.0')).toBe(false); + expect(isThemeVersion(themeWithoutVersion, '0.0')).toBe(false); + expect(isThemeVersion(themeWithoutVersion, '6.0')).toBe(false); }); test('5_5 should BE greater or equal that 5_0', () => { - expect(isThemeVersionGTE(theme5_5, 5, 0)).toBe(true); + expect(isThemeVersion(theme5_5, '5.0')).toBe(true); }); test('5_5 should BE greater or equal that 5_5', () => { - expect(isThemeVersionGTE(theme5_5, 5, 5)).toBe(true); + expect(isThemeVersion(theme5_5, '5.5')).toBe(true); }); test('5_5 should BE greater or equal that 4_0', () => { - expect(isThemeVersionGTE(theme5_5, 4, 0)).toBe(true); + expect(isThemeVersion(theme5_5, '4.0')).toBe(true); }); test('5_5 should NOT BE greater or equal that 5_6', () => { - expect(isThemeVersionGTE(theme5_5, 5, 6)).toBe(false); + expect(isThemeVersion(theme5_5, '5.6')).toBe(false); }); test('5_5 should NOT BE greater or equal that 6_0', () => { - expect(isThemeVersionGTE(theme5_5, 6, 0)).toBe(false); + expect(isThemeVersion(theme5_5, '6.0')).toBe(false); + }); + }); + + describe('isVersionGTE', () => { + test.each` + v1 | v2 | result + ${null} | ${null} | ${false} + ${undefined} | ${undefined} | ${false} + ${''} | ${''} | ${false} + ${'-'} | ${'-'} | ${false} + ${'1'} | ${'1'} | ${false} + ${null} | ${'1.0'} | ${false} + ${undefined} | ${'1.0'} | ${false} + ${''} | ${'1.0'} | ${false} + ${'-'} | ${'1.0'} | ${false} + ${'-.-'} | ${'1.0'} | ${false} + ${'1'} | ${'1.0'} | ${false} + ${'1.0'} | ${null} | ${false} + ${'1.0'} | ${undefined} | ${false} + ${'1.0'} | ${''} | ${false} + ${'1.0'} | ${'-'} | ${false} + ${'1.0'} | ${'-.-'} | ${false} + ${'1.0'} | ${'1'} | ${false} + ${'-.-'} | ${'-.-'} | ${false} + ${'0_0'} | ${'0_0'} | ${false} + ${'0.0'} | ${'0.0'} | ${true} + ${'1.0'} | ${'1.0'} | ${true} + ${'1.1'} | ${'1.1'} | ${true} + ${'1.0'} | ${'1.1'} | ${false} + ${'1.0'} | ${'2.0'} | ${false} + ${'1.0'} | ${'0.0'} | ${true} + ${'1.0'} | ${'0.1'} | ${true} + ${'2.0'} | ${'1.0'} | ${true} + ${'2.1'} | ${'2.0'} | ${true} + ${'2.10'} | ${'2.2'} | ${true} + `('isVersionGTE($v1, $v2) returns $result', ({ v1, v2, result }) => { + expect(isVersionGTE(v1, v2)).toBe(result); }); }); }); diff --git a/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx b/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx index 4da29f4a1c1..9b7e93115c2 100644 --- a/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx +++ b/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx @@ -1,4 +1,4 @@ -import { isDarkTheme, isThemeVersionGTE } from '../../ThemeHelpers'; +import { isDarkTheme, isThemeVersion } from '../../ThemeHelpers'; import { DARK_THEME } from '../DarkTheme'; import { LIGHT_THEME } from '../LightTheme'; import * as DarkThemeImports from '../DarkTheme'; @@ -26,7 +26,7 @@ describe('themes', () => { describe('versions', () => { test.each(ALL_THEMES)('$name has a version', ({ theme }) => { - expect(isThemeVersionGTE(theme, 0, 0)).toBe(true); + expect(isThemeVersion(theme, '0.0')).toBe(true); }); test.each(THEMES_WITH_VERSIONS_IN_NAMES)('name of $name matches its version: $version', ({ theme, version }) => { @@ -35,8 +35,8 @@ describe('themes', () => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const { major, minor } = version!; - expect(isThemeVersionGTE(theme, major, minor)).toBe(true); - expect(isThemeVersionGTE(theme, major, minor + 1)).toBe(false); + expect(isThemeVersion(theme, `${BigInt(major)}.${BigInt(minor)}`)).toBe(true); + expect(isThemeVersion(theme, `${BigInt(major)}.${BigInt(minor + 1)}`)).toBe(false); }); describe('latests', () => { @@ -74,6 +74,7 @@ describe('themes', () => { name | version ${'LIGHT_THEME'} | ${null} ${'LIGHT_THEME_1'} | ${null} + ${'LIGHT_THEME_1_X'} | ${null} ${'LIGHT_THEME_1_0'} | ${{ major: 1, minor: 0 }} ${'LIGHT_THEME_1_0_0'} | ${{ major: 0, minor: 0 }} ${'LIGHT_THEME_10_10'} | ${{ major: 10, minor: 10 }} From 4a54743ec1ecf5b8d08c4c73a417df7a053d34c0 Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Fri, 28 Feb 2025 12:17:53 +0500 Subject: [PATCH 26/27] chore: refactor theme helpers and simplify types --- .../__stories__/ThemeVersioning.stories.tsx | 15 +++-- packages/react-ui/lib/theming/ThemeHelpers.ts | 54 +++++++-------- .../react-ui/lib/theming/ThemeVersions.ts | 37 ++++++++++ .../theming/__tests__/ThemeHelpers-test.tsx | 67 +++++-------------- .../theming/__tests__/ThemeVersions-test.tsx | 66 ++++++++++++++++++ .../theming/themes/__tests__/Themes-test.tsx | 31 +++------ 6 files changed, 163 insertions(+), 107 deletions(-) create mode 100644 packages/react-ui/lib/theming/ThemeVersions.ts create mode 100644 packages/react-ui/lib/theming/__tests__/ThemeVersions-test.tsx diff --git a/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx index 6e97d10358f..fe0f5ee040d 100644 --- a/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx +++ b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx @@ -2,8 +2,9 @@ import React, { CSSProperties } from 'react'; import { Story, Meta } from '../../../typings/stories'; -import { createTheme, isThemeVersion, markThemeVersion } from '../../../lib/theming/ThemeHelpers'; +import { createTheme, isThemeGTE, markThemeVersion } from '../../../lib/theming/ThemeHelpers'; import { BasicThemeClassForExtension } from '../../../internal/themes/BasicTheme'; +import { ThemeVersions } from '../../../lib/theming/ThemeVersions'; export default { title: 'ThemeVersions/Test', @@ -32,7 +33,7 @@ const TEST_THEME_1_0 = createTheme({ public static color = 'red'; public static textTransform = 'lowercase'; }, - themeMarkers: [markThemeVersion('1.0')], + themeMarkers: [markThemeVersion('1.0' as ThemeVersions)], prototypeTheme: TEST_THEME_BASIC, }); @@ -41,7 +42,7 @@ const TEST_THEME_1_1 = createTheme({ public static color = 'green'; public static fontStyle = 'italic'; }, - themeMarkers: [markThemeVersion('1.1')], + themeMarkers: [markThemeVersion('1.1' as ThemeVersions)], prototypeTheme: TEST_THEME_1_0, }); @@ -53,8 +54,8 @@ const Component = ({ theme }: { theme: TestThemeIn }) => { }; const themeVersionList = Object.entries({ - '1_0': isThemeVersion(theme, '1.0'), - '1_1': isThemeVersion(theme, '1.1'), + '1_0': isThemeGTE(theme, '1.0' as ThemeVersions), + '1_1': isThemeGTE(theme, '1.1' as ThemeVersions), }) .filter(([_, isDetected]) => isDetected) .map(([version]) =>
  • {version}
  • ); @@ -66,9 +67,9 @@ const Component = ({ theme }: { theme: TestThemeIn }) => {
    Detected theme versions: {themeVersionList.length === 0 && 'none'} - {isThemeVersion(theme, '1.1') ? ( + {isThemeGTE(theme, '1.1' as ThemeVersions) ? (
      {themeVersionList}
    - ) : isThemeVersion(theme, '1.0') ? ( + ) : isThemeGTE(theme, '1.0' as ThemeVersions) ? (
      {themeVersionList}
    ) : null}
    diff --git a/packages/react-ui/lib/theming/ThemeHelpers.ts b/packages/react-ui/lib/theming/ThemeHelpers.ts index 9706c0e4ec8..b4595752bc0 100644 --- a/packages/react-ui/lib/theming/ThemeHelpers.ts +++ b/packages/react-ui/lib/theming/ThemeHelpers.ts @@ -1,15 +1,18 @@ +import warning from 'warning'; + import { Nullable } from '../../typings/utility-types'; import { isNonNullable } from '../utils'; import { Theme, ThemeIn } from './Theme'; +import { isThemeVersionGTE, ThemeVersions } from './ThemeVersions'; -export type Marker = (theme: Readonly) => Readonly; +export type Marker = (theme: Theme) => Theme; export type Markers = Marker[]; export const ThemeVersionSeparator = '.' as const; export type ThemeVersions = '5.0' | '5.1'; export type ThemeVersionType = `${bigint}${typeof ThemeVersionSeparator}${bigint}`; -export const exposeGetters = >(theme: T): T => { +export const exposeGetters = (theme: Theme): Theme => { const descriptors = Object.getOwnPropertyDescriptors(theme); Object.keys(descriptors).forEach((key) => { const descriptor = descriptors[key]; @@ -28,7 +31,7 @@ export const REACT_UI_THEME_MARKERS: { }; themeVersion: { key: string; - value: ThemeVersionType; + value: ThemeVersions | null; }; } = { darkTheme: { @@ -37,7 +40,7 @@ export const REACT_UI_THEME_MARKERS: { }, themeVersion: { key: '__REACT_UI_THEME_VERSION__', - value: '0.0', + value: null, }, }; @@ -60,27 +63,28 @@ export const markAsDarkTheme: Marker = (theme) => { }); }; -export const markThemeVersion: (version: T) => Marker = - (version) => (theme) => { - return Object.create(theme, { - [REACT_UI_THEME_MARKERS.themeVersion.key]: { - value: version || REACT_UI_THEME_MARKERS.themeVersion.value, - writable: false, - enumerable: false, - configurable: false, - }, - }); - }; +export const markThemeVersion: (version: ThemeVersions) => Marker = (version) => (theme) => { + return Object.create(theme, { + [REACT_UI_THEME_MARKERS.themeVersion.key]: { + value: version || REACT_UI_THEME_MARKERS.themeVersion.value, + writable: false, + enumerable: false, + configurable: false, + }, + }); +}; -export const isThemeVersion = ( - theme: Theme | ThemeIn, - version: T, -): boolean => { - const themeVersion: T | undefined = +export const isThemeGTE = (theme: Theme | ThemeIn, version: ThemeVersions): boolean => { + const themeVersion: Nullable = // @ts-expect-error: internal value. theme[REACT_UI_THEME_MARKERS.themeVersion.key]; - return isVersionGTE(themeVersion, version); + if (!themeVersion) { + warning(true, `[ThemeHelpers]: The theme doesn't have a version. Checking for ${version}.`); + return false; + } + + return isThemeVersionGTE(themeVersion, version); }; export function findPropertyDescriptor(theme: Theme, propName: string) { @@ -95,17 +99,13 @@ export function findPropertyDescriptor(theme: Theme, propName: string) { return {}; } -export function applyMarkers(theme: T, markers: Markers): T { +export function applyMarkers(theme: Theme, markers: Markers): Theme { return markers.reduce((markedTheme, marker) => { return marker(markedTheme); }, Object.create(theme)); } -export function createTheme(options: { - themeClass: Theme; - prototypeTheme?: Theme; - themeMarkers?: Markers; -}): Readonly { +export function createTheme(options: { themeClass: Theme; prototypeTheme?: Theme; themeMarkers?: Markers }): Theme { const { themeClass, prototypeTheme, themeMarkers = [] } = options; if (prototypeTheme) { diff --git a/packages/react-ui/lib/theming/ThemeVersions.ts b/packages/react-ui/lib/theming/ThemeVersions.ts new file mode 100644 index 00000000000..24f7a87f897 --- /dev/null +++ b/packages/react-ui/lib/theming/ThemeVersions.ts @@ -0,0 +1,37 @@ +export type ThemeVersions = '5.0' | '5.1'; +export interface ThemeVersionParsed { + major: number; + minor: number; +} + +export const parseThemeVersion = (str: string, separator: string = '\\.'): ThemeVersionParsed | null => { + const match = str.match(new RegExp(`(\\d+)${separator}(\\d+)`)); + const major = match?.[1]; + const minor = match?.[2]; + + if (!major || !minor) { + return null; + } + + return { + major: Number(major), + minor: Number(minor), + }; +}; + +export const isThemeVersionGTE = (v1: ThemeVersions, v2: ThemeVersions): boolean => { + const parsedV1 = parseThemeVersion(v1); + const parsedV2 = parseThemeVersion(v2); + + if (!parsedV1 || !parsedV2) { + return false; + } + + if (parsedV1.major > parsedV2.major) { + return true; + } else if (parsedV1.major === parsedV2.major) { + return parsedV1.minor >= parsedV2.minor; + } + + return false; +}; diff --git a/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx index cf7a0533f36..5ff642627ce 100644 --- a/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx +++ b/packages/react-ui/lib/theming/__tests__/ThemeHelpers-test.tsx @@ -3,13 +3,13 @@ import { createTheme, exposeGetters, isDarkTheme, - isThemeVersion, - isVersionGTE, + isThemeGTE, markAsDarkTheme, Marker, markThemeVersion, REACT_UI_THEME_MARKERS, } from '../ThemeHelpers'; +import { ThemeVersions } from '../ThemeVersions'; import { ThemeFactory } from '../ThemeFactory'; import { AnyObject } from '../../utils'; import { BasicTheme, BasicThemeClassForExtension } from '../../../internal/themes/BasicTheme'; @@ -64,7 +64,7 @@ describe('ThemeHelpers', () => { expect(isDarkTheme(theme)).toBe(true); }); test('theme version', () => { - expect(isThemeVersion(theme, '1.0')).toBe(true); + expect(isThemeGTE(theme, '5.0')).toBe(true); }); }); }); @@ -100,11 +100,11 @@ describe('ThemeHelpers', () => { }); }); - describe('isThemeVersion', () => { + describe('isThemeGTE', () => { const theme5_1 = applyMarkers(ThemeFactory.create(TestTheme), [markThemeVersion('5.1')]); test('5_1 should BE greater or equal that 5_0', () => { - expect(isThemeVersion(theme5_1, '5.0')).toBe(true); + expect(isThemeGTE(theme5_1, '5.0')).toBe(true); }); }); @@ -122,71 +122,34 @@ describe('ThemeHelpers', () => { }); }); - describe('isThemeVersion', () => { + describe('isThemeGTE', () => { const themeWithoutVersion = TestTheme; - const theme5_5 = applyMarkers(ThemeFactory.create({}, TestTheme), [markThemeVersion('5.5')]); + const theme5_5 = applyMarkers(ThemeFactory.create({}, TestTheme), [markThemeVersion('5.5' as ThemeVersions)]); test('no version should always return false', () => { - expect(isThemeVersion(themeWithoutVersion, '5.0')).toBe(false); - expect(isThemeVersion(themeWithoutVersion, '0.0')).toBe(false); - expect(isThemeVersion(themeWithoutVersion, '6.0')).toBe(false); + expect(isThemeGTE(themeWithoutVersion, '5.0')).toBe(false); + expect(isThemeGTE(themeWithoutVersion, '0.0' as ThemeVersions)).toBe(false); + expect(isThemeGTE(themeWithoutVersion, '6.0' as ThemeVersions)).toBe(false); }); test('5_5 should BE greater or equal that 5_0', () => { - expect(isThemeVersion(theme5_5, '5.0')).toBe(true); + expect(isThemeGTE(theme5_5, '5.0')).toBe(true); }); test('5_5 should BE greater or equal that 5_5', () => { - expect(isThemeVersion(theme5_5, '5.5')).toBe(true); + expect(isThemeGTE(theme5_5, '5.5' as ThemeVersions)).toBe(true); }); test('5_5 should BE greater or equal that 4_0', () => { - expect(isThemeVersion(theme5_5, '4.0')).toBe(true); + expect(isThemeGTE(theme5_5, '4.0' as ThemeVersions)).toBe(true); }); test('5_5 should NOT BE greater or equal that 5_6', () => { - expect(isThemeVersion(theme5_5, '5.6')).toBe(false); + expect(isThemeGTE(theme5_5, '5.6' as ThemeVersions)).toBe(false); }); test('5_5 should NOT BE greater or equal that 6_0', () => { - expect(isThemeVersion(theme5_5, '6.0')).toBe(false); - }); - }); - - describe('isVersionGTE', () => { - test.each` - v1 | v2 | result - ${null} | ${null} | ${false} - ${undefined} | ${undefined} | ${false} - ${''} | ${''} | ${false} - ${'-'} | ${'-'} | ${false} - ${'1'} | ${'1'} | ${false} - ${null} | ${'1.0'} | ${false} - ${undefined} | ${'1.0'} | ${false} - ${''} | ${'1.0'} | ${false} - ${'-'} | ${'1.0'} | ${false} - ${'-.-'} | ${'1.0'} | ${false} - ${'1'} | ${'1.0'} | ${false} - ${'1.0'} | ${null} | ${false} - ${'1.0'} | ${undefined} | ${false} - ${'1.0'} | ${''} | ${false} - ${'1.0'} | ${'-'} | ${false} - ${'1.0'} | ${'-.-'} | ${false} - ${'1.0'} | ${'1'} | ${false} - ${'-.-'} | ${'-.-'} | ${false} - ${'0_0'} | ${'0_0'} | ${false} - ${'0.0'} | ${'0.0'} | ${true} - ${'1.0'} | ${'1.0'} | ${true} - ${'1.1'} | ${'1.1'} | ${true} - ${'1.0'} | ${'1.1'} | ${false} - ${'1.0'} | ${'2.0'} | ${false} - ${'1.0'} | ${'0.0'} | ${true} - ${'1.0'} | ${'0.1'} | ${true} - ${'2.0'} | ${'1.0'} | ${true} - ${'2.1'} | ${'2.0'} | ${true} - ${'2.10'} | ${'2.2'} | ${true} - `('isVersionGTE($v1, $v2) returns $result', ({ v1, v2, result }) => { - expect(isVersionGTE(v1, v2)).toBe(result); + expect(isThemeGTE(theme5_5, '6.0' as ThemeVersions)).toBe(false); }); }); }); diff --git a/packages/react-ui/lib/theming/__tests__/ThemeVersions-test.tsx b/packages/react-ui/lib/theming/__tests__/ThemeVersions-test.tsx new file mode 100644 index 00000000000..71cb43af732 --- /dev/null +++ b/packages/react-ui/lib/theming/__tests__/ThemeVersions-test.tsx @@ -0,0 +1,66 @@ +import { parseThemeVersion, isThemeVersionGTE } from '../ThemeVersions'; + +describe('ThemeVersions', () => { + describe('parseThemeVersion', () => { + test.each` + v | result + ${''} | ${null} + ${'_'} | ${null} + ${'.'} | ${null} + ${'-.-'} | ${null} + ${'1'} | ${null} + ${'0_0'} | ${null} + ${'0.0'} | ${{ major: 0, minor: 0 }} + ${'1.0'} | ${{ major: 1, minor: 0 }} + ${'2.1'} | ${{ major: 2, minor: 1 }} + `('parseThemeVersion($v) returns $result', ({ v, result }) => { + expect(parseThemeVersion(v)).toStrictEqual(result); + }); + + test.each` + v | separator | result + ${''} | ${'_'} | ${null} + ${'_'} | ${'_'} | ${null} + ${'.'} | ${'_'} | ${null} + ${'-.-'} | ${'_'} | ${null} + ${'1'} | ${'_'} | ${null} + ${'0.0'} | ${'_'} | ${null} + ${'0_0'} | ${'_'} | ${{ major: 0, minor: 0 }} + ${'1_0'} | ${'_'} | ${{ major: 1, minor: 0 }} + ${'2_1'} | ${'_'} | ${{ major: 2, minor: 1 }} + `('parseThemeVersion($v, $separator) returns $result', ({ v, separator, result }) => { + expect(parseThemeVersion(v, separator)).toStrictEqual(result); + }); + }); + + describe('isThemeVersionGTE', () => { + test.each` + v1 | v2 | result + ${''} | ${''} | ${false} + ${'-'} | ${'-'} | ${false} + ${'1'} | ${'1'} | ${false} + ${''} | ${'1.0'} | ${false} + ${'-'} | ${'1.0'} | ${false} + ${'-.-'} | ${'1.0'} | ${false} + ${'1'} | ${'1.0'} | ${false} + ${'1.0'} | ${''} | ${false} + ${'1.0'} | ${'-'} | ${false} + ${'1.0'} | ${'-.-'} | ${false} + ${'1.0'} | ${'1'} | ${false} + ${'-.-'} | ${'-.-'} | ${false} + ${'0_0'} | ${'0_0'} | ${false} + ${'0.0'} | ${'0.0'} | ${true} + ${'1.0'} | ${'1.0'} | ${true} + ${'1.1'} | ${'1.1'} | ${true} + ${'1.0'} | ${'1.1'} | ${false} + ${'1.0'} | ${'2.0'} | ${false} + ${'1.0'} | ${'0.0'} | ${true} + ${'1.0'} | ${'0.1'} | ${true} + ${'2.0'} | ${'1.0'} | ${true} + ${'2.1'} | ${'2.0'} | ${true} + ${'2.10'} | ${'2.2'} | ${true} + `('isThemeVersionGTE($v1, $v2) returns $result', ({ v1, v2, result }) => { + expect(isThemeVersionGTE(v1, v2)).toBe(result); + }); + }); +}); diff --git a/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx b/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx index 9b7e93115c2..86d90adc50e 100644 --- a/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx +++ b/packages/react-ui/lib/theming/themes/__tests__/Themes-test.tsx @@ -1,19 +1,15 @@ -import { isDarkTheme, isThemeVersion } from '../../ThemeHelpers'; +import { isDarkTheme, isThemeGTE } from '../../ThemeHelpers'; +import { ThemeVersions, ThemeVersionParsed, parseThemeVersion } from '../../ThemeVersions'; import { DARK_THEME } from '../DarkTheme'; import { LIGHT_THEME } from '../LightTheme'; import * as DarkThemeImports from '../DarkTheme'; import * as LightThemeImports from '../LightTheme'; import { Theme } from '../../Theme'; -interface ThemeVersion { - major: number; - minor: number; -} - interface ThemeWithNameAndVersion { theme: Theme; name: string; - version: ThemeVersion | null; + version: ThemeVersionParsed | null; } describe('themes', () => { @@ -26,7 +22,8 @@ describe('themes', () => { describe('versions', () => { test.each(ALL_THEMES)('$name has a version', ({ theme }) => { - expect(isThemeVersion(theme, '0.0')).toBe(true); + // comparing with '0.0' ensures that the theme has a valid version + expect(isThemeGTE(theme, '0.0' as ThemeVersions)).toBe(true); }); test.each(THEMES_WITH_VERSIONS_IN_NAMES)('name of $name matches its version: $version', ({ theme, version }) => { @@ -35,8 +32,8 @@ describe('themes', () => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const { major, minor } = version!; - expect(isThemeVersion(theme, `${BigInt(major)}.${BigInt(minor)}`)).toBe(true); - expect(isThemeVersion(theme, `${BigInt(major)}.${BigInt(minor + 1)}`)).toBe(false); + expect(isThemeGTE(theme, `${major}.${minor}` as ThemeVersions)).toBe(true); + expect(isThemeGTE(theme, `${major}.${minor + 1}` as ThemeVersions)).toBe(false); }); describe('latests', () => { @@ -76,7 +73,7 @@ describe('themes', () => { ${'LIGHT_THEME_1'} | ${null} ${'LIGHT_THEME_1_X'} | ${null} ${'LIGHT_THEME_1_0'} | ${{ major: 1, minor: 0 }} - ${'LIGHT_THEME_1_0_0'} | ${{ major: 0, minor: 0 }} + ${'LIGHT_THEME_1_0_0'} | ${{ major: 1, minor: 0 }} ${'LIGHT_THEME_10_10'} | ${{ major: 10, minor: 10 }} ${'LIGHT_THEME_00_00'} | ${{ major: 0, minor: 0 }} `('$name should result to $version', ({ name, version }) => { @@ -151,16 +148,8 @@ describe('themes', () => { }); }); -function getVersionFromThemeName(name: string): ThemeVersion | null { - const versionString = name.match(/\d+_\d+$/g); - - if (!versionString) { - return null; - } - - const [major, minor] = versionString[0].split('_').map(Number); - - return { major, minor }; +function getVersionFromThemeName(name: string): ThemeVersionParsed | null { + return parseThemeVersion(name, '_'); } function getThemesFromImports(themes: Record): ThemeWithNameAndVersion[] { From 11a23c3637e458c9c6731673e2b9e4471391accd Mon Sep 17 00:00:00 2001 From: Egor Pogadaev Date: Fri, 28 Feb 2025 12:40:29 +0500 Subject: [PATCH 27/27] chore: fix lint --- .../__stories__/ThemeVersioning.stories.tsx | 1 - packages/react-ui/lib/theming/ThemeHelpers.ts | 20 ------------------- 2 files changed, 21 deletions(-) diff --git a/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx index fe0f5ee040d..6453f14040d 100644 --- a/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx +++ b/packages/react-ui/internal/ThemePlayground/__stories__/ThemeVersioning.stories.tsx @@ -24,7 +24,6 @@ class TestThemeClass extends BasicThemeClassForExtension { } type TestThemeIn = Partial; -type TestVersions = '1.0' | '1.1'; const TEST_THEME_BASIC = createTheme({ themeClass: TestThemeClass }); diff --git a/packages/react-ui/lib/theming/ThemeHelpers.ts b/packages/react-ui/lib/theming/ThemeHelpers.ts index b4595752bc0..71c07409be9 100644 --- a/packages/react-ui/lib/theming/ThemeHelpers.ts +++ b/packages/react-ui/lib/theming/ThemeHelpers.ts @@ -8,9 +8,6 @@ import { isThemeVersionGTE, ThemeVersions } from './ThemeVersions'; export type Marker = (theme: Theme) => Theme; export type Markers = Marker[]; -export const ThemeVersionSeparator = '.' as const; -export type ThemeVersions = '5.0' | '5.1'; -export type ThemeVersionType = `${bigint}${typeof ThemeVersionSeparator}${bigint}`; export const exposeGetters = (theme: Theme): Theme => { const descriptors = Object.getOwnPropertyDescriptors(theme); @@ -116,20 +113,3 @@ export function createTheme(options: { themeClass: Theme; prototypeTheme?: Theme return Object.freeze(theme); } - -export const isVersionGTE = (v1: Nullable, v2: Nullable): boolean => { - if (!v1 || !v2) { - return false; - } - - const [majorV1, minorV1] = v1.split(ThemeVersionSeparator).map(Number); - const [majorV2, minorV2] = v2.split(ThemeVersionSeparator).map(Number); - - if (majorV1 > majorV2) { - return true; - } else if (majorV1 === majorV2) { - return minorV1 >= minorV2; - } - - return false; -};