From a3f866274b7d1056bf733666c828a04100875b3f Mon Sep 17 00:00:00 2001 From: Anton Arnautov Date: Sat, 18 Oct 2025 10:21:13 +0200 Subject: [PATCH 1/2] Initial commit --- .../react/egress-composite/package.json | 2 - .../egress-composite/src/CompositeApp.scss | 54 +++++++++-------- .../egress-composite/src/CompositeApp.tsx | 6 +- .../src/ConfigurationContext.tsx | 3 +- .../src/components/LogoAndTitle.scss | 9 +-- .../DominantSpeaker/DominantSpeaker.scss | 18 +++--- .../DominantSpeakerScreenShare.scss | 32 +++++----- .../layouts/PaginatedGrid/PaginatedGrid.scss | 42 +++++++++---- .../layouts/Spotlight/Spotlight.scss | 22 +++---- .../react/egress-composite/src/global.d.ts | 9 +++ .../hooks/options/useGenericLayoutStyles.ts | 33 ++++------ .../hooks/options/useLogoAndTitleStyles.ts | 40 ++++++------- .../options/useParticipantLabelStyles.ts | 60 +++++++++---------- .../src/hooks/options/useParticipantStyles.ts | 34 +++++------ .../src/hooks/options/useVideoStyles.ts | 24 ++++---- .../src/hooks/useInitializeClient.ts | 2 - .../react/egress-composite/src/main.scss | 4 ++ .../react/egress-composite/src/main.tsx | 3 +- .../egress-composite/tests/layouts.spec.ts | 2 +- yarn.lock | 9 --- 20 files changed, 205 insertions(+), 203 deletions(-) create mode 100644 sample-apps/react/egress-composite/src/global.d.ts create mode 100644 sample-apps/react/egress-composite/src/main.scss diff --git a/sample-apps/react/egress-composite/package.json b/sample-apps/react/egress-composite/package.json index 0bfd34f0ff..efeec9a810 100644 --- a/sample-apps/react/egress-composite/package.json +++ b/sample-apps/react/egress-composite/package.json @@ -14,8 +14,6 @@ "@emotion/css": "^11.13.5", "@sentry/react": "^10.19.0", "@stream-io/video-react-sdk": "workspace:^", - "clsx": "^2.0.0", - "js-base64": "^3.7.8", "react": "19.1.0", "react-dom": "19.1.0" }, diff --git a/sample-apps/react/egress-composite/src/CompositeApp.scss b/sample-apps/react/egress-composite/src/CompositeApp.scss index ee0e82e86e..c8557286b8 100644 --- a/sample-apps/react/egress-composite/src/CompositeApp.scss +++ b/sample-apps/react/egress-composite/src/CompositeApp.scss @@ -1,31 +1,33 @@ -:root { - font-family: Inter, Avenir, Helvetica, Arial, sans-serif; - font-size: 16px; - line-height: 24px; - font-weight: 400; +@layer base-layer { + :root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; -} + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + } -.str-video { - color: var(--str-video__text-color1); - position: relative; - height: 100vh; -} + .str-video { + color: var(--str-video__text-color1); + position: relative; + height: 100vh; + } -body { - margin: 0; - min-width: 320px; - min-height: 100vh; - background-color: #000000; - overflow: hidden; -} + body { + margin: 0; + min-width: 320px; + min-height: 100vh; + background-color: #000000; + overflow: hidden; + } -#root { - margin: 0 auto; - text-align: center; + #root { + margin: 0 auto; + text-align: center; + } } diff --git a/sample-apps/react/egress-composite/src/CompositeApp.tsx b/sample-apps/react/egress-composite/src/CompositeApp.tsx index e7f6573c17..656a0dcea4 100644 --- a/sample-apps/react/egress-composite/src/CompositeApp.tsx +++ b/sample-apps/react/egress-composite/src/CompositeApp.tsx @@ -4,7 +4,7 @@ import { StreamTheme, StreamVideo, } from '@stream-io/video-react-sdk'; -import clsx from 'clsx'; +import { cx } from '@emotion/css'; import { EgressReadyNotificationProvider, @@ -24,9 +24,7 @@ import { WithCustomActions } from './components/CustomActionsContext'; export const CompositeApp = () => { const { client, call } = useInitializeClientAndCall(); - // @ts-expect-error makes it easy to debug in the browser console window.call = call; - // @ts-expect-error makes it easy to debug in the browser console window.client = client; return ( @@ -57,7 +55,7 @@ export const StreamThemeWrapper = ({ children }: PropsWithChildren) => { return ( void; + call: Call; + client: StreamVideoClient; + } +} diff --git a/sample-apps/react/egress-composite/src/hooks/options/useGenericLayoutStyles.ts b/sample-apps/react/egress-composite/src/hooks/options/useGenericLayoutStyles.ts index 5ea76f9bf8..49af3e9e41 100644 --- a/sample-apps/react/egress-composite/src/hooks/options/useGenericLayoutStyles.ts +++ b/sample-apps/react/egress-composite/src/hooks/options/useGenericLayoutStyles.ts @@ -1,5 +1,4 @@ -import { css } from '@emotion/css'; -import clsx from 'clsx'; +import { css, cx } from '@emotion/css'; import { useConfigurationContext } from '../../ConfigurationContext'; @@ -23,32 +22,20 @@ export const useGenericLayoutStyles = () => { const styles = [ css` - & .eca__dominant-speaker__container { - padding-inline: ${singleParticipantPaddingInline}; - padding-block: ${singleParticipantPaddingBlock}; - } - `, - layoutBackgroundColor && - css` + @layer overrides-layer { + & .eca__dominant-speaker__container { + padding-inline: ${singleParticipantPaddingInline}; + padding-block: ${singleParticipantPaddingBlock}; + } + background-color: ${layoutBackgroundColor}; - `, - layoutBackgroundImage && - css` background-image: ${layoutBackgroundImage}; - `, - layoutBackgroundSize && - css` background-size: ${layoutBackgroundSize}; - `, - layoutBackgroundPosition && - css` background-position: ${layoutBackgroundPosition}; - `, - layoutBackgroundRepeat && - css` background-repeat: ${layoutBackgroundRepeat}; - `, + } + `, ]; - return clsx(styles); + return cx(styles); }; diff --git a/sample-apps/react/egress-composite/src/hooks/options/useLogoAndTitleStyles.ts b/sample-apps/react/egress-composite/src/hooks/options/useLogoAndTitleStyles.ts index fe55808e86..de2cb3373a 100644 --- a/sample-apps/react/egress-composite/src/hooks/options/useLogoAndTitleStyles.ts +++ b/sample-apps/react/egress-composite/src/hooks/options/useLogoAndTitleStyles.ts @@ -1,5 +1,4 @@ -import { css } from '@emotion/css'; -import clsx from 'clsx'; +import { css, cx } from '@emotion/css'; import { positionMap, @@ -26,26 +25,27 @@ export const useLogoAndTitleStyles = () => { const styles = [ css` - & .eca__logo-and-title-overlay__logo { - justify-self: ${positionMap.horizontal[logoHorizontalPosition]}; - align-self: ${positionMap.vertical[logoVerticalPosition]}; - margin-block: ${logoMarginBlock}; - margin-inline: ${logoMarginInline}; - width: ${logoWidth}; - height: ${logoHeight}; - } - `, - css` - & .eca__logo-and-title-overlay__title { - color: ${titleColor}; - font-size: ${titleFontSize}; - justify-self: ${positionMap.horizontal[titleHorizontalPosition]}; - align-self: ${positionMap.vertical[titleVerticalPosition]}; - margin-block: ${titleMarginBlock}; - margin-inline: ${titleMarginInline}; + @layer overrides-layer { + & .eca__logo-and-title-overlay__logo { + justify-self: ${positionMap.horizontal[logoHorizontalPosition]}; + align-self: ${positionMap.vertical[logoVerticalPosition]}; + margin-block: ${logoMarginBlock}; + margin-inline: ${logoMarginInline}; + width: ${logoWidth}; + height: ${logoHeight}; + } + + & .eca__logo-and-title-overlay__title { + color: ${titleColor}; + font-size: ${titleFontSize}; + justify-self: ${positionMap.horizontal[titleHorizontalPosition]}; + align-self: ${positionMap.vertical[titleVerticalPosition]}; + margin-block: ${titleMarginBlock}; + margin-inline: ${titleMarginInline}; + } } `, ]; - return clsx(styles); + return cx(styles); }; diff --git a/sample-apps/react/egress-composite/src/hooks/options/useParticipantLabelStyles.ts b/sample-apps/react/egress-composite/src/hooks/options/useParticipantLabelStyles.ts index b52db0c2ca..fe7d1f1e85 100644 --- a/sample-apps/react/egress-composite/src/hooks/options/useParticipantLabelStyles.ts +++ b/sample-apps/react/egress-composite/src/hooks/options/useParticipantLabelStyles.ts @@ -1,5 +1,4 @@ -import { css } from '@emotion/css'; -import clsx from 'clsx'; +import { cx, css } from '@emotion/css'; import { positionMap, @@ -28,51 +27,50 @@ export const useParticipantLabelStyles = () => { const styles = [ !participantLabelDisplay && css` - & .str-video__participant-details { - display: none; + @layer overrides-layer { + & .str-video__participant-details { + display: none; + } } `, - participantLabelTextColor && - css` + css` + @layer overrides-layer { & .str-video__participant-details { color: ${participantLabelTextColor}; } - `, - participantLabelBackgroundColor && - css` + & .str-video__participant-details { background-color: ${participantLabelBackgroundColor}; } - `, - participantLabelBorderRadius && - css` + & .str-video__participant-details { border-radius: ${participantLabelBorderRadius}; } - `, - css` - & .str-video__participant-details { - border: ${participantLabelBorderWidth} solid - ${participantLabelBorderColor}; - } - `, - css` - & .str-video__participant-view { - justify-content: ${positionMap.horizontal[ - participantLabelHorizontalPosition - ]}; - .str-video__participant-details { - align-self: ${positionMap.vertical[participantLabelVerticalPosition]}; - margin-inline: ${participantLabelMarginInline}; - margin-block: ${participantLabelMarginBlock}; + & .str-video__participant-details { + border: ${participantLabelBorderWidth} solid + ${participantLabelBorderColor}; + } + + & .str-video__participant-view { + justify-content: ${positionMap.horizontal[ + participantLabelHorizontalPosition + ]}; + + .str-video__participant-details { + align-self: ${positionMap.vertical[ + participantLabelVerticalPosition + ]}; + margin-inline: ${participantLabelMarginInline}; + margin-block: ${participantLabelMarginBlock}; - transition: unset; - opacity: unset; + transition: unset; + opacity: unset; + } } } `, ]; - return clsx(styles); + return cx(styles); }; diff --git a/sample-apps/react/egress-composite/src/hooks/options/useParticipantStyles.ts b/sample-apps/react/egress-composite/src/hooks/options/useParticipantStyles.ts index 6d311f4c38..c3603e296b 100644 --- a/sample-apps/react/egress-composite/src/hooks/options/useParticipantStyles.ts +++ b/sample-apps/react/egress-composite/src/hooks/options/useParticipantStyles.ts @@ -1,5 +1,4 @@ -import { css } from '@emotion/css'; -import clsx from 'clsx'; +import { css, cx } from '@emotion/css'; import { useCallStateHooks } from '@stream-io/video-react-sdk'; import { useConfigurationContext } from '../../ConfigurationContext'; @@ -19,12 +18,6 @@ export const useParticipantStyles = () => { const { useHasOngoingScreenShare } = useCallStateHooks(); const hasScreenShare = useHasOngoingScreenShare(); const styles = [ - participantBorderRadius && - css` - & .str-video__participant-view { - border-radius: ${participantBorderRadius}; - } - `, // we don't want to apply the aspect ratio when screen share is // enabled, as it breaks our layouts. // we should think about this later, and most likely introduce @@ -32,22 +25,27 @@ export const useParticipantStyles = () => { participantAspectRatio && !hasScreenShare && css` - & .str-video__participant-view { - aspect-ratio: ${participantAspectRatio}; + @layer overrides-layer { + & .str-video__participant-view { + aspect-ratio: ${participantAspectRatio}; + } } `, css` - & .str-video__participant-view.str-video__participant-view--speaking { - outline: ${participantOutlineWidth} solid ${participantOutlineColor}; - } - `, - participantPlaceholderBackgroundColor && - css` + @layer overrides-layer { + & .str-video__participant-view.str-video__participant-view--speaking { + outline: ${participantOutlineWidth} solid ${participantOutlineColor}; + } + & .str-video__video-placeholder { background-color: ${participantPlaceholderBackgroundColor}; } - `, + & .str-video__participant-view { + border-radius: ${participantBorderRadius}; + } + } + `, ]; - return clsx(styles); + return cx(styles); }; diff --git a/sample-apps/react/egress-composite/src/hooks/options/useVideoStyles.ts b/sample-apps/react/egress-composite/src/hooks/options/useVideoStyles.ts index e3d6387490..0bafecaead 100644 --- a/sample-apps/react/egress-composite/src/hooks/options/useVideoStyles.ts +++ b/sample-apps/react/egress-composite/src/hooks/options/useVideoStyles.ts @@ -1,5 +1,4 @@ -import { css } from '@emotion/css'; -import clsx from 'clsx'; +import { css, cx } from '@emotion/css'; import { objectFitMap, @@ -16,25 +15,24 @@ export const useVideoStyles = () => { } = useConfigurationContext(); const styles = [ - videoBackgroundColor && - css` + css` + @layer overrides-layer { & .str-video__video { background-color: ${videoBackgroundColor}; } - `, - videoScaleMode && - css` + & .str-video__video { - object-fit: ${objectFitMap[videoScaleMode]}; + object-fit: ${typeof videoScaleMode === 'undefined' + ? undefined + : objectFitMap[videoScaleMode]}; } - `, - videoScreenshareScaleMode && - css` + & .str-video__video.str-video__video--screen-share { object-fit: ${objectFitMap[videoScreenshareScaleMode]}; } - `, + } + `, ]; - return clsx(styles); + return cx(styles); }; diff --git a/sample-apps/react/egress-composite/src/hooks/useInitializeClient.ts b/sample-apps/react/egress-composite/src/hooks/useInitializeClient.ts index c1337e7a3b..2c661bff92 100644 --- a/sample-apps/react/egress-composite/src/hooks/useInitializeClient.ts +++ b/sample-apps/react/egress-composite/src/hooks/useInitializeClient.ts @@ -99,9 +99,7 @@ export const useInitializeClientAndCall = () => { // join call and proceed normally useJoinCall({ client, call, enabled: !testEnvironment }); - // @ts-expect-error expose the client and call for debugging window.client = client; - // @ts-expect-error expose the client and call for debugging window.call = call; return { client, call }; diff --git a/sample-apps/react/egress-composite/src/main.scss b/sample-apps/react/egress-composite/src/main.scss new file mode 100644 index 0000000000..af48df9f3b --- /dev/null +++ b/sample-apps/react/egress-composite/src/main.scss @@ -0,0 +1,4 @@ +@layer video-sdk-layer, base-layer, overrides-layer; + +@import '@stream-io/video-react-sdk/dist/css/styles.css' layer(video-sdk-layer); + \ No newline at end of file diff --git a/sample-apps/react/egress-composite/src/main.tsx b/sample-apps/react/egress-composite/src/main.tsx index 7fcc851ac8..790c34478d 100644 --- a/sample-apps/react/egress-composite/src/main.tsx +++ b/sample-apps/react/egress-composite/src/main.tsx @@ -1,5 +1,6 @@ import { createRoot } from 'react-dom/client'; import * as Sentry from '@sentry/react'; +import './main.scss'; import { applyConfigurationDefaults, @@ -8,7 +9,6 @@ import { } from './ConfigurationContext'; import { CompositeApp } from './CompositeApp'; -import '@stream-io/video-react-sdk/dist/css/styles.css'; // Uncomment this line to test your own custom CSS // import cssUrl from '../public/example/custom.css?url'; @@ -29,7 +29,6 @@ if (import.meta.env.MODE === 'production') { }); } -// @ts-expect-error TODO: this is a global function, we need to declare it window.setupLayout = (configuration: ConfigurationValue) => { const newConfiguration = applyConfigurationDefaults(configuration); console.log('Mounting with config:', { configuration: newConfiguration }); diff --git a/sample-apps/react/egress-composite/tests/layouts.spec.ts b/sample-apps/react/egress-composite/tests/layouts.spec.ts index 95117ddad5..d0c869d6d5 100644 --- a/sample-apps/react/egress-composite/tests/layouts.spec.ts +++ b/sample-apps/react/egress-composite/tests/layouts.spec.ts @@ -1,5 +1,5 @@ import { expect } from '@playwright/test'; -import { clsx as concatStrings } from 'clsx'; +import { cx as concatStrings } from '@emotion/css'; import { testWithCallId as test } from './baseTests'; import { diff --git a/yarn.lock b/yarn.lock index b8ca5e2c91..72f5a0f834 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7112,8 +7112,6 @@ __metadata: "@types/react": "npm:~19.1.17" "@types/react-dom": "npm:~19.1.11" "@vitejs/plugin-react": "npm:^5.0.4" - clsx: "npm:^2.0.0" - js-base64: "npm:^3.7.8" nanoid: "npm:^5.1.6" react: "npm:19.1.0" react-dom: "npm:19.1.0" @@ -15748,13 +15746,6 @@ __metadata: languageName: node linkType: hard -"js-base64@npm:^3.7.8": - version: 3.7.8 - resolution: "js-base64@npm:3.7.8" - checksum: 10/4baa9a222bc094b072933e4894735653b59992477c731879784e3112f21862b08dd5c56b8b23b0b8e43bc5bb514d957621d1892a6c58950f29e12b731fe087b6 - languageName: node - linkType: hard - "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" From 3ab6f73663011a824f2c6b0eb4eaca11109328cf Mon Sep 17 00:00:00 2001 From: Anton Arnautov Date: Sat, 18 Oct 2025 11:09:28 +0200 Subject: [PATCH 2/2] Fix tests --- .../layouts/Spotlight/Spotlight.scss | 2 +- .../Layouts-Should-render-layout---grid-1.png | Bin 10714 -> 8653 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/sample-apps/react/egress-composite/src/components/layouts/Spotlight/Spotlight.scss b/sample-apps/react/egress-composite/src/components/layouts/Spotlight/Spotlight.scss index 9137960495..49fb804420 100644 --- a/sample-apps/react/egress-composite/src/components/layouts/Spotlight/Spotlight.scss +++ b/sample-apps/react/egress-composite/src/components/layouts/Spotlight/Spotlight.scss @@ -9,7 +9,7 @@ .str-video__participant-view { transition: width 0.2s ease-in-out; - max-width: unset; + // max-width: unset; } } } diff --git a/sample-apps/react/egress-composite/tests/__screenshots__/layouts.spec.ts/Layouts-Should-render-layout---grid-1.png b/sample-apps/react/egress-composite/tests/__screenshots__/layouts.spec.ts/Layouts-Should-render-layout---grid-1.png index 421967889c27245d6480079fefd269f33f6b759e..ce7bc2b62cc65b7fe9401dc887ee3f4e35981285 100644 GIT binary patch literal 8653 zcmeAS@N?(olHy`uVBq!ia0y~yU~gbxV6os}0*a(>3=?5s5Mc0haSW-L^XAq@&LaUl ztd5*_>QC?E;*mcmU8xqrs2@?n3sei#w2l!(H8e1Q`3_DEY#_dZq60ICo*=LQEbqZ_ z0mNlcVaWi~LQE!Lnv+oqL^rgIY99@{(PRV43!_#|y; zdo%vq&b-HOwCWhGHb=`pP#rK@{tf%`Z*Vk|jX|l6fpxSMKH39eU>NOzjP^i4#Q~xR zVrco!0X7ye6gu!p>>&3uFxink>QHkP-YnhDe1M&EDENAG%w=@E37F~;*>*J1j*c@j zFpQ3|GBAveFOQC`1B=GdmhEWE7Su4LQx$oH4`dkwOU>vc7%;>e<*mzfK;dC<;(nFL zZ!TpJJAwl;5h%t4nE-5s4E`o4LR#*l(xbrwOeLeqVesUVr+kxb85kI|K3 z#WAE}&YRnYe1{c8SRFfR{%cQG4mcuou4>uR%B%TdXAF-?0X2cZpS|Z_GD4UPcbpkO zMgl>HKm$}zi6sFn!qC9MC;?$9IE^X;hQw%qK$62~3IT`1XqEv7!)W0E4u;XfVYF~y z7|jc#d4YCKsdbl`87elERxvPapEqSQi&*Xs$&CyjtnH~V*!alGmVx0x;5Fc2 z=AE>>dv|LZ`hZPgh6+KT;rrg+-pI>fz;k%Rm;Z}rD_e;OoZ#NW4H@bj9ku~S!)R9( z91Nq)BuJ(l;PPQ~ssS7gqp<)EhS4G#91NpHG&mSWi)e5#40IE2w1@^r1Feea4rAl* z-#H*L&2X=hff>Sd;Wz*l6J#=gupC+#cpxkRMPy|XPl{q07&tpXYXKng1htGRV?YhV z(Eu3@5J(!pmCHs`$Y=@~O(6^nBveeJS!OiLjJPawglT=WK>nTU8j>Kdd%F6$taD0e F0syM|3Ss~N