From a9fd6bb37928c62bad1b2c82ea06fb2b75c39489 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Thu, 30 Sep 2021 15:27:51 +0300 Subject: [PATCH 1/6] init code for #5721 --- .../components/__snapshots__/Content.jsx.snap | 10 + src/assets/images/member-path/icon-left.svg | 19 ++ src/assets/images/member-path/icon-right.svg | 13 + .../mock-data/member-path-selector-data.json | 40 +++ src/shared/components/Content/index.jsx | 10 + .../Contentful/AppComponent/index.jsx | 9 + .../MemberPath/PathSelector/index.jsx | 133 ++++++++++ .../MemberPath/PathSelector/styles.scss | 244 ++++++++++++++++++ .../examples/MemberPathSelector/index.jsx | 15 ++ .../examples/MemberPathSelector/styles.scss | 8 + src/shared/routes/Examples/Examples.jsx | 2 + 11 files changed, 503 insertions(+) create mode 100644 src/assets/images/member-path/icon-left.svg create mode 100644 src/assets/images/member-path/icon-right.svg create mode 100644 src/assets/mock-data/member-path-selector-data.json create mode 100644 src/shared/components/MemberPath/PathSelector/index.jsx create mode 100644 src/shared/components/MemberPath/PathSelector/styles.scss create mode 100644 src/shared/components/examples/MemberPathSelector/index.jsx create mode 100644 src/shared/components/examples/MemberPathSelector/styles.scss diff --git a/__tests__/shared/components/__snapshots__/Content.jsx.snap b/__tests__/shared/components/__snapshots__/Content.jsx.snap index 86d90bc2d2..dbb34f7656 100644 --- a/__tests__/shared/components/__snapshots__/Content.jsx.snap +++ b/__tests__/shared/components/__snapshots__/Content.jsx.snap @@ -1039,6 +1039,16 @@ exports[`Matches shallow shapshot 1`] = ` - Most of Topcoder websites use the same styleguide for the design. In particular, all typography is supposed to use the set of styles from this example. For all these styles we have global mixins, which should be used whenever possible (but never modified without explicit approval). +
  • + + Member Path - Path Selector + + + — Demo for path selector component on member path page +
  • `; diff --git a/src/assets/images/member-path/icon-left.svg b/src/assets/images/member-path/icon-left.svg new file mode 100644 index 0000000000..8261f5640c --- /dev/null +++ b/src/assets/images/member-path/icon-left.svg @@ -0,0 +1,19 @@ + + + 1EF4CE06-1B7B-47E8-B32B-876E51A166B9 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/member-path/icon-right.svg b/src/assets/images/member-path/icon-right.svg new file mode 100644 index 0000000000..8f44a1718c --- /dev/null +++ b/src/assets/images/member-path/icon-right.svg @@ -0,0 +1,13 @@ + + + 2EEB4162-14EC-4DD7-A445-058D8E648D53 + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/mock-data/member-path-selector-data.json b/src/assets/mock-data/member-path-selector-data.json new file mode 100644 index 0000000000..7091a0dab1 --- /dev/null +++ b/src/assets/mock-data/member-path-selector-data.json @@ -0,0 +1,40 @@ +{ + "title": "WHAT DO YOU WANT TO DO TODAY?", + "items": [ + { + "title": "COMPETE", + "iconURL": "https://media-bucket-s3.s3.eu-central-1.amazonaws.com/sx/icon-compete.svg", + "activeIconURL": "https://s3.eu-central-1.amazonaws.com/media-bucket-s3/sx/icon-compete-active.svg", + "contentText": "A competition based, gig economy, a typical 9-5 job for you", + "btnText": "Topcoder Challenges", + "btnURL": "https://www.topcoder.com/challenges", + "btnNewTab": true + }, { + "title": "BUILD SKILLS", + "iconURL": "https://media-bucket-s3.s3.eu-central-1.amazonaws.com/sx/icon-build-skills.svg", + "activeIconURL": "https://s3.eu-central-1.amazonaws.com/media-bucket-s3/sx/icon-build-skills-active.svg", + "contentText": "Get stronger with Skill Builder Competitions", + "btnText": "Find a Competition", + "btnURL": "https://www.topcoder.com/challenges?bucket=openForRegistration&search=Topcoder%20Skill%20Builder%20Competition&tracks[DS]=true&tracks[Des]=true&tracks[Dev]=true&tracks[QA]=true&types[]=CH&types[]=F2F&types[]=TSK", + "btnNewTab": true + }, + { + "title": "LEARN", + "iconURL": "https://s3.eu-central-1.amazonaws.com/media-bucket-s3/sx/icon-learn.svg", + "activeIconURL": "https://s3.eu-central-1.amazonaws.com/media-bucket-s3/sx/icon-learn-active.svg", + "contentText": "Tutorials and workshops that matter", + "btnText": "Thrive", + "btnURL": "https://www.topcoder.com/thrive", + "btnNewTab": true + }, + { + "title": "GET A GIG", + "iconURL": "https://s3.eu-central-1.amazonaws.com/media-bucket-s3/sx/icon-gig.svg", + "activeIconURL": "https://s3.eu-central-1.amazonaws.com/media-bucket-s3/sx/icon-get-a-gig-active.svg", + "contentText": "Find a freelance job with our help", + "btnText": "Gig Work", + "btnURL": "https://www.topcoder.com/gigs", + "btnNewTab": true + } + ] +} diff --git a/src/shared/components/Content/index.jsx b/src/shared/components/Content/index.jsx index 3331309480..f8f7ea48f7 100644 --- a/src/shared/components/Content/index.jsx +++ b/src/shared/components/Content/index.jsx @@ -840,6 +840,16 @@ export default function Content() { For all these styles we have global mixins, which should be used whenever possible (but never modified without explicit approval). +
  • + + Member Path - Path Selector + + {' '} + — + Demo for path selector component on member path page +
  • ); diff --git a/src/shared/components/Contentful/AppComponent/index.jsx b/src/shared/components/Contentful/AppComponent/index.jsx index 5d19f81fee..a012fb82e9 100644 --- a/src/shared/components/Contentful/AppComponent/index.jsx +++ b/src/shared/components/Contentful/AppComponent/index.jsx @@ -12,6 +12,7 @@ import Leaderboard from 'containers/tco/Leaderboard'; import RecruitCRMJobs from 'containers/Gigs/RecruitCRMJobs'; import EmailSubscribeForm from 'containers/EmailSubscribeForm'; import GSheet from 'containers/GSheet'; +import PathSelector from 'components/MemberPath/PathSelector'; const { fireErrorMessage } = errors; @@ -43,6 +44,14 @@ export function AppComponentSwitch(appComponent) { if (appComponent.fields.type === 'GSheet') { return ; } + if (appComponent.fields.type === 'MemberPath') { + return ( + + ); + } fireErrorMessage(`Unsupported app component type ${appComponent.fields.type}`, ''); return null; } diff --git a/src/shared/components/MemberPath/PathSelector/index.jsx b/src/shared/components/MemberPath/PathSelector/index.jsx new file mode 100644 index 0000000000..ae16a7c202 --- /dev/null +++ b/src/shared/components/MemberPath/PathSelector/index.jsx @@ -0,0 +1,133 @@ +import PT from 'prop-types'; +import React, { useEffect, useState } from 'react'; +import cn from 'classnames'; +import ArrowLeftIcon from 'assets/images/member-path/icon-left.svg'; +import ArrowRightIcon from 'assets/images/member-path/icon-right.svg'; +import styles from './styles.scss'; + +const colorStyles = [styles.purple, styles.blue, styles.teal, styles.orange]; + +export default function PathSelector({ + data, + animationTime, +}) { + const [activeItemIndex, setActiveItemIndex] = useState(0); + const [timer, setTimer] = useState(null); + + useEffect( + () => { + setTimer(setInterval(() => { + setActiveItemIndex(itemIndex => (itemIndex + 1) % data.items.length); + }, + animationTime)); + return () => { + if (timer) { + clearInterval(timer); + } + }; + }, + [], + ); + const stopTimer = () => { + if (timer) { + clearInterval(timer); + setTimer(null); + } + }; + + const handleOptionSelect = index => () => { + setActiveItemIndex(index); + // Stop the animation if user selects an item + stopTimer(); + }; + + const handlePrevButton = () => { + setActiveItemIndex(activeItemIndex - 1 < 0 ? data.items.length - 1 : activeItemIndex - 1); + // Stop the animation if user selects an item + stopTimer(); + }; + + const handleNextButton = () => { + setActiveItemIndex((activeItemIndex + 1) % data.items.length); + // Stop the animation if user selects an item + stopTimer(); + }; + + const items = data.items.map((item, index) => ( + + )); + + return ( +
    +
    +

    {data.title}

    +
    + + {items} + +
    +
    +
    +
    + {data.items[activeItemIndex].contentText} +
    + + {data.items[activeItemIndex].btnText} + +
    +
    + ); +} + +PathSelector.defaultProps = { + animationTime: 3000, +}; + +PathSelector.propTypes = { + data: PT.shape({ + items: PT.arrayOf(PT.shape({ + title: PT.string, + iconURL: PT.string, + activeIconURL: PT.string, + contentText: PT.string, + btnText: PT.string, + btnURL: PT.string, + btnNewTab: PT.bool, + })), + title: PT.string, + }).isRequired, + animationTime: PT.number, +}; diff --git a/src/shared/components/MemberPath/PathSelector/styles.scss b/src/shared/components/MemberPath/PathSelector/styles.scss new file mode 100644 index 0000000000..6839b9ec68 --- /dev/null +++ b/src/shared/components/MemberPath/PathSelector/styles.scss @@ -0,0 +1,244 @@ +@import "~styles/mixins"; +@import "~components/buttons/themed/tc"; + +.container { + width: 100%; + height: 618px; + + @include xs-to-sm { + height: 504px; + } +} + +.header { + background-color: #011423; + border-top-left-radius: 10px; + border-top-right-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + padding: 50px 32px; + height: 380px; + + @include xs-to-sm { + padding: 20px 32px 28px 32px; + height: 298px; + } +} + +.title { + @include barlow-condensed-medium; + + color: $tc-white; + margin-bottom: 61px; + font-size: 48px; + text-align: center; + + @include xs-to-sm { + margin-bottom: 20px; + font-size: 31px; + line-height: 33px; + } +} + +.options { + display: flex; + align-items: center; + justify-content: center; + + .arrowIcon { + display: none; + } + + @include xs-to-sm { + .arrowIcon { + display: block; + margin: 16px; + } + } +} + +.option { + @include barlow-condensed-semi-bold; + + font-size: 24px; + width: 168px; + height: 168px; + border-radius: 12px; + margin: 0 8px; + border: none; + background: $tc-white; + color: $tc-gray-70; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + + @include xs-to-sm { + &:not(.active) { + display: none; + } + } + + .icon, + .activeIcon { + display: block; + content: ''; + height: 64px; + width: 64px; + margin-bottom: 19px; + } + + .activeIcon { + display: none; + } + + &.active { + color: $tc-white; + display: flex; + + .activeIcon { + display: block; + } + + .icon { + display: none; + } + + @include md-to-xl { + &.purple { + background: linear-gradient(305deg, #9d41c9 0%, #ef476f 100%); + + &::before { + background: linear-gradient(305.22deg, #e6477a 0.01%, #e3467c 100%); + } + } + + &.blue { + background: linear-gradient(90deg, #26b3c5 0%, #2984bd 100%); + + &::before { + background: linear-gradient(90deg, #27a2c2 0%, #27a1c1 100%); + } + } + + &.teal { + background: linear-gradient(270deg, #26b3c5 0%, #06d6a0 100%); + + &::before { + background: linear-gradient(270deg, #1ac0b7 0%, #1ac0b8 100%); + } + } + + &.orange { + background: linear-gradient(90deg, #ffc43d 0%, #f37593 100%); + + &::before { + background: linear-gradient(86.76deg, #f58186 0%, #f47e89 100%); + } + } + } + + &::after { + content: ''; + display: block; + position: absolute; + bottom: -58px; + width: 32px; + height: 16px; + border-left: 16px solid #011423; + border-right: 16px solid #011423; + border-top: 16px solid #011423; + border-bottom: 16px solid transparent; + } + + &::before { + content: ''; + display: block; + position: absolute; + bottom: -59px; + width: 31px; + height: 17px; + } + + @include xs-to-sm { + background: $tc-white; + color: $tc-gray-70; + + &::before, + &::after { + display: none; + } + + .icon { + display: block; + } + + .activeIcon { + display: none; + } + } + } +} + +.content { + background: linear-gradient(305deg, #9d41c9 0%, #ef476f 100%); + height: 238px; + width: 100%; + display: flex; + align-items: center; + justify-content: flex-end; + flex-direction: column; + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + padding: 68px 0; + + &.purple { + background: linear-gradient(305deg, #9d41c9 0%, #ef476f 100%); + } + + &.blue { + background: linear-gradient(90deg, #26b3c5 0%, #2984bd 100%); + } + + &.teal { + background: linear-gradient(270deg, #26b3c5 0%, #06d6a0 100%); + } + + &.orange { + background: linear-gradient(90deg, #ffc43d 0%, #f37593 100%); + } + + @include xs-to-sm { + height: 206px; + padding: 30px 0; + } +} + +.contentText { + @include roboto-regular; + + color: $tc-white; + font-size: 24px; + line-height: 29px; + margin-bottom: 26px; + padding: 0 40px; + text-align: center; + flex: 1; + display: flex; + align-items: center; + justify-content: center; + + @include xs-to-sm { + margin-bottom: 15px; + font-size: 20px; + line-height: 30px; + } +} + +.contentButton { + @include primary-borderless; + @include md; +} diff --git a/src/shared/components/examples/MemberPathSelector/index.jsx b/src/shared/components/examples/MemberPathSelector/index.jsx new file mode 100644 index 0000000000..cb9d614720 --- /dev/null +++ b/src/shared/components/examples/MemberPathSelector/index.jsx @@ -0,0 +1,15 @@ +import PathSelector from 'components/MemberPath/PathSelector'; +import React from 'react'; +import mockData from 'assets/mock-data/member-path-selector-data.json'; +import './styles.scss'; + +export default function MemberPathSelectorExample() { + return ( +
    +

    + Member Path - Path Selector Component Preview +

    + +
    + ); +} diff --git a/src/shared/components/examples/MemberPathSelector/styles.scss b/src/shared/components/examples/MemberPathSelector/styles.scss new file mode 100644 index 0000000000..7ee3b6eee8 --- /dev/null +++ b/src/shared/components/examples/MemberPathSelector/styles.scss @@ -0,0 +1,8 @@ +@import "~styles/mixins"; + +.container { + background: white; + margin: 0 auto; + max-width: 800px; + padding: 8px; +} diff --git a/src/shared/routes/Examples/Examples.jsx b/src/shared/routes/Examples/Examples.jsx index a266bfe53a..3d33206a37 100644 --- a/src/shared/routes/Examples/Examples.jsx +++ b/src/shared/routes/Examples/Examples.jsx @@ -32,6 +32,7 @@ import TracksTreeExample from 'components/examples/TracksTree'; import TracksFilterExample from 'components/examples/TracksFilter'; import SearchPageFilterExample from 'components/examples/SearchPageFilter'; import GUIKit from 'components/examples/GUIKit'; +import MemberPathSelectorExample from 'components/examples/MemberPathSelector'; import { Switch, @@ -98,6 +99,7 @@ export default function Examples({ + ); From f67e5d6ebf4a839b802a58f6f784508250f2961f Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Fri, 1 Oct 2021 11:18:07 +0300 Subject: [PATCH 2/6] add header to /start --- src/shared/routes/index.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/shared/routes/index.jsx b/src/shared/routes/index.jsx index 3468ce0f0b..7ffc2b2642 100644 --- a/src/shared/routes/index.jsx +++ b/src/shared/routes/index.jsx @@ -134,13 +134,14 @@ function Routes({ communityId }) { /> ( - +
    +
    - +
    )} exact path={config.START_PAGE_PATH} From b9e3f739ab53c1222ad0605f008a2ad259bdcb22 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Fri, 1 Oct 2021 11:19:06 +0300 Subject: [PATCH 3/6] ci: on qa --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 349c4c5d3c..56994640cb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -356,7 +356,7 @@ workflows: filters: branches: only: - - free + - member-path-component # This is beta env for production soft releases - "build-prod-beta": context : org-global From 192f6d41511f45cd9acaee1b2d22e0c9544422af Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Fri, 1 Oct 2021 11:51:07 +0300 Subject: [PATCH 4/6] fixed height in desktop --- src/shared/components/MemberPath/PathSelector/styles.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/components/MemberPath/PathSelector/styles.scss b/src/shared/components/MemberPath/PathSelector/styles.scss index 6839b9ec68..1764e020ad 100644 --- a/src/shared/components/MemberPath/PathSelector/styles.scss +++ b/src/shared/components/MemberPath/PathSelector/styles.scss @@ -193,7 +193,7 @@ flex-direction: column; border-bottom-left-radius: 10px; border-bottom-right-radius: 10px; - padding: 68px 0; + padding: 41px 0 49px; &.purple { background: linear-gradient(305deg, #9d41c9 0%, #ef476f 100%); From 56e2bc7b60f0a8187e649b2d4e0171c64b510e1e Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Fri, 1 Oct 2021 12:15:00 +0300 Subject: [PATCH 5/6] removed fixed heights --- src/shared/components/MemberPath/PathSelector/styles.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/shared/components/MemberPath/PathSelector/styles.scss b/src/shared/components/MemberPath/PathSelector/styles.scss index 1764e020ad..9178cf0885 100644 --- a/src/shared/components/MemberPath/PathSelector/styles.scss +++ b/src/shared/components/MemberPath/PathSelector/styles.scss @@ -185,7 +185,6 @@ .content { background: linear-gradient(305deg, #9d41c9 0%, #ef476f 100%); - height: 238px; width: 100%; display: flex; align-items: center; @@ -212,7 +211,6 @@ } @include xs-to-sm { - height: 206px; padding: 30px 0; } } From cda85665677af2c5baf64fb350db72b3582d94f3 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Mon, 4 Oct 2021 22:45:53 +0300 Subject: [PATCH 6/6] fix iOS bug with content prop --- src/shared/components/MemberPath/PathSelector/styles.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/components/MemberPath/PathSelector/styles.scss b/src/shared/components/MemberPath/PathSelector/styles.scss index 9178cf0885..c59e1008cd 100644 --- a/src/shared/components/MemberPath/PathSelector/styles.scss +++ b/src/shared/components/MemberPath/PathSelector/styles.scss @@ -85,10 +85,10 @@ .icon, .activeIcon { display: block; - content: ''; height: 64px; width: 64px; margin-bottom: 19px; + content: unset; } .activeIcon {