From b549159f612e714c294c8e3921ef518301a0ebb4 Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Thu, 22 Jan 2026 14:06:25 -0500 Subject: [PATCH 01/18] bumped react, react-dom, types, and types/react-dom --- package.json | 8 ++--- yarn.lock | 82 ++++++++++++++-------------------------------------- 2 files changed, 26 insertions(+), 64 deletions(-) diff --git a/package.json b/package.json index 02cf271da2..9c33f56563 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,8 @@ "@vidstack/react": "^1.12.12", "core-js": "3.7.0", "lodash": "^4.17.5", - "react": "18.3.1", - "react-dom": "18.3.1", + "react": "^19.2.3", + "react-dom": "^19.2.3", "react-helmet-async": "^2.0.5" }, "devDependencies": { @@ -53,8 +53,8 @@ "@types/invariant": "2.2.29", "@types/konami-code-js": "^0.8.0", "@types/lodash": "4.17.0", - "@types/react": "18.3.1", - "@types/react-dom": "18.3.0", + "@types/react": "^19.2.9", + "@types/react-dom": "^19.2.3", "@types/react-test-renderer": "^17.0.1", "@types/stylis": "^4.2.0", "@typescript-eslint/eslint-plugin": "^5.15.0", diff --git a/yarn.lock b/yarn.lock index 58fa2a2af9..c3c9632c1d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5843,13 +5843,6 @@ resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/react-dom@18.3.0": - version "18.3.0" - resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz#0cbc818755d87066ab6ca74fbedb2547d74a82b0" - integrity sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg== - dependencies: - "@types/react" "*" - "@types/react-dom@>=16.9.0", "@types/react-dom@^18.0.0": version "18.3.1" resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz#1e4654c08a9cdcfb6594c780ac59b55aad42fe07" @@ -5857,6 +5850,11 @@ dependencies: "@types/react" "*" +"@types/react-dom@^19.2.3": + version "19.2.3" + resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c" + integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ== + "@types/react-test-renderer@>=16.9.0": version "18.3.0" resolved "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.3.0.tgz#839502eae70058a4ae161f63385a8e7929cef4c0" @@ -5886,14 +5884,6 @@ "@types/prop-types" "*" csstype "^3.0.2" -"@types/react@18.3.1": - version "18.3.1" - resolved "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz#fed43985caa834a2084d002e4771e15dfcbdbe8e" - integrity sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw== - dependencies: - "@types/prop-types" "*" - csstype "^3.0.2" - "@types/react@^17": version "17.0.83" resolved "https://registry.npmjs.org/@types/react/-/react-17.0.83.tgz#b477c56387b74279281149dcf5ba2a1e2216d131" @@ -5903,6 +5893,13 @@ "@types/scheduler" "^0.16" csstype "^3.0.2" +"@types/react@^19.2.9": + version "19.2.9" + resolved "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz#84ec7669742bb3e7e2e8d6a5258d95ead7764200" + integrity sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA== + dependencies: + csstype "^3.2.2" + "@types/resolve@1.20.2": version "1.20.2" resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975" @@ -8506,6 +8503,11 @@ csstype@^3.0.2, csstype@^3.0.7, csstype@^3.1.2: resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +csstype@^3.2.2: + version "3.2.3" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" + integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== + damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" @@ -15624,15 +15626,7 @@ react-docgen@^7.0.0: resolve "^1.22.1" strip-indent "^4.0.0" -react-dom@18.3.1: - version "18.3.1" - resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" - integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== - dependencies: - loose-envify "^1.1.0" - scheduler "^0.23.2" - -"react-dom@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0": +"react-dom@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", react-dom@^19.2.3: version "19.2.3" resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz#f0b61d7e5c4a86773889fcc1853af3ed5f215b17" integrity sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg== @@ -15851,14 +15845,7 @@ react-use@^15.3.8: ts-easing "^0.2.0" tslib "^2.0.0" -react@18.3.1: - version "18.3.1" - resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" - integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== - dependencies: - loose-envify "^1.1.0" - -"react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0": +"react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", react@^19.2.3: version "19.2.3" resolved "https://registry.npmjs.org/react/-/react-19.2.3.tgz#d83e5e8e7a258cf6b4fe28640515f99b87cd19b8" integrity sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA== @@ -17204,16 +17191,7 @@ string-length@^4.0.1, string-length@^4.0.2: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -17317,14 +17295,7 @@ stringify-entities@^3.0.0: character-entities-legacy "^1.0.0" xtend "^4.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -18759,7 +18730,7 @@ wordwrap@^1.0.0: resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -18777,15 +18748,6 @@ wrap-ansi@^6.0.1: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From cedf8b70627d33c3180928dc432b40ad7ece8cad Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Thu, 22 Jan 2026 14:16:51 -0500 Subject: [PATCH 02/18] fix linting issue --- packages/gamut-styles/src/variance/utils.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/gamut-styles/src/variance/utils.ts b/packages/gamut-styles/src/variance/utils.ts index a88b1bfb11..3d82f4b14b 100644 --- a/packages/gamut-styles/src/variance/utils.ts +++ b/packages/gamut-styles/src/variance/utils.ts @@ -20,8 +20,8 @@ export type SystemPropNames = (typeof allPropnames)[number]; export type ElementOrProps = keyof JSX.IntrinsicElements | ThemeProps; export type ForwardableProps = Exclude< El extends keyof JSX.IntrinsicElements - ? keyof JSX.IntrinsicElements[El] - : keyof Element, + ? keyof JSX.IntrinsicElements[El] + : keyof Element, Additional | SystemPropNames >; @@ -57,7 +57,8 @@ export function createStyledOptions< * styled(Box)() * */ -export const styledOptions = Object.assign( - createStyledOptions, - createStyledOptions() -); +export const styledOptions: typeof createStyledOptions & + ReturnType = Object.assign( + createStyledOptions, + createStyledOptions() + ); From ac4a52952e25d400bbe3f74924bb00e2efa4290c Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Thu, 22 Jan 2026 14:27:04 -0500 Subject: [PATCH 03/18] bumped react-test-renderer types and fixed SVG linting errors --- package.json | 2 +- packages/gamut-icons/src/props.ts | 9 ++++++--- yarn.lock | 24 +++++------------------- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 9c33f56563..e654382d17 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "@types/lodash": "4.17.0", "@types/react": "^19.2.9", "@types/react-dom": "^19.2.3", - "@types/react-test-renderer": "^17.0.1", + "@types/react-test-renderer": "^19.1.0", "@types/stylis": "^4.2.0", "@typescript-eslint/eslint-plugin": "^5.15.0", "@typescript-eslint/parser": "^5.15.0", diff --git a/packages/gamut-icons/src/props.ts b/packages/gamut-icons/src/props.ts index 14cfaa4c64..8a0e913b91 100644 --- a/packages/gamut-icons/src/props.ts +++ b/packages/gamut-icons/src/props.ts @@ -1,6 +1,6 @@ import { styledOptions, system } from '@codecademy/gamut-styles'; import { StyleProps, variance } from '@codecademy/variance'; -import styled from '@emotion/styled'; +import styled, { StyledComponent } from '@emotion/styled'; export interface IconStyleProps extends StyleProps { /** @@ -12,7 +12,7 @@ export interface IconStyleProps extends StyleProps { export interface GamutBaseIconProps extends Omit, keyof IconStyleProps>, - IconStyleProps { + IconStyleProps { /** * A suffix added to the end of the unique generated ID for the icon. This is useful if you have multiple of the same icon on the page and need to pass accessibility guidelines. */ @@ -46,4 +46,7 @@ export const iconProps = variance.compose( system.border ); -export const Svg = styled('svg', styledOptions<'svg'>())(iconProps); +export const Svg: StyledComponent = styled( + 'svg', + styledOptions<'svg'>() +)(iconProps); diff --git a/yarn.lock b/yarn.lock index c3c9632c1d..0290c26753 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5862,12 +5862,12 @@ dependencies: "@types/react" "*" -"@types/react-test-renderer@^17.0.1": - version "17.0.9" - resolved "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-17.0.9.tgz#da6d06f3f37eefab39386c390140374dc5db5b33" - integrity sha512-bOfxcu5oZ+KxvACScbkTwZ4eGCtZFTz4VZCOVAIfGbThxqiXSIGipKVG8ubaYBXquUSQROzNIUzviWdSnnAlzg== +"@types/react-test-renderer@^19.1.0": + version "19.1.0" + resolved "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-19.1.0.tgz#1d0af8f2e1b5931e245b8b5b234d1502b854dc10" + integrity sha512-XD0WZrHqjNrxA/MaR9O22w/RNidWR9YZmBdRGI7wcnWGrv/3dA8wKCJ8m63Sn+tLJhcjmuhOi629N66W6kgWzQ== dependencies: - "@types/react" "^17" + "@types/react" "*" "@types/react-transition-group@^4.4.0": version "4.4.11" @@ -5884,15 +5884,6 @@ "@types/prop-types" "*" csstype "^3.0.2" -"@types/react@^17": - version "17.0.83" - resolved "https://registry.npmjs.org/@types/react/-/react-17.0.83.tgz#b477c56387b74279281149dcf5ba2a1e2216d131" - integrity sha512-l0m4ArKJvmFtR4e8UmKrj1pB4tUgOhJITf+mADyF/p69Ts1YAR/E+G9XEM0mHXKVRa1dQNHseyyDNzeuAXfXQw== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "^0.16" - csstype "^3.0.2" - "@types/react@^19.2.9": version "19.2.9" resolved "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz#84ec7669742bb3e7e2e8d6a5258d95ead7764200" @@ -5915,11 +5906,6 @@ resolved "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz#ed279a64fa438bb69f2480eda44937912bb7480a" integrity sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow== -"@types/scheduler@^0.16": - version "0.16.8" - resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" - integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== - "@types/semver@7.5.8", "@types/semver@^7.3.12", "@types/semver@^7.3.4": version "7.5.8" resolved "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" From 57dc0377955cc7b600ec53c2e98d232f12652a00 Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Thu, 22 Jan 2026 14:31:59 -0500 Subject: [PATCH 04/18] resolve linting issue with patterns --- packages/gamut-patterns/src/props.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/gamut-patterns/src/props.tsx b/packages/gamut-patterns/src/props.tsx index 1fdf68df5b..b473df4da2 100644 --- a/packages/gamut-patterns/src/props.tsx +++ b/packages/gamut-patterns/src/props.tsx @@ -1,7 +1,7 @@ import { styledOptions, system } from '@codecademy/gamut-styles'; import { StyleProps, variance } from '@codecademy/variance'; -import styled from '@emotion/styled'; -import { ComponentProps, forwardRef } from 'react'; +import styled, { StyledComponent } from '@emotion/styled'; +import { forwardRef } from 'react'; const patternStyles = variance.compose( system.layout, @@ -21,12 +21,12 @@ export interface PatternProps ref?: React.Ref; } -const StyledSvg = styled( +const StyledSvg: StyledComponent = styled( 'svg', styledOptions<'svg'>() -)(patternStyles); +)(patternStyles); -export const Svg = forwardRef>( +export const Svg = forwardRef( ({ height = '100%', width = '100%', ...rest }, ref) => ( ) From d6ac11682557e892539c9ac66d1572d00d54d5c3 Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Thu, 22 Jan 2026 14:37:44 -0500 Subject: [PATCH 05/18] more linting issues --- packages/gamut-icons/src/props.ts | 6 +++--- packages/gamut-patterns/src/props.tsx | 4 ++-- packages/gamut-styles/src/variance/utils.ts | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/gamut-icons/src/props.ts b/packages/gamut-icons/src/props.ts index 8a0e913b91..9f517d6c7d 100644 --- a/packages/gamut-icons/src/props.ts +++ b/packages/gamut-icons/src/props.ts @@ -12,7 +12,7 @@ export interface IconStyleProps extends StyleProps { export interface GamutBaseIconProps extends Omit, keyof IconStyleProps>, - IconStyleProps { + IconStyleProps { /** * A suffix added to the end of the unique generated ID for the icon. This is useful if you have multiple of the same icon on the page and need to pass accessibility guidelines. */ @@ -46,7 +46,7 @@ export const iconProps = variance.compose( system.border ); -export const Svg: StyledComponent = styled( +export const Svg = styled( 'svg', styledOptions<'svg'>() -)(iconProps); +)(iconProps) as StyledComponent; diff --git a/packages/gamut-patterns/src/props.tsx b/packages/gamut-patterns/src/props.tsx index b473df4da2..f2906226b7 100644 --- a/packages/gamut-patterns/src/props.tsx +++ b/packages/gamut-patterns/src/props.tsx @@ -21,10 +21,10 @@ export interface PatternProps ref?: React.Ref; } -const StyledSvg: StyledComponent = styled( +const StyledSvg = styled( 'svg', styledOptions<'svg'>() -)(patternStyles); +)(patternStyles) as StyledComponent; export const Svg = forwardRef( ({ height = '100%', width = '100%', ...rest }, ref) => ( diff --git a/packages/gamut-styles/src/variance/utils.ts b/packages/gamut-styles/src/variance/utils.ts index 3d82f4b14b..4c58d7b4b3 100644 --- a/packages/gamut-styles/src/variance/utils.ts +++ b/packages/gamut-styles/src/variance/utils.ts @@ -20,8 +20,8 @@ export type SystemPropNames = (typeof allPropnames)[number]; export type ElementOrProps = keyof JSX.IntrinsicElements | ThemeProps; export type ForwardableProps = Exclude< El extends keyof JSX.IntrinsicElements - ? keyof JSX.IntrinsicElements[El] - : keyof Element, + ? keyof JSX.IntrinsicElements[El] + : keyof Element, Additional | SystemPropNames >; @@ -59,6 +59,6 @@ export function createStyledOptions< */ export const styledOptions: typeof createStyledOptions & ReturnType = Object.assign( - createStyledOptions, - createStyledOptions() - ); + createStyledOptions, + createStyledOptions() +); From 92bb2650b2e35038570e336544102d3ce79295ae Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Thu, 22 Jan 2026 14:48:08 -0500 Subject: [PATCH 06/18] another try to address same linting issue --- packages/gamut-styles/src/variance/utils.ts | 24 +++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/gamut-styles/src/variance/utils.ts b/packages/gamut-styles/src/variance/utils.ts index 4c58d7b4b3..d4d9a583d6 100644 --- a/packages/gamut-styles/src/variance/utils.ts +++ b/packages/gamut-styles/src/variance/utils.ts @@ -20,8 +20,8 @@ export type SystemPropNames = (typeof allPropnames)[number]; export type ElementOrProps = keyof JSX.IntrinsicElements | ThemeProps; export type ForwardableProps = Exclude< El extends keyof JSX.IntrinsicElements - ? keyof JSX.IntrinsicElements[El] - : keyof Element, + ? keyof JSX.IntrinsicElements[El] + : keyof Element, Additional | SystemPropNames >; @@ -41,6 +41,23 @@ export function createStyledOptions< }; } +/** Return type of createStyledOptions */ +export type StyledOptionsResult< + El extends ElementOrProps = 'div', + Additional extends string = never +> = { + shouldForwardProp: ( + prop: PropertyKey + ) => prop is ForwardableProps; +}; + +/** Type for styledOptions - callable with generics AND has shouldForwardProp property */ +export interface StyledOptions extends StyledOptionsResult { + ( + additional?: readonly Additional[] + ): StyledOptionsResult; +} + /** * @description * This object can be passed to the second argument of `styled('div', styledOptions)` or be called as a function to filter additional prop names @@ -57,8 +74,7 @@ export function createStyledOptions< * styled(Box)() * */ -export const styledOptions: typeof createStyledOptions & - ReturnType = Object.assign( +export const styledOptions: StyledOptions = Object.assign( createStyledOptions, createStyledOptions() ); From b9ac907df85bfe21b18c26b30aba995c4e7616f7 Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Thu, 22 Jan 2026 15:12:37 -0500 Subject: [PATCH 07/18] added resolution since older packages aren't playing nice with their types --- package.json | 10 +-- yarn.lock | 197 +++++++++------------------------------------------ 2 files changed, 37 insertions(+), 170 deletions(-) diff --git a/package.json b/package.json index e654382d17..e648dad629 100644 --- a/package.json +++ b/package.json @@ -44,10 +44,9 @@ "@storybook/react-webpack5": "^8.6.15", "@storybook/theming": "^8.6.15", "@svgr/cli": "5.5.0", - "@testing-library/dom": "^8.11.1", + "@testing-library/dom": "^10.0.0", "@testing-library/jest-dom": "^5.16.1", - "@testing-library/react": "15.0.6", - "@testing-library/react-hooks": "^7.0.2", + "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.5.2", "@types/classnames": "2.2.10", "@types/invariant": "2.2.29", @@ -84,7 +83,7 @@ "nx-cloud": "^19.1.0", "onchange": "^7.0.2", "prettier": "^2.8.7", - "react-test-renderer": "18.3.1", + "react-test-renderer": "^19.0.0", "storybook": "^8.6.15", "storybook-addon-deep-controls": "^0.9.5", "style-loader": "^4.0.0", @@ -114,7 +113,8 @@ "repository": "git@github.com:Codecademy/gamut.git", "resolutions": { "@typescript-eslint/utils": "^5.15.0", - "error-ex": "1.3.4" + "error-ex": "1.3.4", + "@types/react": "^19.2.9" }, "scripts": { "build": "nx run-many --target=build --all", diff --git a/yarn.lock b/yarn.lock index 0290c26753..26fbeed586 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5412,20 +5412,6 @@ lz-string "^1.5.0" pretty-format "^27.0.2" -"@testing-library/dom@^8.11.1": - version "8.20.1" - resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz#2e52a32e46fc88369eef7eef634ac2a192decd9f" - integrity sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/runtime" "^7.12.5" - "@types/aria-query" "^5.0.1" - aria-query "5.1.3" - chalk "^4.1.0" - dom-accessibility-api "^0.5.9" - lz-string "^1.5.0" - pretty-format "^27.0.2" - "@testing-library/jest-dom@^5.16.1": version "5.17.0" resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz#5e97c8f9a15ccf4656da00fecab505728de81e0c" @@ -5441,25 +5427,12 @@ lodash "^4.17.15" redent "^3.0.0" -"@testing-library/react-hooks@^7.0.2": - version "7.0.2" - resolved "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-7.0.2.tgz#3388d07f562d91e7f2431a4a21b5186062ecfee0" - integrity sha512-dYxpz8u9m4q1TuzfcUApqi8iFfR6R0FaMbr2hjZJy1uC8z+bO/K4v8Gs9eogGKYQop7QsrBTFkv/BCF7MzD2Cg== - dependencies: - "@babel/runtime" "^7.12.5" - "@types/react" ">=16.9.0" - "@types/react-dom" ">=16.9.0" - "@types/react-test-renderer" ">=16.9.0" - react-error-boundary "^3.1.0" - -"@testing-library/react@15.0.6": - version "15.0.6" - resolved "https://registry.npmjs.org/@testing-library/react/-/react-15.0.6.tgz#76be2e9e6da98c044823dfbc9d62ad3f10a3a401" - integrity sha512-UlbazRtEpQClFOiYp+1BapMT+xyqWMnE+hh9tn5DQ6gmlE7AIZWcGpzZukmDZuFk3By01oiqOf8lRedLS4k6xQ== +"@testing-library/react@^16.3.2": + version "16.3.2" + resolved "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz#672883b7acb8e775fc0492d9e9d25e06e89786d0" + integrity sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g== dependencies: "@babel/runtime" "^7.12.5" - "@testing-library/dom" "^10.0.0" - "@types/react-dom" "^18.0.0" "@testing-library/user-event@^14.5.2": version "14.5.2" @@ -5823,11 +5796,6 @@ resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== -"@types/prop-types@*": - version "15.7.13" - resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz#2af91918ee12d9d32914feb13f5326658461b451" - integrity sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA== - "@types/q@^1.5.1": version "1.5.8" resolved "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz#95f6c6a08f2ad868ba230ead1d2d7f7be3db3837" @@ -5843,25 +5811,11 @@ resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/react-dom@>=16.9.0", "@types/react-dom@^18.0.0": - version "18.3.1" - resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz#1e4654c08a9cdcfb6594c780ac59b55aad42fe07" - integrity sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ== - dependencies: - "@types/react" "*" - "@types/react-dom@^19.2.3": version "19.2.3" resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c" integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ== -"@types/react-test-renderer@>=16.9.0": - version "18.3.0" - resolved "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.3.0.tgz#839502eae70058a4ae161f63385a8e7929cef4c0" - integrity sha512-HW4MuEYxfDbOHQsVlY/XtOvNHftCVEPhJF2pQXXwcUiUF+Oyb0usgp48HSgpK5rt8m9KZb22yqOeZm+rrVG8gw== - dependencies: - "@types/react" "*" - "@types/react-test-renderer@^19.1.0": version "19.1.0" resolved "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-19.1.0.tgz#1d0af8f2e1b5931e245b8b5b234d1502b854dc10" @@ -5876,15 +5830,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@>=16.9.0": - version "18.3.12" - resolved "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz#99419f182ccd69151813b7ee24b792fe08774f60" - integrity sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw== - dependencies: - "@types/prop-types" "*" - csstype "^3.0.2" - -"@types/react@^19.2.9": +"@types/react@*", "@types/react@^19.2.9": version "19.2.9" resolved "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz#84ec7669742bb3e7e2e8d6a5258d95ead7764200" integrity sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA== @@ -6608,13 +6554,6 @@ aria-hidden@^1.2.5: dependencies: tslib "^2.0.0" -aria-query@5.1.3: - version "5.1.3" - resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" - integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== - dependencies: - deep-equal "^2.0.5" - aria-query@5.3.0: version "5.3.0" resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" @@ -6627,7 +6566,7 @@ aria-query@^5.0.0, aria-query@^5.3.2: resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== -array-buffer-byte-length@^1.0.0, array-buffer-byte-length@^1.0.1: +array-buffer-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== @@ -8616,30 +8555,6 @@ dedent@^1.0.0, dedent@^1.6.0: resolved "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz#c1f9445335f0175a96587be245a282ff451446ca" integrity sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ== -deep-equal@^2.0.5: - version "2.2.3" - resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" - integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== - dependencies: - array-buffer-byte-length "^1.0.0" - call-bind "^1.0.5" - es-get-iterator "^1.1.3" - get-intrinsic "^1.2.2" - is-arguments "^1.1.1" - is-array-buffer "^3.0.2" - is-date-object "^1.0.5" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - isarray "^2.0.5" - object-is "^1.1.5" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.5.1" - side-channel "^1.0.4" - which-boxed-primitive "^1.0.2" - which-collection "^1.0.1" - which-typed-array "^1.1.13" - deep-equal@~1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" @@ -9189,21 +9104,6 @@ es-errors@^1.2.1, es-errors@^1.3.0: resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-get-iterator@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" - integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" - has-symbols "^1.0.3" - is-arguments "^1.1.1" - is-map "^2.0.2" - is-set "^2.0.2" - is-string "^1.0.7" - isarray "^2.0.5" - stop-iteration-iterator "^1.0.0" - es-iterator-helpers@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.1.0.tgz#f6d745d342aea214fe09497e7152170dc333a7a6" @@ -10300,7 +10200,7 @@ get-caller-file@^2.0.5: resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4, get-intrinsic@^1.2.6: +get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4, get-intrinsic@^1.2.6: version "1.3.0" resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== @@ -11195,7 +11095,7 @@ inquirer@^8.2.4: through "^2.3.6" wrap-ansi "^6.0.1" -internal-slot@^1.0.4, internal-slot@^1.0.7: +internal-slot@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== @@ -11257,7 +11157,7 @@ is-alphanumerical@^1.0.0: is-alphabetical "^1.0.0" is-decimal "^1.0.0" -is-arguments@^1.0.4, is-arguments@^1.1.1: +is-arguments@^1.0.4: version "1.1.1" resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== @@ -11265,7 +11165,7 @@ is-arguments@^1.0.4, is-arguments@^1.1.1: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-array-buffer@^3.0.2, is-array-buffer@^3.0.4: +is-array-buffer@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== @@ -11423,7 +11323,7 @@ is-lambda@^1.0.1: resolved "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== -is-map@^2.0.2, is-map@^2.0.3: +is-map@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== @@ -11517,7 +11417,7 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-set@^2.0.2, is-set@^2.0.3: +is-set@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== @@ -13085,7 +12985,7 @@ longest-streak@^2.0.1: resolved "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4" integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg== -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -14167,14 +14067,6 @@ object-inspect@^1.13.1: resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== -object-is@^1.1.5: - version "1.1.6" - resolved "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" - integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - object-keys@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -15619,13 +15511,6 @@ react-docgen@^7.0.0: dependencies: scheduler "^0.27.0" -react-error-boundary@^3.1.0: - version "3.1.4" - resolved "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz#255db92b23197108757a888b01e5b729919abde0" - integrity sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA== - dependencies: - "@babel/runtime" "^7.12.5" - react-fast-compare@^3.0.1, react-fast-compare@^3.2.2: version "3.2.2" resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" @@ -15669,11 +15554,6 @@ react-hook-form@^7.65.0: resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.66.0.tgz#1a09ea9d0ebb3bdda5073b08a486538d37d9c0d4" integrity sha512-xXBqsWGKrY46ZqaHDo+ZUYiMUgi8suYu5kdrS20EG8KiL7VRQitEbNjm+UcrDYrNi1YLyfpmAeGjCZYXLT9YBw== -"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.3.1: - version "18.3.1" - resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" - integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== - react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -15684,6 +15564,16 @@ react-is@^17.0.1: resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-is@^18.0.0, react-is@^18.3.1: + version "18.3.1" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + +react-is@^19.2.3: + version "19.2.3" + resolved "https://registry.npmjs.org/react-is/-/react-is-19.2.3.tgz#eec2feb69c7fb31f77d0b5c08c10ae1c88886b29" + integrity sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA== + react-player@^2.16.0: version "2.16.0" resolved "https://registry.npmjs.org/react-player/-/react-player-2.16.0.tgz#89070700b03f5a5ded9f0b3165d4717390796481" @@ -15729,14 +15619,6 @@ react-select@^5.2.2: react-transition-group "^4.3.0" use-isomorphic-layout-effect "^1.1.2" -react-shallow-renderer@^16.15.0: - version "16.15.0" - resolved "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457" - integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA== - dependencies: - object-assign "^4.1.1" - react-is "^16.12.0 || ^17.0.0 || ^18.0.0" - react-stately@^3.37.0: version "3.37.0" resolved "https://registry.npmjs.org/react-stately/-/react-stately-3.37.0.tgz#9bd09ecd1c7b11461ec60e17a7c670c17a64962e" @@ -15777,14 +15659,13 @@ react-style-singleton@^2.2.2, react-style-singleton@^2.2.3: get-nonce "^1.0.0" tslib "^2.0.0" -react-test-renderer@18.3.1: - version "18.3.1" - resolved "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.3.1.tgz#e693608a1f96283400d4a3afead6893f958b80b4" - integrity sha512-KkAgygexHUkQqtvvx/otwxtuFu5cVjfzTCtjXLH9boS19/Nbtg84zS7wIQn39G8IlrhThBpQsMKkq5ZHZIYFXA== +react-test-renderer@^19.0.0: + version "19.2.3" + resolved "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-19.2.3.tgz#d20f5193867c98b2df9e13b4e72bb83f311f6a43" + integrity sha512-TMR1LnSFiWZMJkCgNf5ATSvAheTT2NvKIwiVwdBPHxjBI7n/JbWd4gaZ16DVd9foAXdvDz+sB5yxZTwMjPRxpw== dependencies: - react-is "^18.3.1" - react-shallow-renderer "^16.15.0" - scheduler "^0.23.2" + react-is "^19.2.3" + scheduler "^0.27.0" react-transition-group@^4.3.0: version "4.4.5" @@ -16036,7 +15917,7 @@ regenerator-transform@^0.15.2: dependencies: "@babel/runtime" "^7.8.4" -regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2: +regexp.prototype.flags@^1.5.2: version "1.5.3" resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz#b3ae40b1d2499b8350ab2c3fe6ef3845d3a96f42" integrity sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ== @@ -16603,13 +16484,6 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" -scheduler@^0.23.2: - version "0.23.2" - resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" - integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== - dependencies: - loose-envify "^1.1.0" - scheduler@^0.27.0: version "0.27.0" resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd" @@ -17131,13 +17005,6 @@ statuses@^2.0.1: resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== -stop-iteration-iterator@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" - integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== - dependencies: - internal-slot "^1.0.4" - storybook-addon-deep-controls@^0.9.5: version "0.9.5" resolved "https://registry.npmjs.org/storybook-addon-deep-controls/-/storybook-addon-deep-controls-0.9.5.tgz#4ccfac81f6e2a37861957a845659072db319c895" @@ -18657,7 +18524,7 @@ which-builtin-type@^1.1.3: which-collection "^1.0.2" which-typed-array "^1.1.15" -which-collection@^1.0.1, which-collection@^1.0.2: +which-collection@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== @@ -18667,7 +18534,7 @@ which-collection@^1.0.1, which-collection@^1.0.2: is-weakmap "^2.0.2" is-weakset "^2.0.3" -which-typed-array@^1.1.13, which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.2: +which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.2: version "1.1.15" resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== From c63f3f99d176c9482a4d88c6673c5c027e9a7a0b Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Thu, 22 Jan 2026 15:20:50 -0500 Subject: [PATCH 08/18] misc react 19 type fixes --- packages/gamut-styles/src/variance/utils.ts | 5 +++-- packages/gamut/src/Button/shared/types.ts | 2 +- packages/gamut/src/Coachmark/index.tsx | 2 +- packages/gamut/src/ConnectedForm/ConnectedForm.tsx | 9 +++++---- .../src/Form/SelectDropdown/SelectDropdown.tsx | 9 +++++++-- packages/gamut/src/Popover/types.tsx | 9 +++++---- packages/gamut/src/PopoverContainer/hooks.ts | 14 ++++++++------ packages/gamut/src/Tip/__tests__/helpers.tsx | 2 +- packages/gamut/src/Tip/shared/FloatingTip.tsx | 4 ++-- packages/gamut/src/Tip/shared/types.tsx | 4 ++-- packages/gamut/src/utils/react.ts | 6 +++++- 11 files changed, 40 insertions(+), 26 deletions(-) diff --git a/packages/gamut-styles/src/variance/utils.ts b/packages/gamut-styles/src/variance/utils.ts index d4d9a583d6..0d88d4d0e1 100644 --- a/packages/gamut-styles/src/variance/utils.ts +++ b/packages/gamut-styles/src/variance/utils.ts @@ -1,5 +1,6 @@ import { ThemeProps } from '@codecademy/variance'; import isPropValid from '@emotion/is-prop-valid'; +import type { JSX } from 'react'; import { all as allProps } from './config'; @@ -20,8 +21,8 @@ export type SystemPropNames = (typeof allPropnames)[number]; export type ElementOrProps = keyof JSX.IntrinsicElements | ThemeProps; export type ForwardableProps = Exclude< El extends keyof JSX.IntrinsicElements - ? keyof JSX.IntrinsicElements[El] - : keyof Element, + ? keyof JSX.IntrinsicElements[El] + : keyof Element, Additional | SystemPropNames >; diff --git a/packages/gamut/src/Button/shared/types.ts b/packages/gamut/src/Button/shared/types.ts index 02c84d4e27..4d770db8c3 100644 --- a/packages/gamut/src/Button/shared/types.ts +++ b/packages/gamut/src/Button/shared/types.ts @@ -1,6 +1,6 @@ import { ColorModes } from '@codecademy/gamut-styles'; import { StyleProps } from '@codecademy/variance'; -import { ComponentProps, HTMLProps } from 'react'; +import type { ComponentProps, HTMLProps, JSX } from 'react'; import { ButtonBase } from '../../ButtonBase'; import { IconComponentType } from '../../utils'; diff --git a/packages/gamut/src/Coachmark/index.tsx b/packages/gamut/src/Coachmark/index.tsx index a4316f6fde..bdc8bc1773 100644 --- a/packages/gamut/src/Coachmark/index.tsx +++ b/packages/gamut/src/Coachmark/index.tsx @@ -1,4 +1,4 @@ -import { useRef } from 'react'; +import { type JSX, useRef } from 'react'; import * as React from 'react'; import { DelayedRenderWrapper } from '../DelayedRenderWrapper'; diff --git a/packages/gamut/src/ConnectedForm/ConnectedForm.tsx b/packages/gamut/src/ConnectedForm/ConnectedForm.tsx index 95575145d4..a6b8a190e3 100644 --- a/packages/gamut/src/ConnectedForm/ConnectedForm.tsx +++ b/packages/gamut/src/ConnectedForm/ConnectedForm.tsx @@ -181,7 +181,8 @@ export const ConnectedForm = forwardRef( ); } -) as >( - props: ConnectedFormProps, - ref: React.ForwardedRef -) => React.ReactElement; +) as { + >( + props: ConnectedFormProps & React.RefAttributes + ): React.ReactNode; +}; diff --git a/packages/gamut/src/Form/SelectDropdown/SelectDropdown.tsx b/packages/gamut/src/Form/SelectDropdown/SelectDropdown.tsx index f31839eaa4..50af0d4e4c 100644 --- a/packages/gamut/src/Form/SelectDropdown/SelectDropdown.tsx +++ b/packages/gamut/src/Form/SelectDropdown/SelectDropdown.tsx @@ -30,6 +30,7 @@ import { } from './elements'; import { getMemoizedStyles } from './styles'; import { + ExtendedOption, OptionStrict, SelectDropdownGroup, SelectDropdownProps, @@ -273,7 +274,7 @@ export const SelectDropdown: React.FC = ({ inputWidth={inputWidth} isDisabled={disabled} isMulti={multiple} - isOptionDisabled={(option) => option.disabled} + isOptionDisabled={(option: ExtendedOption) => !!option.disabled} isSearchable={isSearchable} menuAlignment={menuAlignment} name={name} @@ -285,7 +286,11 @@ export const SelectDropdown: React.FC = ({ styles={memoizedStyles} value={multiple ? multiValues : parsedValue} onChange={changeHandler} - onKeyDown={multiple ? (e) => keyPressHandler(e) : undefined} + onKeyDown={ + multiple + ? (e: KeyboardEvent) => keyPressHandler(e) + : undefined + } {...rest} /> diff --git a/packages/gamut/src/Popover/types.tsx b/packages/gamut/src/Popover/types.tsx index b85d4d4595..1a605f4309 100755 --- a/packages/gamut/src/Popover/types.tsx +++ b/packages/gamut/src/Popover/types.tsx @@ -98,15 +98,16 @@ export type PopoverProps = PopoverBaseProps & /** * The target element around which the popover will be positioned. */ - targetRef: React.RefObject< - Pick - >; + targetRef: React.RefObject | null>; /** * The PopoverContainer which contents will be rendered into. */ popoverContainerRef?: - | React.RefObject + | React.RefObject | React.RefCallback; /** diff --git a/packages/gamut/src/PopoverContainer/hooks.ts b/packages/gamut/src/PopoverContainer/hooks.ts index 295591da7c..8ddd9c13d3 100644 --- a/packages/gamut/src/PopoverContainer/hooks.ts +++ b/packages/gamut/src/PopoverContainer/hooks.ts @@ -3,9 +3,10 @@ import { useEffect, useMemo } from 'react'; import { findAllAdditionalScrollingParents, findResizingParent } from './utils'; export const useScrollingParentsEffect = ( - targetRef: React.RefObject< - Pick - >, + targetRef: React.RefObject | null>, setTargetRect: (rect: DOMRect | undefined) => void ) => { useEffect(() => { @@ -39,9 +40,10 @@ export const useScrollingParentsEffect = ( }; export const useResizingParentEffect = ( - targetRef: React.RefObject< - Pick - >, + targetRef: React.RefObject | null>, setTargetRect: (rect: DOMRect | undefined) => void ) => { useEffect(() => { diff --git a/packages/gamut/src/Tip/__tests__/helpers.tsx b/packages/gamut/src/Tip/__tests__/helpers.tsx index 4eaa8ef41a..1697aafb45 100644 --- a/packages/gamut/src/Tip/__tests__/helpers.tsx +++ b/packages/gamut/src/Tip/__tests__/helpers.tsx @@ -19,7 +19,7 @@ type LinkTextParam = { linkText: string }; type InfoParam = { info: string }; type PlacementParam = { placement: Placement }; -export const createFocusOnClick = (ref: RefObject) => { +export const createFocusOnClick = (ref: RefObject) => { return ({ isTipHidden }: { isTipHidden: boolean }) => { if (!isTipHidden) ref.current?.focus(); }; diff --git a/packages/gamut/src/Tip/shared/FloatingTip.tsx b/packages/gamut/src/Tip/shared/FloatingTip.tsx index c251d1570c..fb29a0e1b0 100644 --- a/packages/gamut/src/Tip/shared/FloatingTip.tsx +++ b/packages/gamut/src/Tip/shared/FloatingTip.tsx @@ -43,8 +43,8 @@ export const FloatingTip: React.FC = ({ const [isFocused, setIsFocused] = useState(false); // Use refs to store timeouts to prevent race conditions - const hoverDelayRef = useRef(); - const focusDelayRef = useRef(); + const hoverDelayRef = useRef(undefined); + const focusDelayRef = useRef(undefined); const commonPopoverProps = getPopoverAlignmentAndPattern({ alignment, type }); const dims = getAlignmentStyles({ avatar, alignment, type }); diff --git a/packages/gamut/src/Tip/shared/types.tsx b/packages/gamut/src/Tip/shared/types.tsx index a91c5e6a6f..3eea8a24ce 100644 --- a/packages/gamut/src/Tip/shared/types.tsx +++ b/packages/gamut/src/Tip/shared/types.tsx @@ -79,9 +79,9 @@ export type TipPlacementComponentProps = Omit< id?: string; isTipHidden?: boolean; popoverContentRef?: - | React.RefObject + | React.RefObject | ((node: HTMLDivElement | null) => void); type: 'info' | 'tool' | 'preview'; - wrapperRef?: React.RefObject; + wrapperRef?: React.RefObject; zIndex?: number; } & React.PropsWithChildren; diff --git a/packages/gamut/src/utils/react.ts b/packages/gamut/src/utils/react.ts index c0b673bb24..a3ebd4391c 100644 --- a/packages/gamut/src/utils/react.ts +++ b/packages/gamut/src/utils/react.ts @@ -35,7 +35,11 @@ export const extractTextContent = (children: React.ReactNode): string => { return ''; } if (isValidElement(child)) { - const textContent = child.props.children ?? child.props.text ?? ''; + const props = child.props as { + children?: React.ReactNode; + text?: string; + }; + const textContent = props.children ?? props.text ?? ''; return extractTextContent(textContent); } return ''; From c722e000e18300aff361df31fb91dacc5e1eb857 Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Thu, 22 Jan 2026 15:26:06 -0500 Subject: [PATCH 09/18] added JSX to gamut-tests index file --- packages/gamut-styles/src/variance/utils.ts | 4 ++-- packages/gamut-tests/src/index.tsx | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/gamut-styles/src/variance/utils.ts b/packages/gamut-styles/src/variance/utils.ts index 0d88d4d0e1..9a8a1db2a9 100644 --- a/packages/gamut-styles/src/variance/utils.ts +++ b/packages/gamut-styles/src/variance/utils.ts @@ -21,8 +21,8 @@ export type SystemPropNames = (typeof allPropnames)[number]; export type ElementOrProps = keyof JSX.IntrinsicElements | ThemeProps; export type ForwardableProps = Exclude< El extends keyof JSX.IntrinsicElements - ? keyof JSX.IntrinsicElements[El] - : keyof Element, + ? keyof JSX.IntrinsicElements[El] + : keyof Element, Additional | SystemPropNames >; diff --git a/packages/gamut-tests/src/index.tsx b/packages/gamut-tests/src/index.tsx index 7200578954..c290a4af95 100644 --- a/packages/gamut-tests/src/index.tsx +++ b/packages/gamut-tests/src/index.tsx @@ -4,6 +4,7 @@ import { setupRtl as setupRtlBase, } from 'component-test-setup'; import overArgs from 'lodash/overArgs'; +import type { JSX } from 'react'; import * as React from 'react'; // See https://www.notion.so/codecademy/Frontend-Unit-Tests-1cbf4e078a6647559b4583dfb6d3cb18 for more info From 11fa4b259ec5c97ccc7b35077f06b8904e700210 Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Thu, 22 Jan 2026 15:51:48 -0500 Subject: [PATCH 10/18] fix snapshot tests and update snapshots --- jest.config.ts | 8 +- .../src/__tests__/AssetProvider.test.tsx | 73 ++++++++++------- .../src/__tests__/fontLoading.test.tsx | 30 +++++-- packages/gamut-styles/src/variance/utils.ts | 4 +- .../__snapshots__/utils.test.tsx.snap | 80 ++++++++++++------- .../integration/__tests__/component.test.tsx | 16 ++-- 6 files changed, 131 insertions(+), 80 deletions(-) diff --git a/jest.config.ts b/jest.config.ts index d0dbd1b889..6b3f2d6e24 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -1,5 +1,5 @@ -import { getJestProjects } from '@nx/jest'; +import { getJestProjectsAsync } from '@nx/jest'; -export default { - projects: getJestProjects(), -}; +export default async () => ({ + projects: await getJestProjectsAsync(), +}); diff --git a/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx b/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx index 8588c23304..a5eabfe4ef 100644 --- a/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx +++ b/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx @@ -1,18 +1,18 @@ import '@testing-library/jest-dom'; -import { setupRtl } from '@codecademy/gamut-tests'; import { render } from '@testing-library/react'; import { AssetProvider, createFontLinks } from '../AssetProvider'; import { coreTheme, percipioTheme } from '../themes'; - -const renderView = setupRtl(AssetProvider, {}); +import { getFonts } from '../utils/fontUtils'; jest.mock('../utils/fontUtils', () => ({ + __esModule: true, getFonts: jest.fn(), })); jest.mock('../remoteAssets/fonts', () => ({ + __esModule: true, webFonts: { core: [ { @@ -43,11 +43,22 @@ jest.mock('../remoteAssets/fonts', () => ({ }, })); -const mockGetFonts = require('../utils/fontUtils').getFonts; +const mockGetFonts = getFonts as jest.MockedFunction; describe('AssetProvider', () => { + // Helper to get links from either container or document.head (React 19 hoists link elements) + const getPreloadLinks = (container: HTMLElement) => { + const containerLinks = container.querySelectorAll('link[rel="preload"]'); + if (containerLinks.length > 0) return containerLinks; + return document.head.querySelectorAll('link[rel="preload"][as="font"]'); + }; + beforeEach(() => { jest.clearAllMocks(); + // Clean up any links from previous tests (React 19 hoists link elements to head) + document.head + .querySelectorAll('link[rel="preload"][as="font"]') + .forEach((el) => el.remove()); }); describe('createFontLinks', () => { @@ -66,7 +77,7 @@ describe('AssetProvider', () => { ]; const { container } = render(<>{createFontLinks(fonts)}); - const links = container.querySelectorAll('link[rel="preload"]'); + const links = getPreloadLinks(container); expect(links).toHaveLength(1); expect(links[0]).toHaveAttribute( @@ -80,13 +91,13 @@ describe('AssetProvider', () => { it('should handle empty fonts array', () => { const { container } = render(<>{createFontLinks([])}); - const links = container.querySelectorAll('link[rel="preload"]'); + const links = getPreloadLinks(container); expect(links).toHaveLength(0); }); it('should handle undefined fonts parameter', () => { const { container } = render(<>{createFontLinks(undefined)}); - const links = container.querySelectorAll('link[rel="preload"]'); + const links = getPreloadLinks(container); expect(links).toHaveLength(2); }); @@ -110,7 +121,7 @@ describe('AssetProvider', () => { ]; const { container } = render(<>{createFontLinks(fonts)}); - const links = container.querySelectorAll('link[rel="preload"]'); + const links = getPreloadLinks(container); expect(links).toHaveLength(1); expect(links[0]).toHaveAttribute( 'href', @@ -139,7 +150,7 @@ describe('AssetProvider', () => { ]; const { container } = render(<>{createFontLinks(fonts)}); - const links = container.querySelectorAll('link[rel="preload"]'); + const links = getPreloadLinks(container); expect(links).toHaveLength(2); }); }); @@ -154,8 +165,8 @@ describe('AssetProvider', () => { }, ]); - const { view } = renderView(); - const links = view.container.querySelectorAll('link[rel="preload"]'); + const { container } = render(); + const links = getPreloadLinks(container); expect(links).toHaveLength(1); expect(links[0]).toHaveAttribute( @@ -174,8 +185,10 @@ describe('AssetProvider', () => { }, ]); - const { view } = renderView({ theme: percipioTheme as any }); - const links = view.container.querySelectorAll('link[rel="preload"]'); + const { container } = render( + + ); + const links = getPreloadLinks(container); expect(links).toHaveLength(1); expect(links[0]).toHaveAttribute( @@ -189,7 +202,7 @@ describe('AssetProvider', () => { const themeWithoutName = { ...coreTheme, name: undefined }; mockGetFonts.mockReturnValue([]); - renderView({ theme: themeWithoutName }); + render(); expect(mockGetFonts).toHaveBeenCalledWith('core'); }); @@ -197,7 +210,7 @@ describe('AssetProvider', () => { const themeWithInvalidName = { ...coreTheme, name: 'invalid-theme' }; mockGetFonts.mockReturnValue([]); - renderView({ theme: themeWithInvalidName }); + render(); expect(mockGetFonts).toHaveBeenCalledWith('invalid-theme'); }); @@ -206,32 +219,32 @@ describe('AssetProvider', () => { throw new Error('Font loading failed'); }); - const { view } = renderView(); - const links = view.container.querySelectorAll('link[rel="preload"]'); + const { container } = render(); + const links = getPreloadLinks(container); expect(links).toHaveLength(0); }); it('should fallback to core fonts when getFonts returns undefined', () => { mockGetFonts.mockReturnValue(undefined); - const { view } = renderView(); - const links = view.container.querySelectorAll('link[rel="preload"]'); + const { container } = render(); + const links = getPreloadLinks(container); expect(links).toHaveLength(2); }); it('should fallback to core fonts when getFonts returns null', () => { mockGetFonts.mockReturnValue(null); - const { view } = renderView(); - const links = view.container.querySelectorAll('link[rel="preload"]'); + const { container } = render(); + const links = getPreloadLinks(container); expect(links).toHaveLength(2); }); it('should fallback to core fonts when getFonts returns non-array', () => { mockGetFonts.mockReturnValue('not-an-array'); - const { view } = renderView(); - const links = view.container.querySelectorAll('link[rel="preload"]'); + const { container } = render(); + const links = getPreloadLinks(container); expect(links).toHaveLength(0); }); @@ -254,8 +267,8 @@ describe('AssetProvider', () => { }, ]); - const { view } = renderView(); - const links = view.container.querySelectorAll('link[rel="preload"]'); + const { container } = render(); + const links = getPreloadLinks(container); expect(links).toHaveLength(3); expect(links[0]).toHaveAttribute( @@ -272,7 +285,7 @@ describe('AssetProvider', () => { ); }); - it('should only rnder valid font configurations ', () => { + it('should only render valid font configurations', () => { mockGetFonts.mockReturnValue([ { filePath: 'https://www.codecademy.com/gamut/valid-font', @@ -291,8 +304,8 @@ describe('AssetProvider', () => { } as any, ]); - const { view } = renderView(); - const links = view.container.querySelectorAll('link[rel="preload"]'); + const { container } = render(); + const links = getPreloadLinks(container); expect(links).toHaveLength(1); expect(links[0]).toHaveAttribute( @@ -320,8 +333,8 @@ describe('AssetProvider', () => { }, ]); - const { view } = renderView(); - const links = view.container.querySelectorAll('link[rel="preload"]'); + const { container } = render(); + const links = getPreloadLinks(container); expect(links).toHaveLength(1); expect(links[0]).toHaveAttribute( diff --git a/packages/gamut-styles/src/__tests__/fontLoading.test.tsx b/packages/gamut-styles/src/__tests__/fontLoading.test.tsx index 6a46a50c3a..ccdc03b83b 100644 --- a/packages/gamut-styles/src/__tests__/fontLoading.test.tsx +++ b/packages/gamut-styles/src/__tests__/fontLoading.test.tsx @@ -2,15 +2,18 @@ import { render } from '@testing-library/react'; import { AssetProvider } from '../AssetProvider'; import { coreTheme, percipioTheme } from '../themes'; +import { getFonts } from '../utils/fontUtils'; // Type assertion to satisfy Theme interface in GamutProvider from theme.d.ts - this lib is typed to the CoreTheme interface const typedPercipioTheme = percipioTheme as any; jest.mock('../utils/fontUtils', () => ({ + __esModule: true, getFonts: jest.fn(), })); jest.mock('../remoteAssets/fonts', () => ({ + __esModule: true, webFonts: { core: [ { @@ -29,7 +32,7 @@ jest.mock('../remoteAssets/fonts', () => ({ }, })); -const mockGetFonts = require('../utils/fontUtils').getFonts; +const mockGetFonts = getFonts as jest.MockedFunction; const mockDocumentFonts = { load: jest.fn(), @@ -48,11 +51,22 @@ const mockFetch = jest.fn(); global.fetch = mockFetch; describe('Font Loading and Error Handling', () => { + // Helper to get links from either container or document.head (React 19 hoists link elements) + const getPreloadLinks = (container: HTMLElement) => { + const containerLinks = container.querySelectorAll('link[rel="preload"]'); + if (containerLinks.length > 0) return containerLinks; + return document.head.querySelectorAll('link[rel="preload"][as="font"]'); + }; + beforeEach(() => { jest.clearAllMocks(); mockDocumentFonts.load.mockClear(); mockDocumentFonts.check.mockClear(); mockFetch.mockClear(); + // Clean up any links from previous tests (React 19 hoists link elements to head) + document.head + .querySelectorAll('link[rel="preload"][as="font"]') + .forEach((el) => el.remove()); }); describe('Font Preloading', () => { @@ -67,7 +81,7 @@ describe('Font Loading and Error Handling', () => { const { container } = render(); - const links = container.querySelectorAll('link[rel="preload"]'); + const links = getPreloadLinks(container); expect(links).toHaveLength(1); expect(links[0]).toHaveAttribute( 'href', @@ -93,7 +107,7 @@ describe('Font Loading and Error Handling', () => { const { container } = render(); - const links = container.querySelectorAll('link[rel="preload"]'); + const links = getPreloadLinks(container); expect(links).toHaveLength(2); expect(links[0]).toHaveAttribute( 'href', @@ -115,7 +129,7 @@ describe('Font Loading and Error Handling', () => { const { container } = render(); // Should not render any links when getFonts fails - const links = container.querySelectorAll('link[rel="preload"]'); + const links = getPreloadLinks(container); expect(links).toHaveLength(0); }); @@ -141,7 +155,7 @@ describe('Font Loading and Error Handling', () => { const { container } = render(); - const links = container.querySelectorAll('link[rel="preload"]'); + const links = getPreloadLinks(container); expect(links).toHaveLength(2); }); }); @@ -164,7 +178,7 @@ describe('Font Loading and Error Handling', () => { const { container } = render(); // Should render preload links for all fonts - const links = container.querySelectorAll('link[rel="preload"]'); + const links = getPreloadLinks(container); expect(links).toHaveLength(2); expect(links[0]).toHaveAttribute( 'href', @@ -195,7 +209,7 @@ describe('Font Loading and Error Handling', () => { const { container } = render(); - const links = container.querySelectorAll('link[rel="preload"]'); + const links = getPreloadLinks(container); expect(links).toHaveLength(1); Object.defineProperty(document, 'fonts', { @@ -218,7 +232,7 @@ describe('Font Loading and Error Handling', () => { const { container } = render(); - const links = container.querySelectorAll('link[rel="preload"]'); + const links = getPreloadLinks(container); expect(links).toHaveLength(1); global.fetch = originalFetch; diff --git a/packages/gamut-styles/src/variance/utils.ts b/packages/gamut-styles/src/variance/utils.ts index 9a8a1db2a9..0d88d4d0e1 100644 --- a/packages/gamut-styles/src/variance/utils.ts +++ b/packages/gamut-styles/src/variance/utils.ts @@ -21,8 +21,8 @@ export type SystemPropNames = (typeof allPropnames)[number]; export type ElementOrProps = keyof JSX.IntrinsicElements | ThemeProps; export type ForwardableProps = Exclude< El extends keyof JSX.IntrinsicElements - ? keyof JSX.IntrinsicElements[El] - : keyof Element, + ? keyof JSX.IntrinsicElements[El] + : keyof Element, Additional | SystemPropNames >; diff --git a/packages/gamut/src/Form/__tests__/__snapshots__/utils.test.tsx.snap b/packages/gamut/src/Form/__tests__/__snapshots__/utils.test.tsx.snap index 2690797df5..6db6a89db0 100644 --- a/packages/gamut/src/Form/__tests__/__snapshots__/utils.test.tsx.snap +++ b/packages/gamut/src/Form/__tests__/__snapshots__/utils.test.tsx.snap @@ -2,39 +2,63 @@ exports[`parseSelectOptions creates an option list 1`] = ` [ - , - , + { + "$$typeof": Symbol(react.transitional.element), + "_owner": null, + "_store": {}, + "key": "test-val", + "props": { + "children": "Value", + "data-testid": "test-val", + "label": "Value", + "value": "val", + }, + "type": "option", + }, + { + "$$typeof": Symbol(react.transitional.element), + "_owner": null, + "_store": {}, + "key": "test-val2", + "props": { + "children": "Value 2", + "data-testid": "test-val2", + "label": "Value 2", + "value": "val2", + }, + "type": "option", + }, ] `; exports[`parseSelectOptions creates an option list 2`] = ` [ - , - , + { + "$$typeof": Symbol(react.transitional.element), + "_owner": null, + "_store": {}, + "key": "test-val", + "props": { + "children": "val", + "data-testid": "test-val", + "label": "val", + "value": "val", + }, + "type": "option", + }, + { + "$$typeof": Symbol(react.transitional.element), + "_owner": null, + "_store": {}, + "key": "test-val2", + "props": { + "children": "val2", + "data-testid": "test-val2", + "label": "val2", + "value": "val2", + }, + "type": "option", + }, ] `; diff --git a/packages/variance/integration/__tests__/component.test.tsx b/packages/variance/integration/__tests__/component.test.tsx index bdc494b5fc..ed1ec6c47b 100644 --- a/packages/variance/integration/__tests__/component.test.tsx +++ b/packages/variance/integration/__tests__/component.test.tsx @@ -1,9 +1,9 @@ import { matchers } from '@emotion/jest'; import { ThemeProvider } from '@emotion/react'; import styled from '@emotion/styled'; +import { render as rtlRender } from '@testing-library/react'; import { ComponentProps } from 'react'; import * as React from 'react'; -import renderer from 'react-test-renderer'; import { variance } from '../../src/core'; import { theme } from '../__fixtures__/theme'; @@ -30,13 +30,13 @@ const setupRender = >( return (props?: P) => { const mergedProps = { ...defaultProps, ...props }; - return renderer - .create( - - - - ) - .toJSON(); + const { container } = rtlRender( + + + + ); + + return container.firstChild; }; }; From d3a90cd71c06d7c5e4a6c057dbfbc7b57210bd21 Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Thu, 22 Jan 2026 16:03:41 -0500 Subject: [PATCH 11/18] cleaned up tests for assetprovider --- .../src/__tests__/AssetProvider.test.tsx | 60 +++++++++---------- .../src/__tests__/fontLoading.test.tsx | 2 - 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx b/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx index a5eabfe4ef..fb7c39d460 100644 --- a/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx +++ b/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx @@ -1,18 +1,19 @@ import '@testing-library/jest-dom'; +import { setupRtl } from '@codecademy/gamut-tests'; import { render } from '@testing-library/react'; import { AssetProvider, createFontLinks } from '../AssetProvider'; import { coreTheme, percipioTheme } from '../themes'; import { getFonts } from '../utils/fontUtils'; +const renderView = setupRtl(AssetProvider, {}); + jest.mock('../utils/fontUtils', () => ({ - __esModule: true, getFonts: jest.fn(), })); jest.mock('../remoteAssets/fonts', () => ({ - __esModule: true, webFonts: { core: [ { @@ -46,7 +47,7 @@ jest.mock('../remoteAssets/fonts', () => ({ const mockGetFonts = getFonts as jest.MockedFunction; describe('AssetProvider', () => { - // Helper to get links from either container or document.head (React 19 hoists link elements) + // Helper to get links from either container or document.head since React 19 hoists link elements const getPreloadLinks = (container: HTMLElement) => { const containerLinks = container.querySelectorAll('link[rel="preload"]'); if (containerLinks.length > 0) return containerLinks; @@ -55,7 +56,6 @@ describe('AssetProvider', () => { beforeEach(() => { jest.clearAllMocks(); - // Clean up any links from previous tests (React 19 hoists link elements to head) document.head .querySelectorAll('link[rel="preload"][as="font"]') .forEach((el) => el.remove()); @@ -165,8 +165,8 @@ describe('AssetProvider', () => { }, ]); - const { container } = render(); - const links = getPreloadLinks(container); + const { view } = renderView(); + const links = getPreloadLinks(view.container); expect(links).toHaveLength(1); expect(links[0]).toHaveAttribute( @@ -185,10 +185,8 @@ describe('AssetProvider', () => { }, ]); - const { container } = render( - - ); - const links = getPreloadLinks(container); + const { view } = renderView({ theme: percipioTheme as any }); + const links = getPreloadLinks(view.container); expect(links).toHaveLength(1); expect(links[0]).toHaveAttribute( @@ -199,10 +197,10 @@ describe('AssetProvider', () => { }); it('should handle theme without name property', () => { - const themeWithoutName = { ...coreTheme, name: undefined }; + const themeWithoutName = { ...coreTheme, name: undefined } as any; mockGetFonts.mockReturnValue([]); - render(); + renderView({ theme: themeWithoutName }); expect(mockGetFonts).toHaveBeenCalledWith('core'); }); @@ -210,7 +208,7 @@ describe('AssetProvider', () => { const themeWithInvalidName = { ...coreTheme, name: 'invalid-theme' }; mockGetFonts.mockReturnValue([]); - render(); + renderView({ theme: themeWithInvalidName }); expect(mockGetFonts).toHaveBeenCalledWith('invalid-theme'); }); @@ -219,32 +217,32 @@ describe('AssetProvider', () => { throw new Error('Font loading failed'); }); - const { container } = render(); - const links = getPreloadLinks(container); + const { view } = renderView(); + const links = getPreloadLinks(view.container); expect(links).toHaveLength(0); }); it('should fallback to core fonts when getFonts returns undefined', () => { - mockGetFonts.mockReturnValue(undefined); + mockGetFonts.mockReturnValue(undefined as any); - const { container } = render(); - const links = getPreloadLinks(container); + const { view } = renderView(); + const links = getPreloadLinks(view.container); expect(links).toHaveLength(2); }); it('should fallback to core fonts when getFonts returns null', () => { - mockGetFonts.mockReturnValue(null); + mockGetFonts.mockReturnValue(null as any); - const { container } = render(); - const links = getPreloadLinks(container); + const { view } = renderView(); + const links = getPreloadLinks(view.container); expect(links).toHaveLength(2); }); it('should fallback to core fonts when getFonts returns non-array', () => { - mockGetFonts.mockReturnValue('not-an-array'); + mockGetFonts.mockReturnValue('not-an-array' as any); - const { container } = render(); - const links = getPreloadLinks(container); + const { view } = renderView(); + const links = getPreloadLinks(view.container); expect(links).toHaveLength(0); }); @@ -267,8 +265,8 @@ describe('AssetProvider', () => { }, ]); - const { container } = render(); - const links = getPreloadLinks(container); + const { view } = renderView(); + const links = getPreloadLinks(view.container); expect(links).toHaveLength(3); expect(links[0]).toHaveAttribute( @@ -285,7 +283,7 @@ describe('AssetProvider', () => { ); }); - it('should only render valid font configurations', () => { + it('should only rnder valid font configurations ', () => { mockGetFonts.mockReturnValue([ { filePath: 'https://www.codecademy.com/gamut/valid-font', @@ -304,8 +302,8 @@ describe('AssetProvider', () => { } as any, ]); - const { container } = render(); - const links = getPreloadLinks(container); + const { view } = renderView(); + const links = getPreloadLinks(view.container); expect(links).toHaveLength(1); expect(links[0]).toHaveAttribute( @@ -333,8 +331,8 @@ describe('AssetProvider', () => { }, ]); - const { container } = render(); - const links = getPreloadLinks(container); + const { view } = renderView(); + const links = getPreloadLinks(view.container); expect(links).toHaveLength(1); expect(links[0]).toHaveAttribute( diff --git a/packages/gamut-styles/src/__tests__/fontLoading.test.tsx b/packages/gamut-styles/src/__tests__/fontLoading.test.tsx index ccdc03b83b..9c0fe66ad9 100644 --- a/packages/gamut-styles/src/__tests__/fontLoading.test.tsx +++ b/packages/gamut-styles/src/__tests__/fontLoading.test.tsx @@ -8,12 +8,10 @@ import { getFonts } from '../utils/fontUtils'; const typedPercipioTheme = percipioTheme as any; jest.mock('../utils/fontUtils', () => ({ - __esModule: true, getFonts: jest.fn(), })); jest.mock('../remoteAssets/fonts', () => ({ - __esModule: true, webFonts: { core: [ { From d726437a160018221b92f653ee025320e7a43f1e Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Thu, 22 Jan 2026 16:15:43 -0500 Subject: [PATCH 12/18] more cleanup --- .../src/__tests__/AssetProvider.test.tsx | 19 ++++++------------ .../src/__tests__/fontLoading.test.tsx | 20 ++++++------------- 2 files changed, 12 insertions(+), 27 deletions(-) diff --git a/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx b/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx index fb7c39d460..c23f89a950 100644 --- a/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx +++ b/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx @@ -5,7 +5,11 @@ import { render } from '@testing-library/react'; import { AssetProvider, createFontLinks } from '../AssetProvider'; import { coreTheme, percipioTheme } from '../themes'; -import { getFonts } from '../utils/fontUtils'; +import { + cleanupPreloadLinks, + getPreloadLinks, + mockGetFonts, +} from './helpers'; const renderView = setupRtl(AssetProvider, {}); @@ -44,21 +48,10 @@ jest.mock('../remoteAssets/fonts', () => ({ }, })); -const mockGetFonts = getFonts as jest.MockedFunction; - describe('AssetProvider', () => { - // Helper to get links from either container or document.head since React 19 hoists link elements - const getPreloadLinks = (container: HTMLElement) => { - const containerLinks = container.querySelectorAll('link[rel="preload"]'); - if (containerLinks.length > 0) return containerLinks; - return document.head.querySelectorAll('link[rel="preload"][as="font"]'); - }; - beforeEach(() => { jest.clearAllMocks(); - document.head - .querySelectorAll('link[rel="preload"][as="font"]') - .forEach((el) => el.remove()); + cleanupPreloadLinks(); }); describe('createFontLinks', () => { diff --git a/packages/gamut-styles/src/__tests__/fontLoading.test.tsx b/packages/gamut-styles/src/__tests__/fontLoading.test.tsx index 9c0fe66ad9..f358368edf 100644 --- a/packages/gamut-styles/src/__tests__/fontLoading.test.tsx +++ b/packages/gamut-styles/src/__tests__/fontLoading.test.tsx @@ -2,7 +2,11 @@ import { render } from '@testing-library/react'; import { AssetProvider } from '../AssetProvider'; import { coreTheme, percipioTheme } from '../themes'; -import { getFonts } from '../utils/fontUtils'; +import { + cleanupPreloadLinks, + getPreloadLinks, + mockGetFonts, +} from './helpers'; // Type assertion to satisfy Theme interface in GamutProvider from theme.d.ts - this lib is typed to the CoreTheme interface const typedPercipioTheme = percipioTheme as any; @@ -30,8 +34,6 @@ jest.mock('../remoteAssets/fonts', () => ({ }, })); -const mockGetFonts = getFonts as jest.MockedFunction; - const mockDocumentFonts = { load: jest.fn(), ready: Promise.resolve(), @@ -49,22 +51,12 @@ const mockFetch = jest.fn(); global.fetch = mockFetch; describe('Font Loading and Error Handling', () => { - // Helper to get links from either container or document.head (React 19 hoists link elements) - const getPreloadLinks = (container: HTMLElement) => { - const containerLinks = container.querySelectorAll('link[rel="preload"]'); - if (containerLinks.length > 0) return containerLinks; - return document.head.querySelectorAll('link[rel="preload"][as="font"]'); - }; - beforeEach(() => { jest.clearAllMocks(); mockDocumentFonts.load.mockClear(); mockDocumentFonts.check.mockClear(); mockFetch.mockClear(); - // Clean up any links from previous tests (React 19 hoists link elements to head) - document.head - .querySelectorAll('link[rel="preload"][as="font"]') - .forEach((el) => el.remove()); + cleanupPreloadLinks(); }); describe('Font Preloading', () => { From 7eb54bac031ab0a82acdf8a293e0b4dcee840b33 Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Thu, 22 Jan 2026 16:20:48 -0500 Subject: [PATCH 13/18] DRY up code and clean up --- .../src/__tests__/AssetProvider.test.tsx | 6 +---- .../src/__tests__/fontLoading.test.tsx | 6 +---- .../gamut-styles/src/__tests__/helpers.ts | 25 +++++++++++++++++++ 3 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 packages/gamut-styles/src/__tests__/helpers.ts diff --git a/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx b/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx index c23f89a950..66a6be302b 100644 --- a/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx +++ b/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx @@ -5,11 +5,7 @@ import { render } from '@testing-library/react'; import { AssetProvider, createFontLinks } from '../AssetProvider'; import { coreTheme, percipioTheme } from '../themes'; -import { - cleanupPreloadLinks, - getPreloadLinks, - mockGetFonts, -} from './helpers'; +import { cleanupPreloadLinks, getPreloadLinks, mockGetFonts } from './helpers'; const renderView = setupRtl(AssetProvider, {}); diff --git a/packages/gamut-styles/src/__tests__/fontLoading.test.tsx b/packages/gamut-styles/src/__tests__/fontLoading.test.tsx index f358368edf..49bf3625aa 100644 --- a/packages/gamut-styles/src/__tests__/fontLoading.test.tsx +++ b/packages/gamut-styles/src/__tests__/fontLoading.test.tsx @@ -2,11 +2,7 @@ import { render } from '@testing-library/react'; import { AssetProvider } from '../AssetProvider'; import { coreTheme, percipioTheme } from '../themes'; -import { - cleanupPreloadLinks, - getPreloadLinks, - mockGetFonts, -} from './helpers'; +import { cleanupPreloadLinks, getPreloadLinks, mockGetFonts } from './helpers'; // Type assertion to satisfy Theme interface in GamutProvider from theme.d.ts - this lib is typed to the CoreTheme interface const typedPercipioTheme = percipioTheme as any; diff --git a/packages/gamut-styles/src/__tests__/helpers.ts b/packages/gamut-styles/src/__tests__/helpers.ts new file mode 100644 index 0000000000..1bd3fc54c3 --- /dev/null +++ b/packages/gamut-styles/src/__tests__/helpers.ts @@ -0,0 +1,25 @@ +/** + * Helper to get preload links from either container or document.head. + * React 19 hoists elements to document.head automatically. + */ +export const getPreloadLinks = (container: HTMLElement) => { + const containerLinks = container.querySelectorAll('link[rel="preload"]'); + if (containerLinks.length > 0) return containerLinks; + return document.head.querySelectorAll('link[rel="preload"][as="font"]'); +}; + +/** + * Cleans up hoisted preload links between tests. + * Call this in beforeEach for tests that render elements. + */ +export const cleanupPreloadLinks = () => { + document.head + .querySelectorAll('link[rel="preload"][as="font"]') + .forEach((el) => el.remove()); +}; + +/** + * Mock reference for getFonts function. + * Must be used after jest.mock('../utils/fontUtils') is called. + */ +export const mockGetFonts = require('../utils/fontUtils').getFonts; From 91246adff47304861fef66cc7a1d2cb15484c27b Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Fri, 23 Jan 2026 15:50:48 -0500 Subject: [PATCH 14/18] update peerDependencies --- packages/gamut-icons/package.json | 2 +- packages/gamut-illustrations/package.json | 4 ++-- packages/gamut-patterns/package.json | 4 ++-- packages/gamut-styles/package.json | 2 +- packages/gamut-tests/package.json | 2 +- packages/gamut/package.json | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/gamut-icons/package.json b/packages/gamut-icons/package.json index c056b6a6cc..257a1ed716 100644 --- a/packages/gamut-icons/package.json +++ b/packages/gamut-icons/package.json @@ -17,7 +17,7 @@ "@emotion/react": "^11.4.0", "@emotion/styled": "^11.3.0", "lodash": "^4.17.5", - "react": "^17.0.2 || ^18.2.0" + "react": "^17.0.2 || ^18.2.0 || ^19.0.0" }, "publishConfig": { "access": "public" diff --git a/packages/gamut-illustrations/package.json b/packages/gamut-illustrations/package.json index 1e6374def1..0773ced453 100644 --- a/packages/gamut-illustrations/package.json +++ b/packages/gamut-illustrations/package.json @@ -18,8 +18,8 @@ "peerDependencies": { "@emotion/react": "^11.4.0", "@emotion/styled": "^11.3.0", - "react": "^17.0.2 || ^18.2.0", - "react-dom": "^17.0.2 || ^18.2.0" + "react": "^17.0.2 || ^18.2.0 || ^19.0.0", + "react-dom": "^17.0.2 || ^18.2.0 || ^19.0.0" }, "publishConfig": { "access": "public" diff --git a/packages/gamut-patterns/package.json b/packages/gamut-patterns/package.json index c8e342c6d6..390f028436 100644 --- a/packages/gamut-patterns/package.json +++ b/packages/gamut-patterns/package.json @@ -19,8 +19,8 @@ "peerDependencies": { "@emotion/react": "^11.4.0", "@emotion/styled": "^11.3.0", - "react": "^17.0.2 || ^18.2.0", - "react-dom": "^17.0.2 || ^18.2.0" + "react": "^17.0.2 || ^18.2.0 || ^19.0.0", + "react-dom": "^17.0.2 || ^18.2.0 || ^19.0.0" }, "publishConfig": { "access": "public" diff --git a/packages/gamut-styles/package.json b/packages/gamut-styles/package.json index 510d757756..ff101645a5 100644 --- a/packages/gamut-styles/package.json +++ b/packages/gamut-styles/package.json @@ -24,7 +24,7 @@ "@emotion/react": "^11.4.0", "@emotion/styled": "^11.3.0", "lodash": "^4.17.5", - "react": "^17.0.2 || ^18.2.0", + "react": "^17.0.2 || ^18.2.0 || ^19.0.0", "stylis": "^4.0.7" }, "publishConfig": { diff --git a/packages/gamut-tests/package.json b/packages/gamut-tests/package.json index 97b2eb16aa..0325e22d57 100644 --- a/packages/gamut-tests/package.json +++ b/packages/gamut-tests/package.json @@ -22,7 +22,7 @@ "main": "dist/index.js", "module": "dist/index.js", "peerDependencies": { - "react": "^17.0.2 || ^18.2.0" + "react": "^17.0.2 || ^18.2.0 || ^19.0.0" }, "publishConfig": { "access": "public" diff --git a/packages/gamut/package.json b/packages/gamut/package.json index 8d7340151b..9cae0743f0 100644 --- a/packages/gamut/package.json +++ b/packages/gamut/package.json @@ -37,8 +37,8 @@ "peerDependencies": { "@emotion/react": "^11.4.0", "@emotion/styled": "^11.3.0", - "react": "^17.0.2 || ^18.2.0", - "react-dom": "^17.0.2 || ^18.2.0" + "react": "^17.0.2 || ^18.2.0 || ^19.0.0", + "react-dom": "^17.0.2 || ^18.2.0 || ^19.0.0" }, "publishConfig": { "access": "public" From d950213953e3e06ba2127d42462477dc45f9931d Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Fri, 23 Jan 2026 16:07:51 -0500 Subject: [PATCH 15/18] update peerDependencies with types --- packages/gamut-icons/package.json | 1 + packages/gamut-illustrations/package.json | 1 + packages/gamut-patterns/package.json | 1 + packages/gamut-styles/package.json | 1 + packages/gamut-tests/package.json | 1 + packages/gamut/package.json | 1 + 6 files changed, 6 insertions(+) diff --git a/packages/gamut-icons/package.json b/packages/gamut-icons/package.json index 257a1ed716..9be932c04e 100644 --- a/packages/gamut-icons/package.json +++ b/packages/gamut-icons/package.json @@ -16,6 +16,7 @@ "peerDependencies": { "@emotion/react": "^11.4.0", "@emotion/styled": "^11.3.0", + "@types/react": "^18.0.0 || ^19.0.0", "lodash": "^4.17.5", "react": "^17.0.2 || ^18.2.0 || ^19.0.0" }, diff --git a/packages/gamut-illustrations/package.json b/packages/gamut-illustrations/package.json index 0773ced453..7b652b36d5 100644 --- a/packages/gamut-illustrations/package.json +++ b/packages/gamut-illustrations/package.json @@ -18,6 +18,7 @@ "peerDependencies": { "@emotion/react": "^11.4.0", "@emotion/styled": "^11.3.0", + "@types/react": "^18.0.0 || ^19.0.0", "react": "^17.0.2 || ^18.2.0 || ^19.0.0", "react-dom": "^17.0.2 || ^18.2.0 || ^19.0.0" }, diff --git a/packages/gamut-patterns/package.json b/packages/gamut-patterns/package.json index 390f028436..3f41cf1160 100644 --- a/packages/gamut-patterns/package.json +++ b/packages/gamut-patterns/package.json @@ -19,6 +19,7 @@ "peerDependencies": { "@emotion/react": "^11.4.0", "@emotion/styled": "^11.3.0", + "@types/react": "^18.0.0 || ^19.0.0", "react": "^17.0.2 || ^18.2.0 || ^19.0.0", "react-dom": "^17.0.2 || ^18.2.0 || ^19.0.0" }, diff --git a/packages/gamut-styles/package.json b/packages/gamut-styles/package.json index ff101645a5..d45f2443f1 100644 --- a/packages/gamut-styles/package.json +++ b/packages/gamut-styles/package.json @@ -23,6 +23,7 @@ "@emotion/cache": "^11.4.0", "@emotion/react": "^11.4.0", "@emotion/styled": "^11.3.0", + "@types/react": "^18.0.0 || ^19.0.0", "lodash": "^4.17.5", "react": "^17.0.2 || ^18.2.0 || ^19.0.0", "stylis": "^4.0.7" diff --git a/packages/gamut-tests/package.json b/packages/gamut-tests/package.json index 0325e22d57..7639ad6c38 100644 --- a/packages/gamut-tests/package.json +++ b/packages/gamut-tests/package.json @@ -22,6 +22,7 @@ "main": "dist/index.js", "module": "dist/index.js", "peerDependencies": { + "@types/react": "^18.0.0 || ^19.0.0", "react": "^17.0.2 || ^18.2.0 || ^19.0.0" }, "publishConfig": { diff --git a/packages/gamut/package.json b/packages/gamut/package.json index 9cae0743f0..c0806126fc 100644 --- a/packages/gamut/package.json +++ b/packages/gamut/package.json @@ -37,6 +37,7 @@ "peerDependencies": { "@emotion/react": "^11.4.0", "@emotion/styled": "^11.3.0", + "@types/react": "^18.0.0 || ^19.0.0", "react": "^17.0.2 || ^18.2.0 || ^19.0.0", "react-dom": "^17.0.2 || ^18.2.0 || ^19.0.0" }, From 8afef4aef381e48a9529df3818413c5552909eac Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Fri, 23 Jan 2026 16:30:28 -0500 Subject: [PATCH 16/18] revert back to react 18 types --- package.json | 7 +++---- yarn.lock | 23 ++++++++++++++++++----- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index e648dad629..77960a9ddb 100644 --- a/package.json +++ b/package.json @@ -52,8 +52,8 @@ "@types/invariant": "2.2.29", "@types/konami-code-js": "^0.8.0", "@types/lodash": "4.17.0", - "@types/react": "^19.2.9", - "@types/react-dom": "^19.2.3", + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", "@types/react-test-renderer": "^19.1.0", "@types/stylis": "^4.2.0", "@typescript-eslint/eslint-plugin": "^5.15.0", @@ -113,8 +113,7 @@ "repository": "git@github.com:Codecademy/gamut.git", "resolutions": { "@typescript-eslint/utils": "^5.15.0", - "error-ex": "1.3.4", - "@types/react": "^19.2.9" + "error-ex": "1.3.4" }, "scripts": { "build": "nx run-many --target=build --all", diff --git a/yarn.lock b/yarn.lock index 26fbeed586..b583d1a5bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5796,6 +5796,11 @@ resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== +"@types/prop-types@*": + version "15.7.15" + resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz#e6e5a86d602beaca71ce5163fadf5f95d70931c7" + integrity sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw== + "@types/q@^1.5.1": version "1.5.8" resolved "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz#95f6c6a08f2ad868ba230ead1d2d7f7be3db3837" @@ -5811,10 +5816,10 @@ resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/react-dom@^19.2.3": - version "19.2.3" - resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c" - integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ== +"@types/react-dom@^18.2.0": + version "18.3.7" + resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz#b89ddf2cd83b4feafcc4e2ea41afdfb95a0d194f" + integrity sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ== "@types/react-test-renderer@^19.1.0": version "19.1.0" @@ -5830,13 +5835,21 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^19.2.9": +"@types/react@*": version "19.2.9" resolved "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz#84ec7669742bb3e7e2e8d6a5258d95ead7764200" integrity sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA== dependencies: csstype "^3.2.2" +"@types/react@^18.2.0": + version "18.3.27" + resolved "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz#74a3b590ea183983dc65a474dc17553ae1415c34" + integrity sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w== + dependencies: + "@types/prop-types" "*" + csstype "^3.2.2" + "@types/resolve@1.20.2": version "1.20.2" resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975" From ccfb162f1614be8d8089f361f9e972ec9678716f Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Fri, 23 Jan 2026 16:35:33 -0500 Subject: [PATCH 17/18] revert some types to support react 18 --- packages/gamut/src/Popover/types.tsx | 4 ++-- packages/gamut/src/PopoverContainer/hooks.ts | 6 +++--- packages/gamut/src/Tip/__tests__/helpers.tsx | 2 +- packages/gamut/src/Tip/shared/types.tsx | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/gamut/src/Popover/types.tsx b/packages/gamut/src/Popover/types.tsx index 1a605f4309..d65d0dddcd 100755 --- a/packages/gamut/src/Popover/types.tsx +++ b/packages/gamut/src/Popover/types.tsx @@ -101,13 +101,13 @@ export type PopoverProps = PopoverBaseProps & targetRef: React.RefObject | null>; + >>; /** * The PopoverContainer which contents will be rendered into. */ popoverContainerRef?: - | React.RefObject + | React.RefObject | React.RefCallback; /** diff --git a/packages/gamut/src/PopoverContainer/hooks.ts b/packages/gamut/src/PopoverContainer/hooks.ts index 8ddd9c13d3..a65f9e35a1 100644 --- a/packages/gamut/src/PopoverContainer/hooks.ts +++ b/packages/gamut/src/PopoverContainer/hooks.ts @@ -6,7 +6,7 @@ export const useScrollingParentsEffect = ( targetRef: React.RefObject | null>, + >>, setTargetRect: (rect: DOMRect | undefined) => void ) => { useEffect(() => { @@ -43,7 +43,7 @@ export const useResizingParentEffect = ( targetRef: React.RefObject | null>, + >>, setTargetRect: (rect: DOMRect | undefined) => void ) => { useEffect(() => { @@ -72,7 +72,7 @@ export const useResizingParentEffect = ( * Returns an empty array if the target element is not available. */ export const useScrollingParents = ( - targetRef: React.RefObject + targetRef: React.RefObject ): HTMLElement[] => { return useMemo(() => { if (!targetRef.current) { diff --git a/packages/gamut/src/Tip/__tests__/helpers.tsx b/packages/gamut/src/Tip/__tests__/helpers.tsx index 1697aafb45..4eaa8ef41a 100644 --- a/packages/gamut/src/Tip/__tests__/helpers.tsx +++ b/packages/gamut/src/Tip/__tests__/helpers.tsx @@ -19,7 +19,7 @@ type LinkTextParam = { linkText: string }; type InfoParam = { info: string }; type PlacementParam = { placement: Placement }; -export const createFocusOnClick = (ref: RefObject) => { +export const createFocusOnClick = (ref: RefObject) => { return ({ isTipHidden }: { isTipHidden: boolean }) => { if (!isTipHidden) ref.current?.focus(); }; diff --git a/packages/gamut/src/Tip/shared/types.tsx b/packages/gamut/src/Tip/shared/types.tsx index 3eea8a24ce..a91c5e6a6f 100644 --- a/packages/gamut/src/Tip/shared/types.tsx +++ b/packages/gamut/src/Tip/shared/types.tsx @@ -79,9 +79,9 @@ export type TipPlacementComponentProps = Omit< id?: string; isTipHidden?: boolean; popoverContentRef?: - | React.RefObject + | React.RefObject | ((node: HTMLDivElement | null) => void); type: 'info' | 'tool' | 'preview'; - wrapperRef?: React.RefObject; + wrapperRef?: React.RefObject; zIndex?: number; } & React.PropsWithChildren; From f99ea8c777368ee5b427fb405c050783f08a3399 Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Fri, 23 Jan 2026 16:39:18 -0500 Subject: [PATCH 18/18] more lint fixes --- packages/gamut/src/Popover/types.tsx | 7 +++---- .../src/PopoverContainer/PopoverContainer.tsx | 2 +- packages/gamut/src/PopoverContainer/hooks.ts | 14 ++++++-------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/gamut/src/Popover/types.tsx b/packages/gamut/src/Popover/types.tsx index d65d0dddcd..b85d4d4595 100755 --- a/packages/gamut/src/Popover/types.tsx +++ b/packages/gamut/src/Popover/types.tsx @@ -98,10 +98,9 @@ export type PopoverProps = PopoverBaseProps & /** * The target element around which the popover will be positioned. */ - targetRef: React.RefObject>; + targetRef: React.RefObject< + Pick + >; /** * The PopoverContainer which contents will be rendered into. diff --git a/packages/gamut/src/PopoverContainer/PopoverContainer.tsx b/packages/gamut/src/PopoverContainer/PopoverContainer.tsx index 86ba4908bd..cfec4659ec 100644 --- a/packages/gamut/src/PopoverContainer/PopoverContainer.tsx +++ b/packages/gamut/src/PopoverContainer/PopoverContainer.tsx @@ -51,7 +51,7 @@ export const PopoverContainer: React.FC = ({ // Memoize scrolling parents to avoid expensive DOM traversals const scrollingParents = useScrollingParents( - targetRef as React.RefObject + targetRef as React.RefObject ); // Keep onRequestClose ref up to date diff --git a/packages/gamut/src/PopoverContainer/hooks.ts b/packages/gamut/src/PopoverContainer/hooks.ts index a65f9e35a1..ea2a5d1c5d 100644 --- a/packages/gamut/src/PopoverContainer/hooks.ts +++ b/packages/gamut/src/PopoverContainer/hooks.ts @@ -3,10 +3,9 @@ import { useEffect, useMemo } from 'react'; import { findAllAdditionalScrollingParents, findResizingParent } from './utils'; export const useScrollingParentsEffect = ( - targetRef: React.RefObject>, + targetRef: React.RefObject< + Pick + >, setTargetRect: (rect: DOMRect | undefined) => void ) => { useEffect(() => { @@ -40,10 +39,9 @@ export const useScrollingParentsEffect = ( }; export const useResizingParentEffect = ( - targetRef: React.RefObject>, + targetRef: React.RefObject< + Pick + >, setTargetRect: (rect: DOMRect | undefined) => void ) => { useEffect(() => {