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 02cf271da29..9c33f56563c 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 58fa2a2af98..c3c9632c1d1 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 a88b1bfb116..3d82f4b14be 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 9c33f56563c..e654382d179 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 14cfaa4c647..8a0e913b916 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 c3c9632c1d1..0290c267539 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 1fdf68df5b2..b473df4da27 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 8a0e913b916..9f517d6c7d6 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 b473df4da27..f2906226b74 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 3d82f4b14be..4c58d7b4b3c 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 4c58d7b4b3c..d4d9a583d6e 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 e654382d179..e648dad6294 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 0290c267539..26fbeed5867 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 d4d9a583d6e..0d88d4d0e17 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 02c84d4e278..4d770db8c3f 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 a4316f6fde3..bdc8bc17736 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 95575145d48..a6b8a190e35 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 f31839eaa46..50af0d4e4c7 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 b85d4d4595c..1a605f43097 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 295591da7cd..8ddd9c13d35 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 4eaa8ef41a5..1697aafb451 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 c251d1570cf..fb29a0e1b06 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 a91c5e6a6f7..3eea8a24ce5 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 c0b673bb24d..a3ebd4391cb 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 0d88d4d0e17..9a8a1db2a9a 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 72005789548..c290a4af950 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 d0dbd1b889d..6b3f2d6e243 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 8588c23304d..a5eabfe4efb 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 6a46a50c3ab..ccdc03b83b1 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 9a8a1db2a9a..0d88d4d0e17 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 2690797df50..6db6a89db06 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 bdc494b5fca..ed1ec6c47b2 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 a5eabfe4efb..fb7c39d460c 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 ccdc03b83b1..9c0fe66ad99 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 fb7c39d460c..c23f89a9501 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 9c0fe66ad99..f358368edfd 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 c23f89a9501..66a6be302b5 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 f358368edfd..49bf3625aac 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 00000000000..1bd3fc54c34 --- /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 c056b6a6cc3..257a1ed7169 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 1e6374def14..0773ced453d 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 c8e342c6d6d..390f0284361 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 510d757756f..ff101645a50 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 97b2eb16aae..0325e22d576 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 8d7340151b6..9cae0743f08 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 257a1ed7169..9be932c04e9 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 0773ced453d..7b652b36d57 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 390f0284361..3f41cf11606 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 ff101645a50..d45f2443f14 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 0325e22d576..7639ad6c388 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 9cae0743f08..c0806126fc8 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 e648dad6294..77960a9ddb3 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 26fbeed5867..b583d1a5bf8 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 1a605f43097..d65d0dddcd0 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 8ddd9c13d35..a65f9e35a1c 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 1697aafb451..4eaa8ef41a5 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 3eea8a24ce5..a91c5e6a6f7 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 d65d0dddcd0..b85d4d4595c 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 86ba4908bde..cfec4659ecb 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 a65f9e35a1c..ea2a5d1c5df 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(() => {