diff --git a/client/package-lock.json b/client/package-lock.json index 48cbf7f713..d39589f1b0 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -19599,6 +19599,11 @@ } } }, + "jump.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/jump.js/-/jump.js-1.0.1.tgz", + "integrity": "sha1-DeKxYxupocLGuFcq0nfYd+hQNgA=" + }, "just-curry-it": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/just-curry-it/-/just-curry-it-3.1.0.tgz", @@ -23347,6 +23352,15 @@ "prop-types": "^15.6.1" } }, + "react-scrollable-anchor": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/react-scrollable-anchor/-/react-scrollable-anchor-0.6.1.tgz", + "integrity": "sha1-/W54Amx0T3ZBQFPQaQO4KtzLVNk=", + "requires": { + "jump.js": "1.0.1", + "prop-types": "^15.5.10" + } + }, "react-side-effect": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.0.tgz", diff --git a/client/package.json b/client/package.json index 962a9aedeb..82db5dbc59 100644 --- a/client/package.json +++ b/client/package.json @@ -61,6 +61,7 @@ "react-redux": "^5.0.7", "react-reflex": "^3.1.1", "react-responsive": "^6.1.1", + "react-scrollable-anchor": "^0.6.1", "react-spinkit": "^3.0.0", "react-stripe-elements": "^2.0.3", "react-tooltip": "^4.2.13", diff --git a/client/src/templates/Challenges/components/Challenge-Title.js b/client/src/templates/Challenges/components/Challenge-Title.js index 2f4363b441..06db39bc0c 100644 --- a/client/src/templates/Challenges/components/Challenge-Title.js +++ b/client/src/templates/Challenges/components/Challenge-Title.js @@ -26,6 +26,7 @@ function ChallengeTitle({ block, children, isCompleted, superBlock }) {
@@ -36,7 +37,7 @@ function ChallengeTitle({ block, children, isCompleted, superBlock }) { {i18next.t( `intro:${dasherize(superBlock)}.blocks.${dasherize(block)}.title` diff --git a/client/src/templates/Challenges/components/ChallengeTitle.test.js b/client/src/templates/Challenges/components/ChallengeTitle.test.js index 01ed2103b1..28d96c39da 100644 --- a/client/src/templates/Challenges/components/ChallengeTitle.test.js +++ b/client/src/templates/Challenges/components/ChallengeTitle.test.js @@ -6,8 +6,10 @@ import renderer from 'react-test-renderer'; import ChallengeTitle from './Challenge-Title'; const baseProps = { + block: 'fake block', children: 'title text', - isCompleted: true + isCompleted: true, + superBlock: 'fake superblock' }; describe('', () => { diff --git a/client/src/templates/Challenges/components/__snapshots__/ChallengeTitle.test.js.snap b/client/src/templates/Challenges/components/__snapshots__/ChallengeTitle.test.js.snap index 8f55acc75c..b2baacd3ed 100644 --- a/client/src/templates/Challenges/components/__snapshots__/ChallengeTitle.test.js.snap +++ b/client/src/templates/Challenges/components/__snapshots__/ChallengeTitle.test.js.snap @@ -15,7 +15,12 @@ exports[` renders correctly 1`] = ` > renders correctly 1`] = ` /> diff --git a/client/src/templates/Introduction/SuperBlockIntro.js b/client/src/templates/Introduction/SuperBlockIntro.js index ce8e31cba8..8ef017faeb 100644 --- a/client/src/templates/Introduction/SuperBlockIntro.js +++ b/client/src/templates/Introduction/SuperBlockIntro.js @@ -8,6 +8,7 @@ import { createSelector } from 'reselect'; import { bindActionCreators } from 'redux'; import { withTranslation } from 'react-i18next'; import { Grid, Row, Col } from '@freecodecamp/react-bootstrap'; +import { configureAnchors } from 'react-scrollable-anchor'; import Login from '../../components/Header/components/Login'; import Map from '../../components/Map'; @@ -40,6 +41,7 @@ const propTypes = { }), isSignedIn: PropTypes.bool, location: PropTypes.shape({ + hash: PropTypes.string, state: PropTypes.shape({ breadcrumbBlockClick: PropTypes.string }) @@ -49,6 +51,8 @@ const propTypes = { toggleBlock: PropTypes.func }; +configureAnchors({ offset: -40, scrollDuration: 0 }); + const mapStateToProps = state => { return createSelector( currentChallengeIdSelector, @@ -71,6 +75,14 @@ const mapDispatchToProps = dispatch => export class SuperBlockIntroductionPage extends Component { componentDidMount() { this.initializeExpandedState(); + + setTimeout(() => { + configureAnchors({ offset: -40, scrollDuration: 400 }); + }, 0); + } + + componentWillUnmount() { + configureAnchors({ offset: -40, scrollDuration: 0 }); } getChosenBlock() { @@ -84,8 +96,15 @@ export class SuperBlockIntroductionPage extends Component { } = this.props; // if coming from breadcrumb click - if (location.state && location.state.breadcrumbBlockClick) + if (location.state && location.state.breadcrumbBlockClick) { return dasherize(location.state.breadcrumbBlockClick); + } + + // if the URL includes a hash + if (location.hash) { + const dashedBlock = location.hash.replace('#', '').replace('/', ''); + return dashedBlock; + } let edge = edges[0]; @@ -146,7 +165,7 @@ export class SuperBlockIntroductionPage extends Component {
{blockDashedNames.map(blockDashedName => ( -
+ <> - {blockDashedName !== 'project-euler' ? ( - - ) : null} -
+ {blockDashedName !== 'project-euler' ? : null} + ))} {superBlock !== 'Coding Interview Prep' && (
diff --git a/client/src/templates/Introduction/components/Block.js b/client/src/templates/Introduction/components/Block.js index 78260975a2..0e54709732 100644 --- a/client/src/templates/Introduction/components/Block.js +++ b/client/src/templates/Introduction/components/Block.js @@ -4,6 +4,7 @@ import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import { withTranslation } from 'react-i18next'; +import ScrollableAnchor from 'react-scrollable-anchor'; import { makeExpandedBlockSelector, toggleBlock } from '../redux'; import { completedChallengesSelector, executeGA } from '../../../redux'; @@ -132,67 +133,81 @@ export class Block extends Component { } = t('intro:misc-text'); return isProjectBlock ? ( -
-
-

{blockTitle}

- {!isAuditedCert(curriculumLocale, superBlockDashedName) && ( -
- - {t('misc.translation-pending')} - -
- )} -
- {this.renderBlockIntros(blockIntroArr)} - -
- ) : ( -
+
+ + ) : ( + +
+
+ +

+ {blockTitle} + # +

+
+ {!isAuditedCert(curriculumLocale, superBlockDashedName) && ( +
+ + {t('misc.translation-pending')} + +
+ )} +
+ {this.renderBlockIntros(blockIntroArr)} + + {isExpanded && ( + + )} +
+
); } } diff --git a/client/src/templates/Introduction/intro.css b/client/src/templates/Introduction/intro.css index 179211b6fa..516fe03a71 100644 --- a/client/src/templates/Introduction/intro.css +++ b/client/src/templates/Introduction/intro.css @@ -14,6 +14,25 @@ flex-direction: row; } +.block-link:hover, +.block-link:focus, +.block-link:active { + background-color: var(--primary-background); +} + +.block-link { + cursor: pointer; +} + +.block-link:hover .block-link-icon { + display: inline-block; +} + +.block-link-icon { + display: none; + margin-left: 15px; +} + .big-block-title { font-size: 1.5rem; overflow-wrap: break-word; @@ -185,6 +204,7 @@ button.map-title { .map-challenges-ul { padding-inline-start: 0; + margin-bottom: 0; } .map-challenge-title { diff --git a/cypress/integration/learn/responsive-web-design/intro-page.js b/cypress/integration/learn/responsive-web-design/intro-page.js index 18dbf6ec8c..c51383bea7 100644 --- a/cypress/integration/learn/responsive-web-design/intro-page.js +++ b/cypress/integration/learn/responsive-web-design/intro-page.js @@ -1,7 +1,7 @@ /* global cy */ const selectors = { - firstBlock: ':nth-child(1) > .block > .map-title' + firstBlock: '.block-ui > .block:nth-child(1) > .map-title' }; describe('Certificate intro page', () => {