From e2e94bee863d1831bc32ceff253bce028d040621 Mon Sep 17 00:00:00 2001 From: Yelyzaveta Khokhlova <69502158+yelyzavetakhokhlova@users.noreply.github.com> Date: Tue, 22 Oct 2024 14:39:15 +0300 Subject: [PATCH 1/7] Finish landing animations (#535) --- .../AnimatedHeader/AnimatedHeader.tsx | 7 +- src/components/AnimatedList/AnimatedList.tsx | 12 +- .../ArticlePreview/ArticlePreview.tsx | 13 ++- .../ArticlePreviewRow/ArticlePreviewRow.tsx | 19 +++- .../HeroSwitching/HeroSwitching.tsx | 8 +- src/components/LinkedCard/LinkedCard.tsx | 2 +- .../OfferPageWrapper/OfferPageWrapper.tsx | 2 +- src/components/PricingHero/PricingHero.tsx | 10 +- .../ProcessIntegration/ProcessIntegration.tsx | 90 ++++++++++----- .../StartTestingWithReportPortal.tsx | 106 ++++++++++++------ .../StatisticList/StatisticList.tsx | 51 +++++++-- .../SubscriptionBanner/SubscriptionBanner.tsx | 46 +++++--- .../WhyInstanceBlocks/WhyInstanceBlocks.tsx | 6 +- src/containers/BlogPage/BlogPage.tsx | 4 +- src/containers/FeaturesPage/FeaturesPage.tsx | 4 +- .../InstallationPage/InstallationPage.tsx | 2 +- .../ScrollIndicator/ScrollIndicator.tsx | 2 +- .../ScrollIndicator/ScrollItem/ScrollItem.tsx | 2 +- .../CustomersStatistics.tsx | 36 +++++- .../LandingPage/HowItWorks/HowItWorks.tsx | 6 +- src/containers/LandingPage/LandingPage.tsx | 8 +- .../LatestFromOurBlog/LatestFromOurBlog.tsx | 18 ++- .../LandingPage/Showcase/Showcase.tsx | 10 +- .../SassPage/PricingCards/PricingCards.tsx | 5 +- src/hooks/useMotionEnterAnimation.ts | 6 +- src/utils/types.ts | 2 + 26 files changed, 326 insertions(+), 151 deletions(-) diff --git a/src/components/AnimatedHeader/AnimatedHeader.tsx b/src/components/AnimatedHeader/AnimatedHeader.tsx index 6abd05146..c5abddc4c 100644 --- a/src/components/AnimatedHeader/AnimatedHeader.tsx +++ b/src/components/AnimatedHeader/AnimatedHeader.tsx @@ -2,17 +2,16 @@ import { createElement, FC, PropsWithChildren } from 'react'; import { motion, Transition } from 'framer-motion'; import { useInView } from '@app/hooks/useInView'; import { useMotionEnterAnimation } from '@app/hooks/useMotionEnterAnimation'; -import { easeInOutTransition, opacityScaleAnimationProps } from '@app/utils'; +import { easeInOutTransition, opacityScaleAnimationProps, PropsWithAnimation } from '@app/utils'; interface AnimatedHeaderProps { transition?: Transition; delay?: number; headerLevel?: 1 | 2 | 3 | 4 | 5 | 6; className?: string; - isAnimationEnabled?: boolean; } -export const AnimatedHeader: FC> = ({ +export const AnimatedHeader: FC>> = ({ transition = easeInOutTransition, delay, headerLevel = 2, @@ -29,7 +28,7 @@ export const AnimatedHeader: FC> = ({ return createElement( motion[`h${headerLevel}`], - { className, ref: titleRef, ...getAnimation({ inView: isTitleInView, delay }) }, + { className, ref: titleRef, ...getAnimation({ isInView: isTitleInView, delay }) }, children, ); }; diff --git a/src/components/AnimatedList/AnimatedList.tsx b/src/components/AnimatedList/AnimatedList.tsx index beef06a9b..940fa5301 100644 --- a/src/components/AnimatedList/AnimatedList.tsx +++ b/src/components/AnimatedList/AnimatedList.tsx @@ -74,7 +74,9 @@ export const AnimatedList: FC = ({
{title} - {subtitle} + + {subtitle} +
= ({ className={getBlocksWithList('__item')} key={itemTitle} onClick={() => setIndexAndResetInterval(index)} - {...getCardAnimation({ inView, delay: 0.2 * index })} + {...getCardAnimation({ isInView: inView, delay: 0.2 * index })} > {itemTitle} @@ -97,7 +99,7 @@ export const AnimatedList: FC = ({ = ({ key={image} alt="" {...getImageAnimation({ - inView, + isInView: inView, delay: isFirstImageAnimationPlayed.current ? 0 : 0.6, additionalEffects: { transitionAdditional: { @@ -134,7 +136,7 @@ export const AnimatedList: FC = ({
{children} diff --git a/src/components/ArticlePreview/ArticlePreview.tsx b/src/components/ArticlePreview/ArticlePreview.tsx index be0fcc8fe..e4af07e5b 100644 --- a/src/components/ArticlePreview/ArticlePreview.tsx +++ b/src/components/ArticlePreview/ArticlePreview.tsx @@ -2,7 +2,7 @@ import React, { FC } from 'react'; import isEmpty from 'lodash/isEmpty'; import chunk from 'lodash/chunk'; import { useMediaQuery } from 'react-responsive'; -import { BlogPostDto, MEDIA_DESKTOP_SM, MEDIA_TABLET_SM } from '@app/utils'; +import { BlogPostDto, MEDIA_DESKTOP_SM, MEDIA_TABLET_SM, PropsWithAnimation } from '@app/utils'; import { ArticlePreviewRow } from './ArticlePreviewRow'; @@ -10,6 +10,7 @@ import './ArticlePreview.scss'; interface ArticlePreviewProps { posts: BlogPostDto[]; + hasFixedItemsPerRow?: boolean; } const getItemsPerRow = (isDesktop: boolean, isTablet: boolean) => { @@ -24,15 +25,19 @@ const getItemsPerRow = (isDesktop: boolean, isTablet: boolean) => { return 1; }; -export const ArticlePreview: FC = ({ posts }) => { +export const ArticlePreview: FC> = ({ + posts, + hasFixedItemsPerRow = false, + isAnimationEnabled = false, +}) => { const isDesktop = useMediaQuery({ query: MEDIA_DESKTOP_SM }); const isTablet = useMediaQuery({ query: MEDIA_TABLET_SM }); - const rows = chunk(posts, getItemsPerRow(isDesktop, isTablet)); + const rows = chunk(posts, hasFixedItemsPerRow ? 3 : getItemsPerRow(isDesktop, isTablet)); return !isEmpty(posts) ? (
    {rows.map((row, rowIndex) => ( - + ))}
) : null; diff --git a/src/components/ArticlePreview/ArticlePreviewRow/ArticlePreviewRow.tsx b/src/components/ArticlePreview/ArticlePreviewRow/ArticlePreviewRow.tsx index 0f1c94109..3a37d9ab6 100644 --- a/src/components/ArticlePreview/ArticlePreviewRow/ArticlePreviewRow.tsx +++ b/src/components/ArticlePreview/ArticlePreviewRow/ArticlePreviewRow.tsx @@ -5,6 +5,7 @@ import { createBemBlockBuilder, getEaseInOutTransition, opacityScaleAnimationProps, + PropsWithAnimation, } from '@app/utils'; import { useInView } from '@app/hooks/useInView'; import { useMotionEnterAnimation } from '@app/hooks/useMotionEnterAnimation'; @@ -17,19 +18,25 @@ interface ArticlePreviewRowProps { const getBlocksWith = createBemBlockBuilder(['article-preview-list']); -export const ArticlePreviewRow: FC = ({ row }) => { +export const ArticlePreviewRow: FC> = ({ + row, + isAnimationEnabled = false, +}) => { const [rowRef, isInView] = useInView(); - const getAnimation = useMotionEnterAnimation({ - ...opacityScaleAnimationProps, - ...getEaseInOutTransition(0.7), - }); + const getAnimation = useMotionEnterAnimation( + { + ...opacityScaleAnimationProps, + ...getEaseInOutTransition(0.7), + }, + isAnimationEnabled, + ); return ( void; subtitle?: string; isHeroInView?: boolean; - isAnimationEnabled?: boolean; children?: ReactNode; } const getBlocksWith = createBemBlockBuilder(['hero-switching']); -export const HeroSwitching: FC = ({ +export const HeroSwitching: FC> = ({ title, subtitle, buttons, @@ -55,7 +55,7 @@ export const HeroSwitching: FC = ({ {subtitle && ( {subtitle} @@ -63,7 +63,7 @@ export const HeroSwitching: FC = ({ {children} diff --git a/src/components/LinkedCard/LinkedCard.tsx b/src/components/LinkedCard/LinkedCard.tsx index bc374c7db..40c5745d8 100644 --- a/src/components/LinkedCard/LinkedCard.tsx +++ b/src/components/LinkedCard/LinkedCard.tsx @@ -20,7 +20,7 @@ export const LinkedCard: FC = ({ itemTitle, description, link, - linkText, + linkText = '', icon, delay = false, }) => { diff --git a/src/components/OfferPageWrapper/OfferPageWrapper.tsx b/src/components/OfferPageWrapper/OfferPageWrapper.tsx index 140084036..341c4e39f 100644 --- a/src/components/OfferPageWrapper/OfferPageWrapper.tsx +++ b/src/components/OfferPageWrapper/OfferPageWrapper.tsx @@ -98,7 +98,7 @@ export const OfferPageWrapper: FC = ({ className={getBlocksWith('__pentagons')} ref={cardsRef} {...getCardsAnimation({ - inView: areCardsInView, + isInView: areCardsInView, delay: 0.6, additionalEffects: { hiddenAdditional: { diff --git a/src/components/PricingHero/PricingHero.tsx b/src/components/PricingHero/PricingHero.tsx index 9fe7ded66..e8603fb48 100644 --- a/src/components/PricingHero/PricingHero.tsx +++ b/src/components/PricingHero/PricingHero.tsx @@ -7,6 +7,7 @@ import { easeInOutOpacityScaleAnimationProps, easeInOutTransition, heroBackgroundAnimationProps, + PropsWithAnimation, } from '@app/utils'; import { useInView } from '@app/hooks/useInView'; import { useMotionEnterAnimation } from '@app/hooks/useMotionEnterAnimation'; @@ -23,7 +24,6 @@ interface PricingHeroProps { description: string; switcherProps: PlanTypeSwitcherProps; subtitle?: string; - isAnimationEnabled?: boolean; } const getBlocksWith = createBemBlockBuilder(['pricing-hero']); @@ -40,7 +40,7 @@ const offerDescriptionAnimation = { ...easeInOutTransition, }; -export const PricingHero: FC = ({ +export const PricingHero: FC> = ({ title, subtitle, buttons, @@ -69,7 +69,7 @@ export const PricingHero: FC = ({ = ({
{offerType}
@@ -91,7 +91,7 @@ export const PricingHero: FC = ({ { - const { integrations } = useHomePage(); +export const ProcessIntegration = forwardRef( + ({ isAnimationEnabled = false }, ref) => { + const { integrations } = useHomePage(); + const [containerRef, isInView] = useInView(); - return ( -
-
-

Integrate with your existing test automation process

-

- Integrate ReportPortal with frameworks, bug tracking systems, infrastructure providers you - already use and others so as to streamline the development and testing processes -

-
- - See all integrations - + const getButtonAnimation = useMotionEnterAnimation( + { + ...opacityScaleAnimationProps, + ...easeInOutTransition, + }, + isAnimationEnabled, + ); + + return ( +
+
+ + Integrate with your existing test automation process + + + Integrate ReportPortal with frameworks, bug tracking systems, infrastructure providers + you already use and others so as to streamline the development and testing processes + + + + See all integrations + + +
+
+ + {[integrations, integrations].flat().map((slide, index) => ( +
+ {slide.alt} +
+ ))} +
-
-
- - {[integrations, integrations].flat().map((slide, index) => ( -
- {slide.alt} -
- ))} -
-
-
- ); -}); +
+ ); + }, +); ProcessIntegration.displayName = 'ProcessIntegration'; diff --git a/src/components/StartTestingWithReportPortal/StartTestingWithReportPortal.tsx b/src/components/StartTestingWithReportPortal/StartTestingWithReportPortal.tsx index 261d9e12d..d6fee2bc4 100644 --- a/src/components/StartTestingWithReportPortal/StartTestingWithReportPortal.tsx +++ b/src/components/StartTestingWithReportPortal/StartTestingWithReportPortal.tsx @@ -1,7 +1,16 @@ import React, { FC } from 'react'; import classNames from 'classnames'; +import { motion } from 'framer-motion'; import { Link } from '@app/components/Link'; -import { createBemBlockBuilder, iconsCommon } from '@app/utils'; +import { + createBemBlockBuilder, + getEaseInOutTransition, + iconsCommon, + opacityScaleAnimationProps, + PropsWithAnimation, +} from '@app/utils'; +import { useInView } from '@app/hooks/useInView'; +import { useMotionEnterAnimation } from '@app/hooks/useMotionEnterAnimation'; import './StartTestingWithReportPortal.scss'; @@ -11,37 +20,68 @@ interface StartTestingWithReportPortalProps { const getBlocksWith = createBemBlockBuilder(['start-testing-with-report-portal']); -export const StartTestingWithReportPortal: FC = ({ - startFreeTrialUrl = '/contact-us/general', -}) => ( -
-
-
-

Start testing with ReportPortal

-

- Ready to get the most of ReportPortal features? Start your 30-day free trial or get - in touch with us to learn more about our offer. -

-
-
- - Start free trial - - +> = ({ startFreeTrialUrl = '/contact-us/general', isAnimationEnabled = false }) => { + const [ref, isInView] = useInView(); + + const getContentAnimation = useMotionEnterAnimation( + { + hiddenState: { + opacity: 0, + x: -50, + }, + enterState: { + opacity: 1, + x: 0, + }, + ...getEaseInOutTransition(0.7), + }, + isAnimationEnabled, + ); + const getImageAnimation = useMotionEnterAnimation( + { + ...opacityScaleAnimationProps, + ...getEaseInOutTransition(1), + }, + isAnimationEnabled, + ); + + return ( +
+
+
+ + Start testing with ReportPortal + + + Ready to get the most of ReportPortal features? Start your 30-day free trial or + get in touch with us to learn more about our offer. + +
+ - Get a quote - + + Start free trial + + + Get a quote + + +
+
+
-
-
- -
-
-); + + ); +}; diff --git a/src/components/StatisticList/StatisticList.tsx b/src/components/StatisticList/StatisticList.tsx index c65bb7f9c..46d6d48d6 100644 --- a/src/components/StatisticList/StatisticList.tsx +++ b/src/components/StatisticList/StatisticList.tsx @@ -1,4 +1,7 @@ import React, { FC } from 'react'; +import { motion } from 'framer-motion'; +import { useMotionEnterAnimation } from '@app/hooks/useMotionEnterAnimation'; +import { getEaseInOutTransition, PropsWithAnimation } from '@app/utils'; import './StatisticList.scss'; @@ -8,16 +11,42 @@ interface StatisticListProps { entities: string; achievement?: string; }[]; + isInView?: boolean; } -export const StatisticList: FC = ({ statistics }) => ( -
    - {statistics.map(({ quantity, entities, achievement }) => ( -
  • - {quantity} - {entities} - {achievement && {achievement}} -
  • - ))} -
-); +export const StatisticList: FC> = ({ + statistics, + isInView = true, + isAnimationEnabled = false, +}) => { + const getAnimation = useMotionEnterAnimation( + { + hiddenState: { + opacity: 0, + x: -150, + }, + enterState: { + opacity: 1, + x: 0, + }, + ...getEaseInOutTransition(0.5), + }, + isAnimationEnabled, + ); + + return ( +
    + {statistics.map(({ quantity, entities, achievement }, index) => ( + + {quantity} + {entities} + {achievement && {achievement}} + + ))} +
+ ); +}; diff --git a/src/components/SubscriptionBanner/SubscriptionBanner.tsx b/src/components/SubscriptionBanner/SubscriptionBanner.tsx index 879119f8e..7e9f2aa53 100644 --- a/src/components/SubscriptionBanner/SubscriptionBanner.tsx +++ b/src/components/SubscriptionBanner/SubscriptionBanner.tsx @@ -1,24 +1,44 @@ -import React from 'react'; +import React, { FC } from 'react'; +import { motion } from 'framer-motion'; import { FooterContent } from '@app/components/Layout'; import { SubscriptionForm } from '@app/components/SubscriptionForm'; import { Banner } from '@app/components/Banner'; -import { createBemBlockBuilder } from '@app/utils'; +import { + createBemBlockBuilder, + getEaseInOutTransition, + opacityScaleAnimationProps, + PropsWithAnimation, +} from '@app/utils'; +import { useMotionEnterAnimation } from '@app/hooks/useMotionEnterAnimation'; +import { useInView } from '@app/hooks/useInView'; import './SubscriptionBanner.scss'; const getBlocksWith = createBemBlockBuilder(['subscription-banner']); -export const SubscriptionBanner = () => { +export const SubscriptionBanner: FC = ({ isAnimationEnabled = false }) => { + const [ref, isInView] = useInView(); + + const getAnimation = useMotionEnterAnimation( + { + ...opacityScaleAnimationProps, + ...getEaseInOutTransition(0.7), + }, + isAnimationEnabled, + ); + return ( - -
- - - -
-
+
+ + + + + + + +
); }; diff --git a/src/components/WhyInstanceBlocks/WhyInstanceBlocks.tsx b/src/components/WhyInstanceBlocks/WhyInstanceBlocks.tsx index 0b258cc46..593af3880 100644 --- a/src/components/WhyInstanceBlocks/WhyInstanceBlocks.tsx +++ b/src/components/WhyInstanceBlocks/WhyInstanceBlocks.tsx @@ -51,7 +51,7 @@ export const WhyInstanceBlocks: FC = ({
  • @@ -59,7 +59,7 @@ export const WhyInstanceBlocks: FC = ({ = ({ = ({ visiblePosts, allPosts, loadMorePo return ( <> - +
    = ({ visiblePosts, allPosts, loadMorePo > Product updates, news and technology articles - + {visiblePosts.length < allPosts.length && (