From edbb246c9edf926b872f5fdeb01d8cc0379f86ae Mon Sep 17 00:00:00 2001 From: Christiaan Scheermeijer Date: Fri, 27 Sep 2024 14:27:02 +0200 Subject: [PATCH] feat(project): screen animations (#614) * feat: add screen animations * chore: update snapshots * fix: animation on mount not stable * chore: remove key from media screen routing --- .../src/components/Animation/Animation.tsx | 8 +- .../src/components/Header/Header.module.scss | 3 +- .../src/containers/ShelfList/ShelfList.tsx | 31 +- .../Home/__snapshots__/Home.test.tsx.snap | 304 +++++++++--------- .../pages/ScreenRouting/MediaScreenRouter.tsx | 7 +- .../ScreenRouting/PlaylistScreenRouter.tsx | 11 +- platforms/web/src/styles/main.scss | 2 +- 7 files changed, 197 insertions(+), 169 deletions(-) diff --git a/packages/ui-react/src/components/Animation/Animation.tsx b/packages/ui-react/src/components/Animation/Animation.tsx index 50f94bf4a..250de7d91 100644 --- a/packages/ui-react/src/components/Animation/Animation.tsx +++ b/packages/ui-react/src/components/Animation/Animation.tsx @@ -15,6 +15,8 @@ type Props = { export type Status = 'opening' | 'open' | 'closing' | 'closed'; +const triggerReflow = (element: HTMLElement | null) => element?.scrollTop; + const Animation: React.FC = ({ className, createStyle, @@ -26,6 +28,7 @@ const Animation: React.FC = ({ keepMounted = false, children, }) => { + const nodeRef = useRef(null); const [status, setStatus] = useState('closed'); const [hasOpenedBefore, setHasOpenedBefore] = useState(false); @@ -35,6 +38,9 @@ const Animation: React.FC = ({ // use event callbacks to ignore reactive dependencies const openEvent = useEventCallback(() => { setHasOpenedBefore(true); + // trigger a reflow to ensure the transition is respected after mount + triggerReflow(nodeRef.current); + timeout.current = window.setTimeout(() => setStatus('opening'), delay); timeout2.current = window.setTimeout(() => { setStatus('open'); @@ -70,7 +76,7 @@ const Animation: React.FC = ({ } return ( -
+
{children}
); diff --git a/packages/ui-react/src/components/Header/Header.module.scss b/packages/ui-react/src/components/Header/Header.module.scss index 90ccf3e50..0944cf960 100644 --- a/packages/ui-react/src/components/Header/Header.module.scss +++ b/packages/ui-react/src/components/Header/Header.module.scss @@ -27,7 +27,8 @@ // Make header static // &.static { - position: static; + position: relative; + z-index: 1; width: 100%; } } diff --git a/packages/ui-react/src/containers/ShelfList/ShelfList.tsx b/packages/ui-react/src/containers/ShelfList/ShelfList.tsx index de19fb526..e9097919b 100644 --- a/packages/ui-react/src/containers/ShelfList/ShelfList.tsx +++ b/packages/ui-react/src/containers/ShelfList/ShelfList.tsx @@ -16,6 +16,7 @@ import usePlaylists from '@jwp/ott-hooks-react/src/usePlaylists'; import Shelf from '../../components/Shelf/Shelf'; import InfiniteScrollLoader from '../../components/InfiniteScrollLoader/InfiniteScrollLoader'; import ErrorPage from '../../components/ErrorPage/ErrorPage'; +import Fade from '../../components/Animation/Fade/Fade'; import styles from './ShelfList.module.scss'; @@ -76,20 +77,22 @@ const ShelfList = ({ rows }: Props) => { data-testid={testId(`shelf-${featured ? 'featured' : type === 'playlist' ? slugify(title || playlist?.title) : type}`)} aria-label={title || playlist?.title} > - + + + ); })} diff --git a/packages/ui-react/src/pages/Home/__snapshots__/Home.test.tsx.snap b/packages/ui-react/src/pages/Home/__snapshots__/Home.test.tsx.snap index 27e966e86..ca0219352 100644 --- a/packages/ui-react/src/pages/Home/__snapshots__/Home.test.tsx.snap +++ b/packages/ui-react/src/pages/Home/__snapshots__/Home.test.tsx.snap @@ -19,111 +19,115 @@ exports[`Home Component tests > Home test 1`] = ` data-testid="shelf-this-is-a-playlist" > - +
@@ -134,111 +138,115 @@ exports[`Home Component tests > Home test 1`] = ` data-testid="shelf-second-playlist" > - + diff --git a/packages/ui-react/src/pages/ScreenRouting/MediaScreenRouter.tsx b/packages/ui-react/src/pages/ScreenRouting/MediaScreenRouter.tsx index fcb70b9f6..65bc5db2e 100644 --- a/packages/ui-react/src/pages/ScreenRouting/MediaScreenRouter.tsx +++ b/packages/ui-react/src/pages/ScreenRouting/MediaScreenRouter.tsx @@ -10,6 +10,7 @@ import useMedia from '@jwp/ott-hooks-react/src/useMedia'; import type { ScreenComponent } from '../../../types/screens'; import Loading from '../Loading/Loading'; import ErrorPage from '../../components/ErrorPage/ErrorPage'; +import Fade from '../../components/Animation/Fade/Fade'; import MediaStaticPage from './mediaScreens/MediaStaticPage/MediaStaticPage'; import MediaMovie from './mediaScreens/MediaMovie/MediaMovie'; @@ -48,7 +49,11 @@ const MediaScreenRouter = () => { const MediaScreen = mediaScreenMap.getScreen(data); - return ; + return ( + + + + ); }; export default MediaScreenRouter; diff --git a/packages/ui-react/src/pages/ScreenRouting/PlaylistScreenRouter.tsx b/packages/ui-react/src/pages/ScreenRouting/PlaylistScreenRouter.tsx index 1663b49a3..e52ea9298 100644 --- a/packages/ui-react/src/pages/ScreenRouting/PlaylistScreenRouter.tsx +++ b/packages/ui-react/src/pages/ScreenRouting/PlaylistScreenRouter.tsx @@ -10,6 +10,7 @@ import type { AppMenuType } from '@jwp/ott-common/types/config'; import Loading from '../Loading/Loading'; import ErrorPage from '../../components/ErrorPage/ErrorPage'; import type { ScreenComponent } from '../../../types/screens'; +import Fade from '../../components/Animation/Fade/Fade'; import PlaylistGrid from './playlistScreens/PlaylistGrid/PlaylistGrid'; import PlaylistLiveChannels from './playlistScreens/PlaylistLiveChannels/PlaylistLiveChannels'; @@ -28,10 +29,10 @@ const PlaylistScreenRouter = ({ type }: { type: AppMenuType }) => { const params = useParams(); const id = params.id || ''; - const { isLoading, isFetching, error, data } = usePlaylist(id, {}, true, true, type); + const { isFetching, error, data } = usePlaylist(id, {}, true, true, type); const { t } = useTranslation('error'); - if (isLoading) { + if (isFetching) { return ; } @@ -45,7 +46,11 @@ const PlaylistScreenRouter = ({ type }: { type: AppMenuType }) => { const Screen = type === APP_CONFIG_ITEM_TYPE.content_list ? contentScreenMap.getScreen(data) : playlistScreenMap.getScreen(data); - return ; + return ( + + + + ); }; export default PlaylistScreenRouter; diff --git a/platforms/web/src/styles/main.scss b/platforms/web/src/styles/main.scss index d1c89e698..bfaf19360 100644 --- a/platforms/web/src/styles/main.scss +++ b/platforms/web/src/styles/main.scss @@ -24,7 +24,7 @@ body { margin: 0; padding: 0; overflow-x: hidden; - overflow-y: auto; + overflow-y: scroll; // this prevents layout jumps when navigating between screens color: var(--body-color); font-family: var(--body-font-family); font-size: variables.$body-font-size;