refactor(client): move to react-scroll (#62921)

This commit is contained in:
Sem Bauke
2025-10-24 13:48:54 +02:00
committed by GitHub
parent f881d63de5
commit b28f2c3f6b
9 changed files with 72 additions and 59 deletions

View File

@@ -112,7 +112,7 @@
"react-redux": "7.2.9",
"react-reflex": "4.1.0",
"react-responsive": "9.0.2",
"react-scrollable-anchor": "0.6.1",
"react-scroll": "1.9.0",
"react-spinkit": "3.0.0",
"react-tooltip": "4.5.1",
"react-transition-group": "4.4.5",
@@ -153,7 +153,7 @@
"@types/react-helmet": "6.1.11",
"@types/react-redux": "7.1.33",
"@types/react-responsive": "8.0.8",
"@types/react-scrollable-anchor": "0.6.4",
"@types/react-scroll": "1.8.10",
"@types/react-spinkit": "3.0.10",
"@types/react-test-renderer": "16.9.12",
"@types/react-transition-group": "4.4.10",

View File

@@ -1,8 +1,9 @@
import React, { useRef } from 'react';
import React, { useRef, useEffect } from 'react';
import Helmet from 'react-helmet';
import { useTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { scroller } from 'react-scroll';
import { Container, Spacer } from '@freecodecamp/ui';
import { useFeatureIsOn } from '@growthbook/growthbook-react';
@@ -109,6 +110,24 @@ export function ShowSettings(props: ShowSettingsProps): JSX.Element {
const examTokenFlag = useFeatureIsOn('exam-token-widget');
const handleHashChange = () => {
const id = window.location.hash.replace('#', '');
if (id) {
scroller.scrollTo(id, {
smooth: true,
duration: 500,
offset: -100
});
}
};
useEffect(() => {
handleHashChange();
window.addEventListener('hashchange', handleHashChange);
return () => window.removeEventListener('hashchange', handleHashChange);
}, []);
if (showLoading || !user) {
return <Loader fullScreen={true} />;
}

View File

@@ -1,7 +1,7 @@
import { WindowLocation } from '@gatsbyjs/reach-router';
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { goToAnchor } from 'react-scrollable-anchor';
import { scroller } from 'react-scroll';
import { bindActionCreators, Dispatch, AnyAction } from 'redux';
import { createSelector } from 'reselect';
import { Modal } from '@freecodecamp/ui';
@@ -67,7 +67,10 @@ function DonateModal({
const handleModalHide = () => {
// If modal is open on a SuperBlock page
if (isLocationSuperBlock(location)) {
goToAnchor('claim-cert-block');
scroller.scrollTo('claim-cert-block', {
duration: 0,
smooth: false
});
}
if (isA11yFeatureEnabled && canClose) {

View File

@@ -2,7 +2,7 @@ import { find } from 'lodash-es';
import React, { MouseEvent, useState } from 'react';
import { withTranslation } from 'react-i18next';
import type { TFunction } from 'i18next';
import ScrollableAnchor, { configureAnchors } from 'react-scrollable-anchor';
import { Element } from 'react-scroll';
import { connect } from 'react-redux';
import { Table, Button, Spacer } from '@freecodecamp/ui';
@@ -39,8 +39,6 @@ import './certification.css';
const { showUpcomingChanges } = env;
configureAnchors({ offset: -40, scrollDuration: 0 });
const mapDispatchToProps = {
openModal
};
@@ -296,7 +294,7 @@ function CertificationSettings(props: CertificationSettingsProps) {
t: TFunction;
}) => {
return (
<ScrollableAnchor id={`cert-${certSlug}`}>
<Element name={`cert-${certSlug}`}>
<section>
<FullWidthRow>
<Spacer size='m' />
@@ -319,7 +317,7 @@ function CertificationSettings(props: CertificationSettingsProps) {
</Table>
</FullWidthRow>
</section>
</ScrollableAnchor>
</Element>
);
};

View File

@@ -2,7 +2,7 @@ import React, { Component, ReactNode } from 'react';
import type { TFunction } from 'i18next';
import { withTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import ScrollableAnchor from 'react-scrollable-anchor';
import { Element } from 'react-scroll';
import { bindActionCreators, Dispatch } from 'redux';
import { createSelector } from 'reselect';
import { Spacer } from '@freecodecamp/ui';
@@ -179,7 +179,7 @@ export class Block extends Component<BlockProps> {
* Example: https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/#basic-javascript
*/
const LegacyChallengeListBlock = (
<ScrollableAnchor id={block}>
<Element name={block}>
<div
className={`block ${isExpanded ? 'open' : ''}`}
onMouseOver={this.handleBlockHover}
@@ -226,7 +226,7 @@ export class Block extends Component<BlockProps> {
</button>
{isExpanded && <ChallengesList challenges={extendedChallenges} />}
</div>
</ScrollableAnchor>
</Element>
);
/**
@@ -235,7 +235,7 @@ export class Block extends Component<BlockProps> {
* Example: https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/#javascript-algorithms-and-data-structures-projects
*/
const ProjectListBlock = (
<ScrollableAnchor id={block}>
<Element name={block}>
<div
className='block'
onMouseOver={this.handleBlockHover}
@@ -258,7 +258,7 @@ export class Block extends Component<BlockProps> {
<BlockIntros intros={blockIntroArr} />
<ChallengesList challenges={extendedChallenges} />
</div>
</ScrollableAnchor>
</Element>
);
/**
@@ -267,7 +267,7 @@ export class Block extends Component<BlockProps> {
* Example: https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures-v8/#learn-basic-javascript-by-building-a-role-playing-game
*/
const LegacyChallengeGridBlock = (
<ScrollableAnchor id={block}>
<Element name={block}>
<div
className={`block block-grid ${isExpanded ? 'open' : ''}`}
onMouseOver={this.handleBlockHover}
@@ -309,7 +309,7 @@ export class Block extends Component<BlockProps> {
</>
)}
</div>
</ScrollableAnchor>
</Element>
);
/**
@@ -318,7 +318,7 @@ export class Block extends Component<BlockProps> {
* Example: https://www.freecodecamp.org/learn/a2-english-for-developers/#learn-greetings-in-your-first-day-at-the-office
*/
const TaskGridBlock = (
<ScrollableAnchor id={block}>
<Element name={block}>
<div className={`block block-grid ${isExpanded ? 'open' : ''}`}>
<BlockHeader
blockDashed={block}
@@ -355,7 +355,7 @@ export class Block extends Component<BlockProps> {
</>
)}
</div>
</ScrollableAnchor>
</Element>
);
/**
@@ -364,7 +364,7 @@ export class Block extends Component<BlockProps> {
* Example: https://www.freecodecamp.org/learn/2022/responsive-web-design/#build-a-survey-form-project
*/
const LegacyLinkBlock = (
<ScrollableAnchor id={block}>
<Element name={block}>
<div
className='block block-grid grid-project-block'
onMouseOver={this.handleBlockHover}
@@ -408,7 +408,7 @@ export class Block extends Component<BlockProps> {
</div>
<BlockIntros intros={blockIntroArr} />
</div>
</ScrollableAnchor>
</Element>
);
/**
@@ -416,9 +416,9 @@ export class Block extends Component<BlockProps> {
*/
const AccordionBlock = (
<>
<ScrollableAnchor id={block}>
<Element name={block}>
<span className='hide-scrollable-anchor'></span>
</ScrollableAnchor>
</Element>
<div
className={`block block-grid challenge-grid-block ${isExpanded ? 'open' : ''}`}
onMouseOver={this.handleBlockHover}

View File

@@ -6,7 +6,7 @@ import React, { useEffect, memo, useMemo } from 'react';
import Helmet from 'react-helmet';
import { useTranslation, withTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { configureAnchors } from 'react-scrollable-anchor';
import { scroller } from 'react-scroll';
import { bindActionCreators, Dispatch } from 'redux';
import { createSelector } from 'reselect';
import { Container, Col, Row, Spacer } from '@freecodecamp/ui';
@@ -95,8 +95,6 @@ type SuperBlockProps = {
user: User | null;
};
configureAnchors({ offset: -40, scrollDuration: 0 });
const mapStateToProps = (state: Record<string, unknown>) => {
return createSelector(
currentChallengeIdSelector,
@@ -129,6 +127,16 @@ const mapDispatchToProps = (dispatch: Dispatch) =>
},
dispatch
);
const handleHashChange = () => {
const id = window.location.hash.replace('#', '');
if (id) {
scroller.scrollTo(id, {
smooth: true,
duration: 500,
offset: -50
});
}
};
const SuperBlockIntroductionPage = (props: SuperBlockProps) => {
const { t } = useTranslation();
@@ -136,13 +144,10 @@ const SuperBlockIntroductionPage = (props: SuperBlockProps) => {
initializeExpandedState();
props.tryToShowDonationModal();
setTimeout(() => {
configureAnchors({ offset: -40, scrollDuration: 400 });
}, 0);
handleHashChange();
return () => {
configureAnchors({ offset: -40, scrollDuration: 0 });
};
window.addEventListener('hashchange', handleHashChange);
return () => window.removeEventListener('hashchange', handleHashChange);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

View File

@@ -133,12 +133,6 @@
},
"packageManager": "pnpm@10.18.0",
"pnpm": {
"peerDependencyRules": {
"allowedVersions": {
"react-scrollable-anchor>react": "17",
"react-scrollable-anchor>react-dom": "17"
}
},
"onlyBuiltDependencies": [
"@freecodecamp/ui",
"@prisma/client",

35
pnpm-lock.yaml generated
View File

@@ -510,9 +510,9 @@ importers:
react-responsive:
specifier: 9.0.2
version: 9.0.2(react@17.0.2)
react-scrollable-anchor:
specifier: 0.6.1
version: 0.6.1(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
react-scroll:
specifier: 1.9.0
version: 1.9.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
react-spinkit:
specifier: 3.0.0
version: 3.0.0
@@ -628,9 +628,9 @@ importers:
'@types/react-responsive':
specifier: 8.0.8
version: 8.0.8
'@types/react-scrollable-anchor':
specifier: 0.6.4
version: 0.6.4
'@types/react-scroll':
specifier: 1.8.10
version: 1.8.10
'@types/react-spinkit':
specifier: 3.0.10
version: 3.0.10
@@ -4745,8 +4745,8 @@ packages:
'@types/react-responsive@8.0.8':
resolution: {integrity: sha512-HDUZtoeFRHrShCGaND23HmXAB9evOOTjkghd2wAasLkuorYYitm5A1XLeKkhXKZppcMBxqB/8V4Snl6hRUTA8g==}
'@types/react-scrollable-anchor@0.6.4':
resolution: {integrity: sha512-Yn81DRAXPdOkqvDC6JO9KsTQv2g3bu45WGDfuI5G08vMntT7EvbBGrOeCuPkMQ8Q2JeZoP3TbKEMzLhJ8yAlGA==}
'@types/react-scroll@1.8.10':
resolution: {integrity: sha512-RD4Z7grbdNGOKwKnUBKar6zNxqaW3n8m9QSrfvljW+gmkj1GArb8AFBomVr6xMOgHPD3v1uV3BrIf01py57daQ==}
'@types/react-spinkit@3.0.10':
resolution: {integrity: sha512-grNfPdesm/xVJPyohfW752bM8N9kuJUx2yFo0I41mZwF3BuXt4+IV4TwaCPcBtA1V3C5r2NUPyqfEUpNTtWbvA==}
@@ -9508,9 +9508,6 @@ packages:
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
engines: {node: '>=4.0'}
jump.js@1.0.1:
resolution: {integrity: sha512-KXS4lfs3AcGdYl7cXk4X9mHb6PWIdrvBzGJs/QnG2g6niRoORkwisedbdjKu0VyUYmE2x7V+cV9ypSpaGRL0zg==}
just-curry-it@3.2.1:
resolution: {integrity: sha512-Q8206k8pTY7krW32cdmPsP+DqqLgWx/hYPSj9/+7SYqSqz7UuwPbfSe07lQtvuuaVyiSJveXk0E5RydOuWwsEg==}
@@ -11784,11 +11781,11 @@ packages:
peerDependencies:
react: '>=16.8'
react-scrollable-anchor@0.6.1:
resolution: {integrity: sha512-baaZKLXEmmaJzVXthx57K0jFgZjRiSEkhVTTssX03bzB097lIE1BYPVgtpgpAoETxTbiM+vDRJ2IPtClSQlj0A==}
react-scroll@1.9.0:
resolution: {integrity: sha512-mamNcaX9Ng+JeSbBu97nWwRhYvL2oba+xR2GxvyXsbDeGP+gkYIKZ+aDMMj/n20TbV9SCWm/H7nyuNTSiXA6yA==}
peerDependencies:
react: ^15.3.0 || ^16.0.0
react-dom: ^15.3.0 || ^16.0.0
react: ^15.5.4 || ^16.0.0 || ^17.0.0 || ^18.0.0
react-dom: ^15.5.4 || ^16.0.0 || ^17.0.0 || ^18.0.0
react-shallow-renderer@16.15.0:
resolution: {integrity: sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==}
@@ -19493,7 +19490,7 @@ snapshots:
dependencies:
'@types/react': 17.0.83
'@types/react-scrollable-anchor@0.6.4':
'@types/react-scroll@1.8.10':
dependencies:
'@types/react': 17.0.83
@@ -25883,8 +25880,6 @@ snapshots:
object.assign: 4.1.5
object.values: 1.1.7
jump.js@1.0.1: {}
just-curry-it@3.2.1: {}
jwa@1.4.1:
@@ -28658,9 +28653,9 @@ snapshots:
'@remix-run/router': 1.11.0
react: 17.0.2
react-scrollable-anchor@0.6.1(react-dom@17.0.2(react@17.0.2))(react@17.0.2):
react-scroll@1.9.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2):
dependencies:
jump.js: 1.0.1
lodash.throttle: 4.1.1
prop-types: 15.8.1
react: 17.0.2
react-dom: 17.0.2(react@17.0.2)