diff --git a/package-lock.json b/package-lock.json index d8db2c8..34aabda 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5510,6 +5510,11 @@ } } }, + "css-mediaquery": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz", + "integrity": "sha1-aiw3NEkoYYYxxUvTPO3TAdoYvqA=" + }, "css-modules-loader-core": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz", @@ -8490,6 +8495,11 @@ "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true }, + "hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -12762,6 +12772,14 @@ } } }, + "matchmediaquery": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.3.1.tgz", + "integrity": "sha512-Hlk20WQHRIm9EE9luN1kjRjYXAQToHOIAHPJn9buxBwuhfTHoKUcX+lXBbxc85DVQfXYbEQ4HcwQdd128E3qHQ==", + "requires": { + "css-mediaquery": "^0.1.2" + } + }, "material-colors": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", @@ -15121,6 +15139,17 @@ "react-is": "^16.13.1" } }, + "react-responsive": { + "version": "9.0.0-beta.5", + "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-9.0.0-beta.5.tgz", + "integrity": "sha512-Zvikc/28FsabQ4caLP1wIQlRPXBelYMct6dnUEwTRI4P7jH5d9x8RAMb1SbAZ0IdZGQHQ06aSRvhhg/tvqXktA==", + "requires": { + "hyphenate-style-name": "^1.0.0", + "matchmediaquery": "^0.3.0", + "prop-types": "^15.6.1", + "shallow-equal": "^1.2.1" + } + }, "react-router": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz", diff --git a/package.json b/package.json index 58c092c..e34ff3e 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "react-dom": "^16.12.0", "react-redux": "^7.2.3", "react-select": "^1.3.0", + "react-responsive": "^9.0.0-beta.5", "redux": "^4.0.5", "redux-actions": "^2.6.5", "redux-logger": "^3.0.6", diff --git a/src/App.jsx b/src/App.jsx index adbafb7..05136ff 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -17,7 +17,10 @@ const App = () => { varsRef.current = { previousLocation }; useEffect(() => { - if (location.pathname !== varsRef.current.previousLocation.pathname) { + if ( + location.pathname !== varsRef.current.previousLocation.pathname || + location.search !== varsRef.current.previousLocation.search + ) { window.scrollTo(0, 0); } }, [location]); diff --git a/src/assets/icons/close-gray.svg b/src/assets/icons/close-gray.svg new file mode 100644 index 0000000..d773daa --- /dev/null +++ b/src/assets/icons/close-gray.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/social/icon_email.svg b/src/assets/images/social/icon_email.svg index 7a8b2e7..1281306 100644 --- a/src/assets/images/social/icon_email.svg +++ b/src/assets/images/social/icon_email.svg @@ -1,5 +1,5 @@ - icon email + email diff --git a/src/assets/images/social/icon_facebook.svg b/src/assets/images/social/icon_facebook.svg index 4fb1b6e..fd89ed4 100644 --- a/src/assets/images/social/icon_facebook.svg +++ b/src/assets/images/social/icon_facebook.svg @@ -2,7 +2,7 @@ - icon facebook + facebook diff --git a/src/assets/images/social/icon_plus.svg b/src/assets/images/social/icon_plus.svg index deca5c7..7567503 100644 --- a/src/assets/images/social/icon_plus.svg +++ b/src/assets/images/social/icon_plus.svg @@ -1,7 +1,7 @@ - icon + + + Created with Sketch. diff --git a/src/assets/images/social/icon_print.svg b/src/assets/images/social/icon_print.svg index aa0b1e8..34411c4 100644 --- a/src/assets/images/social/icon_print.svg +++ b/src/assets/images/social/icon_print.svg @@ -1,7 +1,7 @@ - icon print + print Created with Sketch. diff --git a/src/assets/images/social/icon_twitter.svg b/src/assets/images/social/icon_twitter.svg index 55b1e7b..0342b99 100644 --- a/src/assets/images/social/icon_twitter.svg +++ b/src/assets/images/social/icon_twitter.svg @@ -1,7 +1,7 @@ - icon twitter + twitter Created with Sketch. diff --git a/src/components/DateRangePicker/DateInput/styles.scss b/src/components/DateRangePicker/DateInput/styles.scss index 0f01a28..4ac3434 100644 --- a/src/components/DateRangePicker/DateInput/styles.scss +++ b/src/components/DateRangePicker/DateInput/styles.scss @@ -18,7 +18,6 @@ } .date-range-input { - width: 230px; margin-top: -12px; font-size: $font-size-sm; diff --git a/src/components/DateRangePicker/index.jsx b/src/components/DateRangePicker/index.jsx index 96b0d27..a970a13 100644 --- a/src/components/DateRangePicker/index.jsx +++ b/src/components/DateRangePicker/index.jsx @@ -551,15 +551,11 @@ function DateRangePicker(props) { return isBeforeDay(preview.endDate, range.startDate); }; - const className = ` - ${focusedRange[1] === 1 && styles.endDate} - ${" "} - ${range.startDate && range.endDate && styles.isRange} - ${" "} - ${isInvalidPreview() && styles.isInvalidPreview} - ${" "} - ${(errors.startDate || errors.endDate) && styles.isErrorInput} - `; + const className = `${(focusedRange[1] === 1 && styles.endDate) || ""} ${ + (range.startDate && range.endDate && styles.isRange) || "" + } ${(isInvalidPreview() && styles.isInvalidPreview) || ""} ${ + ((errors.startDate || errors.endDate) && styles.isErrorInput) || "" + }`; return ( diff --git a/src/components/DateRangePicker/style.scss b/src/components/DateRangePicker/style.scss index c0bd6f1..2e8467a 100644 --- a/src/components/DateRangePicker/style.scss +++ b/src/components/DateRangePicker/style.scss @@ -57,24 +57,25 @@ $darkGreen: #0AB88A;; @include phone { .rdrDateRangePickerWrapper { position: relative !important; - width: 100vw !important; + width: 100% !important; flex-direction: column-reverse; align-items: center; justify-content: flex-end; padding: 0 20px; + border-radius: 0 !important; .rdrDefinedRangesWrapper { - width: 100vw; - padding-bottom: 10px; - .rdrStaticRanges { display: inline-flex; flex-direction: row; justify-content: space-around; + flex-wrap: wrap; margin-top: 10px !important; - border-bottom: 1px solid $tc-gray-20; - width: 100vw; - padding-bottom: 10px; + width: calc(100% - 40px); + + .rdrStaticRange { + width: 50%; + } .rdrStaticRangeLabel { font-size: 14px; @@ -83,10 +84,6 @@ $darkGreen: #0AB88A;; > button:hover .rdrStaticRangeLabel { background-color: $green; } - - & > *:last-child { - display: none; - } } } @@ -137,14 +134,14 @@ $darkGreen: #0AB88A;; .rdrDateRangePickerWrapper { z-index: 15; position: relative; - background: $tc-white; + // background: $tc-white; border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; overflow: hidden; width: 455px; .rdrDefinedRangesWrapper { - width: unset; + width: 100%; border: none; .rdrStaticRanges { @@ -164,6 +161,10 @@ $darkGreen: #0AB88A;; background-color: $green; } } + + .rdrInputRanges { + display: none; + } } } @@ -373,7 +374,6 @@ $darkGreen: #0AB88A;; } .dateInputWrapper { - display: inline-flex; position: relative; text-align: left; } @@ -395,26 +395,32 @@ $darkGreen: #0AB88A;; z-index: 10; @include phone { - width: 100vw; + width: 100%; position: fixed; top: 0; left: 0; - right: 0; + right: 20px; bottom: 0; z-index: 15; - padding: 65px 0 0; + padding: 187px 0 0; border: 0; border-radius: 0; - } + background: rgba(#2A2A2A, 0.6); - .calendar-footer { - width: 100%; + @include down(320px) { + padding: 40px 0 0; + } - @include phone { - padding: 0 20px; + .calendar-footer { + margin: 0 20px; + padding: 20px 0; } } + .calendar-footer { + background: $tc-white; + } + .calendar-button { @include roboto-bold; @@ -439,20 +445,18 @@ $darkGreen: #0AB88A;; } } -.endDate { +@include tablet { .calendar-container, .calendar-inner-container { - left: 0; + right: 0; } :global { .rdrDateRangePickerWrapper { - @include tablet { - .calendar-container, - .calendar-inner-container { - right: auto; - left: 62px; - } + .calendar-container, + .calendar-inner-container { + right: 62px; + left: auto; } } } diff --git a/src/components/DropdownTerms/index.jsx b/src/components/DropdownTerms/index.jsx index b605974..a93798b 100644 --- a/src/components/DropdownTerms/index.jsx +++ b/src/components/DropdownTerms/index.jsx @@ -4,7 +4,7 @@ import React, { useState, useRef, useEffect } from "react"; import PT from "prop-types"; import _ from "lodash"; -import { Creatable } from "react-select"; +import Select from "react-select"; import iconDown from "assets/icons/dropdown-arrow.png"; import config from "../../../config"; import "./styles.scss"; @@ -100,7 +100,7 @@ function DropdownTerms({ }`} > - setFocused(true)} onClose={() => setFocused(false)} autosize={false} diff --git a/src/components/Editor/MarkdownEditor/style.scss b/src/components/Editor/MarkdownEditor/style.scss index 63b91b0..6f3e02c 100644 --- a/src/components/Editor/MarkdownEditor/style.scss +++ b/src/components/Editor/MarkdownEditor/style.scss @@ -18,6 +18,7 @@ display: block; font-family: "Roboto Mono", monospace; padding: 15px 20px; + white-space: pre-wrap; &:global.inline { background: $tc-gray-10; diff --git a/src/components/Pagination/index.jsx b/src/components/Pagination/index.jsx index ee625ef..503899f 100644 --- a/src/components/Pagination/index.jsx +++ b/src/components/Pagination/index.jsx @@ -105,14 +105,14 @@ const Pagination = ({ length, pageIndex, pageSize, onChange }) => { onChange={onChangePageSize} size="xs" /> + per page - {" "} - PREVIOUS + {displayPages.map((p) => ( @@ -132,7 +132,6 @@ const Pagination = ({ length, pageIndex, pageSize, onChange }) => { }`} > - NEXT{" "} diff --git a/src/components/Pagination/styles.scss b/src/components/Pagination/styles.scss index 0e4f979..826fc7b 100644 --- a/src/components/Pagination/styles.scss +++ b/src/components/Pagination/styles.scss @@ -5,15 +5,60 @@ display: flex; align-items: center; justify-content: center; + + @include down(1024px) { + .per-page { + width: 60px; + + :global { + .Select-arrow-zone { + padding: 0 8px !important; + } + + .Select ~ img { + right: 8px; + } + } + + .label { + display: none; + } + } + + .pages { + margin: 0; + + .page.previous > button, + .page.next > button { + width: 30px; + padding: 0; + + &::before, + &::after { + content: ''; + } + } + } + } + + @include down($mfe-screen-xs) { + .pages { + flex: auto; + margin: 0; + justify-content: flex-end; + } + } } .per-page { - width: 128px; + display: flex; + align-items: center; + width: 144px; - :global { - .Select-value-label::after { - content: ' per page'; - } + .label { + white-space: nowrap; + margin-left: 9px; + font-size: $font-size-sm; } } @@ -65,18 +110,22 @@ } &.previous { + > button::after { + content: 'PREVIOUS '; + } + .arrow { transform: rotate(90deg) scale(0.75); } } &.next { + > button::before { + content: ' NEXT'; + } + .arrow { transform: rotate(-90deg) scale(0.75); } } } - - - - diff --git a/src/components/Panel/styles.scss b/src/components/Panel/styles.scss index db6712b..c1837ab 100644 --- a/src/components/Panel/styles.scss +++ b/src/components/Panel/styles.scss @@ -7,6 +7,21 @@ $panel-padding-y: 16px; .panel { background: $white; border-radius: $border-radius-lg; + + @include down($mfe-screen-xs) { + padding: 0; + border-radius: 0; + + .panel-header, + .panel-footer { + padding: 10px 20px; + border-radius: 0; + } + + .panel-body { + padding: 0; + } + } } .panel-header { diff --git a/src/components/challenge-detail/Header/ChallengeTags.jsx b/src/components/challenge-detail/Header/ChallengeTags.jsx index a96a7fb..35f3d70 100644 --- a/src/components/challenge-detail/Header/ChallengeTags.jsx +++ b/src/components/challenge-detail/Header/ChallengeTags.jsx @@ -38,7 +38,6 @@ export default function ChallengeTags(props) { challengeType, events, technPlatforms, - setChallengeListingFilter, openForRegistrationChallenges, } = props; @@ -94,11 +93,6 @@ export default function ChallengeTags(props) { {challengeType && ( - setImmediate(() => - setChallengeListingFilter({ types: [challengeType.name] }) - ) - } to={`${challengesUrl}${filterByChallengeType}&types[]=${encodeURIComponent( challengeType.abbreviation )}`} @@ -124,9 +118,6 @@ export default function ChallengeTags(props) { tag && ( - setImmediate(() => setChallengeListingFilter({ tags: [tag] })) - } to={`${challengesUrl}${filterByTag}&tags[]=${encodeURIComponent( tag )}`} @@ -150,7 +141,6 @@ ChallengeTags.propTypes = { track: PT.string.isRequired, events: PT.arrayOf(PT.string), technPlatforms: PT.arrayOf(PT.string), - setChallengeListingFilter: PT.func.isRequired, challengeType: PT.shape().isRequired, openForRegistrationChallenges: PT.shape().isRequired, }; diff --git a/src/components/challenge-detail/Header/index.jsx b/src/components/challenge-detail/Header/index.jsx index 2cc5d4b..8bb27ef 100644 --- a/src/components/challenge-detail/Header/index.jsx +++ b/src/components/challenge-detail/Header/index.jsx @@ -47,7 +47,6 @@ export default function ChallengeHeader(props) { onToggleDeadlines, registering, registerForChallenge, - setChallengeListingFilter, unregisterFromChallenge, unregistering, challengeTypesMap, @@ -282,7 +281,6 @@ export default function ChallengeHeader(props) { challengesUrl={challengesUrl} events={eventNames} technPlatforms={miscTags} - setChallengeListingFilter={setChallengeListingFilter} openForRegistrationChallenges={openForRegistrationChallenges} /> {(hasRecommendedChallenges || hasThriveArticles) && ( @@ -531,7 +529,6 @@ ChallengeHeader.propTypes = { registerForChallenge: PT.func.isRequired, registering: PT.bool.isRequired, selectedView: PT.string.isRequired, - setChallengeListingFilter: PT.func.isRequired, showDeadlineDetail: PT.bool.isRequired, unregisterFromChallenge: PT.func.isRequired, unregistering: PT.bool.isRequired, diff --git a/src/components/challenge-detail/Specification/styles.module.scss b/src/components/challenge-detail/Specification/styles.module.scss index 9c4195a..4a5a89a 100644 --- a/src/components/challenge-detail/Specification/styles.module.scss +++ b/src/components/challenge-detail/Specification/styles.module.scss @@ -141,6 +141,7 @@ $tc-link-visited: #0c4e98; margin: (2 * $base-unit) 0 (3 * $base-unit); padding: 3 * $base-unit; display: block; + white-space: pre-wrap; } pre { @@ -374,7 +375,7 @@ $tc-link-visited: #0c4e98; } code { - white-space: pre; + white-space: pre-wrap; margin: 10px 0 15px; background: $tc-gray-neutral-light; border: 1px solid silver; diff --git a/src/components/challenge-listing/ChallengeLoading/styles.module.scss b/src/components/challenge-listing/ChallengeLoading/styles.module.scss index f0b4ed9..aa30816 100644 --- a/src/components/challenge-listing/ChallengeLoading/styles.module.scss +++ b/src/components/challenge-listing/ChallengeLoading/styles.module.scss @@ -1,4 +1,5 @@ @import "~styles/mixins"; +@import "~styles/variables"; @keyframes placeholderAnim { 0% { @@ -81,4 +82,19 @@ height: 42px; width: 40px; } + + @include down($mfe-screen-sm) { + .title { + width: 50%; + max-width: 100px; + } + .info { + width: 100%; + max-width: 200px; + } + } + + @include down($mfe-screen-xs) { + margin: 0; + } } diff --git a/src/containers/Challenges/Listing/ChallengeItem/Prize/index.jsx b/src/containers/Challenges/Listing/ChallengeItem/Prize/index.jsx index 66ae139..11bb944 100644 --- a/src/containers/Challenges/Listing/ChallengeItem/Prize/index.jsx +++ b/src/containers/Challenges/Listing/ChallengeItem/Prize/index.jsx @@ -5,7 +5,7 @@ import * as utils from "../../../../../utils"; import "./styles.scss"; const Prize = ({ totalPrizes, currencySymbol }) => ( - + Purse {utils.formatMoneyValue(totalPrizes, currencySymbol)} diff --git a/src/containers/Challenges/Listing/ChallengeItem/Prize/styles.scss b/src/containers/Challenges/Listing/ChallengeItem/Prize/styles.scss index b558dd7..4671530 100644 --- a/src/containers/Challenges/Listing/ChallengeItem/Prize/styles.scss +++ b/src/containers/Challenges/Listing/ChallengeItem/Prize/styles.scss @@ -1,6 +1,20 @@ @import "styles/variables"; @import "styles/mixins"; +.prize { + @include down($mfe-screen-xs) { + display: inline-flex; + flex-direction: row-reverse; + align-items: center; + + .value { + margin-right: 10px; + font-size: 21px; + line-height: 25px; + } + } +} + .text, .value { display: block; diff --git a/src/containers/Challenges/Listing/ChallengeItem/Tags/index.jsx b/src/containers/Challenges/Listing/ChallengeItem/Tags/index.jsx index 2dca982..3daf985 100644 --- a/src/containers/Challenges/Listing/ChallengeItem/Tags/index.jsx +++ b/src/containers/Challenges/Listing/ChallengeItem/Tags/index.jsx @@ -1,21 +1,46 @@ -import React, { useState } from "react"; +import React, { useState, useMemo } from "react"; import PT from "prop-types"; import Tag from "../../../../../components/Tag"; import * as util from "../../../../../utils/tag"; +import { useTargetSize } from "../../../../../utils/hooks/useTargetSize"; import "./styles.scss"; const Tags = ({ tags, onClickTag, tooltip }) => { - const n = util.calculateNumberOfVisibleTags(tags); + const Tooltip = tooltip; + + const [size, ref] = useTargetSize(); + + const n = useMemo(() => { + const tagArray = [...tags]; + let tagsWidth = util.measureText(tagArray); + + if (!size) { + return 0; + } + + const maxWidth = Math.min(260, size.width); + if (tagsWidth < maxWidth) { + return tagArray.length; + } + + const widthOfMoreTag = 40; + while (tagsWidth > maxWidth - widthOfMoreTag) { + tagArray.pop(); + tagsWidth = util.measureText(tagArray); + } + + return tagArray.length; + }, [tags, size]); + const more = n < tags.length ? tags.length - n : 0; + const [collapsed, setCollapsed] = useState(more > 0); const visibleTags = collapsed ? tags.slice(0, n) : tags; const invisibleTags = collapsed ? tags.slice(n) : []; - const Tooltip = tooltip; - return ( - + {visibleTags.map((tag) => ( ))} diff --git a/src/containers/Challenges/Listing/ChallengeItem/Tags/styles.scss b/src/containers/Challenges/Listing/ChallengeItem/Tags/styles.scss index 052530f..7664d85 100644 --- a/src/containers/Challenges/Listing/ChallengeItem/Tags/styles.scss +++ b/src/containers/Challenges/Listing/ChallengeItem/Tags/styles.scss @@ -1,9 +1,15 @@ @import "styles/variables"; .tags { + margin-top: -5px; + min-width: 1px; + > * { - margin-right: 4px; - margin-bottom: 4px; + margin-top: 5px; + } + + > *:not(:last-child) { + margin-right: 5px; } } diff --git a/src/containers/Challenges/Listing/ChallengeItem/index.jsx b/src/containers/Challenges/Listing/ChallengeItem/index.jsx index 1fef4cd..bab2ad7 100644 --- a/src/containers/Challenges/Listing/ChallengeItem/index.jsx +++ b/src/containers/Challenges/Listing/ChallengeItem/index.jsx @@ -32,6 +32,10 @@ const ChallengeItem = ({ challenge, onClickTag, onClickTrack, isLoggedIn }) => { submissionLink += "?tab=submissions"; } + const challengeName = utils.toBreakableWords(challenge.name, (w) => + w.length > 20 ? `${w}` : w + ); + return ( @@ -46,7 +50,7 @@ const ChallengeItem = ({ challenge, onClickTag, onClickTrack, isLoggedIn }) => { - {challenge.name} + * { + &:first-child { + margin-left: 0; + display: inline-block; + width: 64px; + } + } + } + } + + .prize { + align-self: flex-start; + padding-top: 5px; + } + } + + @include down($mfe-screen-xs) { + position: relative; + flex-wrap: wrap; + padding: 20px 0 20px 68px; + + .track { + position: absolute; + left: 15px; + top: 20px; + } + + .info { + width: 100%; + + .name-container { + padding-right: 20px; + + .name { + line-height: 20px; + } + } + + .tags { + max-width: none; + min-width: 0; + } + + .name-container, + .tags { + margin-bottom: 10px; + } + + .nums { + display: none; + } + } + } } .track { @@ -39,7 +110,7 @@ min-width: calc(50% - 84px); flex: 1 1 auto; - @media (min-width: $screen-xxl + 1px) { + @media (min-width: ($screen-xxl + 1px)) { min-width: 294px; max-width: 25%; } @@ -50,14 +121,11 @@ white-space: nowrap; > * { - margin: 0 20px; &:first-child { margin-left: 0; - } - - &:last-child { - margin-right: 0; + display: inline-block; + width: 84px; } } } diff --git a/src/containers/Challenges/Listing/index.jsx b/src/containers/Challenges/Listing/index.jsx index bfc1318..8c25e12 100644 --- a/src/containers/Challenges/Listing/index.jsx +++ b/src/containers/Challenges/Listing/index.jsx @@ -14,6 +14,9 @@ import * as utils from "../../../utils"; import * as constants from "../../../constants"; import IconSearch from "../../../assets/icons/search.svg"; +import IconClear from "../../../assets/icons/close-gray.svg"; +import Button from "../../../components/Button"; + import "./styles.scss"; const Listing = ({ @@ -47,6 +50,11 @@ const Listing = ({ updateFilter(filterChange); }; + const onShowSidebar = () => { + const sidebarEl = document.getElementById("sidebar-id"); + sidebarEl.classList.add("show"); + }; + return ( @@ -70,6 +78,24 @@ const Listing = ({ }} maxLength="100" /> + {search.length ? ( + + { + onSearch.current(() => { + const filterChange = { + search: "", + page: 1, + }; + updateFilter(filterChange); + }); + }} + /> + + ) : ( + + )} + + FILTER + - {loadingChallenges ? ( - _.times(3, () => ) - ) : challenges.length ? ( - - {challenges.map((challenge, index) => ( - - { - const filterChange = { - tags: [tag], - page: 1, - }; - updateFilter(filterChange); - }} - onClickTrack={(track) => { - const filterChange = { - tracks: [track], - page: 1, - }; - updateFilter(filterChange); - }} - isLoggedIn={isLoggedIn} - /> - - ))} - - { - const filterChange = { - page: utils.pagination.pageIndexToPage(event.pageIndex), - perPage: event.pageSize, - }; - updateFilter(filterChange); - }} - /> - - - ) : ( - - )} + {loadingChallenges && _.times(3, () => )} + {!loadingChallenges && + (challenges.length ? ( + + {challenges.map((challenge, index) => ( + + { + const filterChange = { + tags: [tag], + page: 1, + }; + updateFilter(filterChange); + }} + onClickTrack={(track) => { + const filterChange = { + tracks: [track], + page: 1, + }; + updateFilter(filterChange); + }} + isLoggedIn={isLoggedIn} + /> + + ))} + + ) : ( + + ))} + + + { + const filterChange = { + page: utils.pagination.pageIndexToPage(event.pageIndex), + perPage: event.pageSize, + }; + updateFilter(filterChange); + }} + /> + + ); }; diff --git a/src/containers/Challenges/Listing/styles.scss b/src/containers/Challenges/Listing/styles.scss index 331471b..57f6fa8 100644 --- a/src/containers/Challenges/Listing/styles.scss +++ b/src/containers/Challenges/Listing/styles.scss @@ -11,7 +11,46 @@ .header-container { display: flex; - align-items: top; + align-items: flex-start; + + @include down($mfe-screen-xs) { + flex-wrap: wrap; + + .separator { + display: none; + } + + .input-group { + width: 100%; + max-width: none; + flex: auto; + margin: 0 0 20px 0; + } + + .sort-by { + max-width: 241px; + } + + .sort-by, + .from-to { + flex: 1 1 auto; + margin: 0 22px 0 0; + + @include down(375px - 1px) { + max-width: calc(100% - 92px); + } + } + + .filter-button { + display: block; + margin: 0 0 0 auto; + + button { + height: 40px; + border-radius: 20px; + } + } + } } .input-group { @@ -19,13 +58,8 @@ display: inline-block; margin-right: 16px; margin-bottom: 17px; - width: 38%; - min-width: 380px; - - @include xs-to-md { - width: 62%; - min-width: 230px; - } + flex: auto; + max-width: 380px; .search-icon { position: absolute; @@ -39,6 +73,19 @@ height: 40px; } + .clear-icon { + position: absolute; + bottom: 0; + right: 0; + z-index: 1; + display: flex; + align-items: center; + justify-content: center; + width: 46px; + height: 40px; + cursor: pointer; + } + input { margin-top: 0 !important; padding-left: 46px !important; @@ -60,7 +107,8 @@ } .from-to { - display: inline-block; + width: 50%; + max-width: 241px; } .sort-by, @@ -72,6 +120,14 @@ } } +.filter-button { + display: none; +} + .pagination { padding: 17px 0 15px; + + @include down($mfe-screen-xs) { + padding: 20px; + } } diff --git a/src/containers/Challenges/index.jsx b/src/containers/Challenges/index.jsx index 73833b7..3e9e9fe 100644 --- a/src/containers/Challenges/index.jsx +++ b/src/containers/Challenges/index.jsx @@ -12,7 +12,12 @@ import * as constants from "../../constants"; import { Banner } from "@topcoder/micro-frontends-earn-app"; import * as utils from "../../utils"; +import { useMediaQuery } from "react-responsive"; +import { useCssVariable } from "../../utils/hooks/useCssVariable"; +import IconArrow from "../../assets/icons/arrow.svg"; + import "./styles.scss"; +import { useClickOutside } from "../../utils/hooks/useClickOutside"; const Challenges = ({ challenges, @@ -74,20 +79,55 @@ const Challenges = ({ recommended && recommendedChallenges.length === 0; + const screenXs = useCssVariable("--mfe-screen-xs", (value) => + parseInt(value) + ); + const isScreenXs = useMediaQuery({ maxWidth: screenXs }); + + const [menuExpanded, setMenuExpanded] = useState(false); + const menuRef = useClickOutside(menuExpanded, (event) => { + setMenuExpanded(false); + }); + + const mobileMenu = ( + + ); + return ( - - CHALLENGES - {/* - - - - - - - */} - + + + isScreenXs && setMenuExpanded(!menuExpanded)} + > + {isScreenXs && menuExpanded ? "EARN" : "CHALLENGES"} + + {isScreenXs ? ( + + + + ) : ( + + {/* + + + + + */} + + )} + + + {mobileMenu} + + {initialized ? ( <> {/*noRecommendedChallenges && */} diff --git a/src/containers/Challenges/styles.scss b/src/containers/Challenges/styles.scss index 13c7fde..d3bca64 100644 --- a/src/containers/Challenges/styles.scss +++ b/src/containers/Challenges/styles.scss @@ -3,11 +3,51 @@ .page { padding: $page-padding-y $page-padding-x 105px; + + @include down($mfe-screen-xs) { + padding: 10px 0; + background-color: $tc-white; + + :global(.banner) { + margin: 0 20px; + width: calc(100% - 40px); + } + } } .title { @include barlow-condensed-medium; margin-bottom: 22px; + font-size: 34px; + line-height: 1; + + @include down($mfe-screen-xs) { + font-size: 25px; + line-height: 27px; + margin: 10px 0 0; + padding: 0 20px 10px 20px; + background-color: $tc-white; + position: relative; + z-index: 11; + cursor: pointer; + + &.menu-title { + text-align: center; + } + } +} + +.mobile-menu { + position: absolute; + width: 100%; + background-color: $tc-white; + border-top: 1px solid $gray3; + box-shadow: 0 10px 20px 0 rgba($tc-black, 0.25); + z-index: 10; + + &.hidden { + display: none; + } } .button-icon { @@ -36,3 +76,12 @@ margin: 0 2px; } } + +.arrow-down { + float: right; + vertical-align: middle; + + &.up { + transform: rotate(180deg); + } +} diff --git a/src/containers/Filter/ChallengeFilter/styles.scss b/src/containers/Filter/ChallengeFilter/styles.scss index fc29ced..539761c 100644 --- a/src/containers/Filter/ChallengeFilter/styles.scss +++ b/src/containers/Filter/ChallengeFilter/styles.scss @@ -33,10 +33,10 @@ $filter-padding-y: 3 * $base-unit; .count-badge { display: inline-block; margin: -2px 0 0 $base-unit; - padding: 2px 5px 0; + padding: 2px 5px; font-weight: bold; font-size: 11px; - line-height: 14px; + line-height: 11px; text-align: center; color: white; vertical-align: middle; @@ -96,6 +96,10 @@ $filter-padding-y: 3 * $base-unit; position: relative; margin-top: -12px; + :global(.textInputContainer) { + width: 100%; + } + input { padding-right: 36px !important; } diff --git a/src/containers/Submission/Submit/SubmitForm/index.jsx b/src/containers/Submission/Submit/SubmitForm/index.jsx index 8d2798b..1504577 100644 --- a/src/containers/Submission/Submit/SubmitForm/index.jsx +++ b/src/containers/Submission/Submit/SubmitForm/index.jsx @@ -1,3 +1,4 @@ +/* eslint jsx-a11y/no-static-element-interactions:0 */ /** * components.page.challenge-details.Submit * Component @@ -50,6 +51,8 @@ const SubmitForm = ({ const propsRef = useRef(); propsRef.current = { resetForm }; + const checkboxRef = useRef(); + useEffect(() => { return () => { propsRef.current.resetForm(); @@ -93,6 +96,10 @@ const SubmitForm = ({ submit(getFormData()); }; + const handleAgree = () => { + checkboxRef.current.checked = !checkboxRef.current.checked; + }; + const id = "file-picker-submission"; const isLoadingCommunitiesList = !isCommunityListLoaded; @@ -255,13 +262,14 @@ const SubmitForm = ({ Topcoder terms of use and to the extent your uploaded file wins a topcoder - Competition, you hereby assign, grant and transfer and agree to - assign, grant and transfer to topcoder all right and title in and to - the Winning Submission (as further described in the terms of use). + Competition, you hereby agree to assign, grant and transfer to + Topcoder all right and title in and to the Winning Submission (as + further described in the terms of use). setAgreed(e.target.checked)} @@ -269,7 +277,9 @@ const SubmitForm = ({ - I UNDERSTAND AND AGREE + + I UNDERSTAND AND AGREE + 0} hasThriveArticles={thriveArticles.length > 0} - setChallengeListingFilter={setChallengeListingFilter} unregisterFromChallenge={() => unregisterFromChallenge(auth, challengeId) } @@ -718,7 +716,6 @@ ChallengeDetailPageContainer.propTypes = { resultsLoadedForChallengeId: PT.string.isRequired, savingChallenge: PT.bool.isRequired, selectedTab: PT.string.isRequired, - setChallengeListingFilter: PT.func.isRequired, setSpecsTabState: PT.func.isRequired, specsTabState: PT.string.isRequired, terms: PT.arrayOf(PT.shape()), @@ -967,18 +964,6 @@ const mapDispatchToProps = (dispatch) => { return challengeDetails; }); }, - setChallengeListingFilter: (change, stateProps) => { - const updateFilter = actions.filter.updateFilter; - const updateQuery = actions.filter.updateChallengeQuery; - change.page = 1; - change.tracks = constants.FILTER_CHALLENGE_TRACKS; - change.backet = constants.FILTER_BUCKETS[1]; - if (change.tags) { - change.types = constants.FILTER_CHALLENGE_TYPES; - } - dispatch(updateFilter(change)); - dispatch(updateQuery({ ...stateProps.filter.challenge, ...change })); - }, setSpecsTabState: (state) => dispatch(pageActions.page.challengeDetails.setSpecsTabState(state)), unregisterFromChallenge: (auth, challengeId) => { @@ -1041,18 +1026,9 @@ const mapDispatchToProps = (dispatch) => { }; }; -const mergeProps = (stateProps, dispatchProps, ownProps) => ({ - ...ownProps, - ...stateProps, - ...dispatchProps, - setChallengeListingFilter: (filter) => - dispatchProps.setChallengeListingFilter(filter, stateProps), -}); - const ChallengeDetailContainer = connect( mapStateToProps, - mapDispatchToProps, - mergeProps + mapDispatchToProps )(ChallengeDetailPageContainer); export default ChallengeDetailContainer; diff --git a/src/routers/challenge-list/index.jsx b/src/routers/challenge-list/index.jsx index 6adcfb0..5fddb7f 100644 --- a/src/routers/challenge-list/index.jsx +++ b/src/routers/challenge-list/index.jsx @@ -11,6 +11,8 @@ import * as utils from "../../utils"; import store from "../../store"; import { initialChallengeFilter } from "../../reducers/filter"; import _ from "lodash"; +import { useMediaQuery } from "react-responsive"; +import { useCssVariable } from "../../utils/hooks/useCssVariable"; import "react-date-range/dist/theme/default.css"; import "react-date-range/dist/styles.css"; @@ -20,13 +22,6 @@ const App = () => { const location = useLocation(); const previousControllerRef = useRef(); - useLayoutEffect(() => { - showMenu(true); - return () => { - showMenu(false); - }; - }, []); - useEffect(() => { if (!location.search) { const currentFilter = store.getState().filter.challenge; @@ -72,18 +67,42 @@ const App = () => { store.dispatch(actions.challenges.getChallengesDone(updatedFilter, signal)); }, [location]); + const onHideSidebar = () => { + const sidebarEl = document.getElementById("sidebar-id"); + sidebarEl.classList.remove("show"); + }; + + const screenXs = useCssVariable("--mfe-screen-xs", (value) => + parseInt(value) + ); + const isScreenXs = useMediaQuery({ maxWidth: screenXs }); + + useLayoutEffect(() => { + showMenu(true); + return () => { + showMenu(false); + }; + }, [isScreenXs]); + return ( <> -