feat: improve landing page (#36826)

* feat/ add smooth scroll from landing to map

* feat: scroll to currentChallengeId if it exist

* fix: update tests

* refactor: migrate from componentWillMount

* fix: update Map and Block's tests and mocks


Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
Ahmad Abdolsaheb
2019-10-04 18:32:35 +03:00
committed by mrugesh
parent f9a112b43e
commit 203ca92a20
21 changed files with 565 additions and 375 deletions

View File

@@ -4,12 +4,13 @@ import React from 'react';
import ShallowRenderer from 'react-test-renderer/shallow';
import Enzyme, { shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import store from 'store';
import { Map } from './';
import mockChallengeNodes from '../../__mocks__/challenge-nodes';
import mockIntroNodes from '../../__mocks__/intro-nodes';
import { dasherize } from '../../../../utils/slugs';
Enzyme.configure({ adapter: new Adapter() });
const renderer = new ShallowRenderer();
@@ -21,11 +22,15 @@ const baseProps = {
resetExpansion: () => {}
};
// set .scrollTo to avoid errors in default test environment
window.scrollTo = jest.fn();
test('<Map /> snapshot', () => {
const componentToRender = (
<Map
introNodes={mockIntroNodes}
nodes={mockChallengeNodes}
resetExpansion={() => {}}
toggleBlock={() => {}}
toggleSuperBlock={() => {}}
/>
@@ -36,42 +41,74 @@ test('<Map /> snapshot', () => {
describe('<Map/>', () => {
describe('after reload', () => {
let initializeSpy = null;
beforeEach(() => {
initializeSpy = jest.spyOn(Map.prototype, 'initializeExpandedState');
});
afterEach(() => {
initializeSpy.mockRestore();
store.clearAll();
});
// 7 was chosen because it has a different superblock from the first node.
const currentChallengeId = mockChallengeNodes[7].id;
const defaultNode = mockChallengeNodes[0];
const idNode = mockChallengeNodes[7];
const hashNode = mockChallengeNodes[9];
const currentChallengeId = idNode.id;
const hash = dasherize(hashNode.superBlock);
it('should expand the block with the most recent challenge', () => {
const initializeSpy = jest.spyOn(
Map.prototype,
'initializeExpandedState'
);
const blockSpy = jest.fn();
const superSpy = jest.fn();
const props = {
...baseProps,
toggleBlock: blockSpy,
toggleSuperBlock: superSpy
};
const mapToRender = <Map {...props} />;
shallow(mapToRender);
expect(blockSpy).toHaveBeenCalledTimes(1);
expect(superSpy).toHaveBeenCalledTimes(1);
expect(initializeSpy).toHaveBeenCalledTimes(1);
initializeSpy.mockRestore();
});
it('should use the hash prop if it exists', () => {
const blockSpy = jest.fn();
const superSpy = jest.fn();
const props = {
...baseProps,
hash,
toggleBlock: blockSpy,
toggleSuperBlock: superSpy,
currentChallengeId
};
const mapToRender = <Map {...props} />;
shallow(mapToRender);
expect(blockSpy).toHaveBeenCalledTimes(1);
// the block here should always be the first block of the superblock
// this is tested implicitly, as there is a second block in the mock nodes
expect(blockSpy).toHaveBeenCalledWith(hashNode.block);
expect(superSpy).toHaveBeenCalledTimes(1);
expect(superSpy).toHaveBeenCalledWith(hashNode.superBlock);
});
it('should use the currentChallengeId prop if there is no hash', () => {
const blockSpy = jest.fn();
const superSpy = jest.fn();
const props = {
...baseProps,
toggleBlock: blockSpy,
toggleSuperBlock: superSpy,
currentChallengeId: currentChallengeId
currentChallengeId
};
const mapToRender = <Map {...props} />;
shallow(mapToRender);
expect(blockSpy).toHaveBeenCalledTimes(1);
expect(blockSpy).toHaveBeenCalledWith(mockChallengeNodes[7].block);
expect(blockSpy).toHaveBeenCalledWith(idNode.block);
expect(superSpy).toHaveBeenCalledTimes(1);
expect(superSpy).toHaveBeenCalledWith(mockChallengeNodes[7].superBlock);
});
it('should use the currentChallengeId prop if it exists', () => {
const props = { ...baseProps, currentChallengeId };
const mapToRender = <Map {...props} />;
shallow(mapToRender);
expect(initializeSpy).toHaveBeenCalledTimes(1);
expect(initializeSpy).toHaveBeenCalledWith(currentChallengeId);
expect(superSpy).toHaveBeenCalledWith(idNode.superBlock);
});
it('should default to the first challenge otherwise', () => {
@@ -85,10 +122,10 @@ describe('<Map/>', () => {
const mapToRender = <Map {...props} />;
shallow(mapToRender);
expect(blockSpy).toHaveBeenCalledTimes(1);
expect(blockSpy).toHaveBeenCalledWith(mockChallengeNodes[0].block);
expect(blockSpy).toHaveBeenCalledWith(defaultNode.block);
expect(superSpy).toHaveBeenCalledTimes(1);
expect(superSpy).toHaveBeenCalledWith(mockChallengeNodes[0].superBlock);
expect(superSpy).toHaveBeenCalledWith(defaultNode.superBlock);
});
it('calls resetExpansion when initializing', () => {

View File

@@ -120,7 +120,7 @@ exports[`<Map /> snapshot: Map 1`] = `
"dashedName": "challenge-one",
"fields": Object {
"blockName": "Block A",
"slug": "/super-block-one/block-a/challenge-one",
"slug": "/super-block-two/block-a/challenge-one",
},
"id": "f",
"isPrivate": false,
@@ -133,7 +133,7 @@ exports[`<Map /> snapshot: Map 1`] = `
"dashedName": "challenge-two",
"fields": Object {
"blockName": "Block A",
"slug": "/super-block-one/block-a/challenge-two",
"slug": "/super-block-two/block-a/challenge-two",
},
"id": "g",
"isPrivate": false,
@@ -146,7 +146,7 @@ exports[`<Map /> snapshot: Map 1`] = `
"dashedName": "challenge-one",
"fields": Object {
"blockName": "Block B",
"slug": "/super-block-one/block-b/challenge-one",
"slug": "/super-block-two/block-b/challenge-one",
},
"id": "h",
"isPrivate": false,
@@ -159,7 +159,7 @@ exports[`<Map /> snapshot: Map 1`] = `
"dashedName": "challenge-two",
"fields": Object {
"blockName": "Block B",
"slug": "/super-block-one/block-b/challenge-two",
"slug": "/super-block-two/block-b/challenge-two",
},
"id": "i",
"isPrivate": false,
@@ -167,6 +167,32 @@ exports[`<Map /> snapshot: Map 1`] = `
"superBlock": "Super Block Two",
"title": "Challenge Two",
},
Object {
"block": "block-a",
"dashedName": "challenge-one",
"fields": Object {
"blockName": "Block A",
"slug": "/super-block-three/block-a/challenge-one",
},
"id": "j",
"isPrivate": false,
"isRequired": false,
"superBlock": "Super Block Three",
"title": "Challenge One",
},
Object {
"block": "block-c",
"dashedName": "challenge-two",
"fields": Object {
"blockName": "Block C",
"slug": "/super-block-three/block-c/challenge-two",
},
"id": "k",
"isPrivate": false,
"isRequired": false,
"superBlock": "Super Block Three",
"title": "Challenge Two",
},
]
}
superBlock="Super Block One"
@@ -275,7 +301,7 @@ exports[`<Map /> snapshot: Map 1`] = `
"dashedName": "challenge-one",
"fields": Object {
"blockName": "Block A",
"slug": "/super-block-one/block-a/challenge-one",
"slug": "/super-block-two/block-a/challenge-one",
},
"id": "f",
"isPrivate": false,
@@ -288,7 +314,7 @@ exports[`<Map /> snapshot: Map 1`] = `
"dashedName": "challenge-two",
"fields": Object {
"blockName": "Block A",
"slug": "/super-block-one/block-a/challenge-two",
"slug": "/super-block-two/block-a/challenge-two",
},
"id": "g",
"isPrivate": false,
@@ -301,7 +327,7 @@ exports[`<Map /> snapshot: Map 1`] = `
"dashedName": "challenge-one",
"fields": Object {
"blockName": "Block B",
"slug": "/super-block-one/block-b/challenge-one",
"slug": "/super-block-two/block-b/challenge-one",
},
"id": "h",
"isPrivate": false,
@@ -314,7 +340,7 @@ exports[`<Map /> snapshot: Map 1`] = `
"dashedName": "challenge-two",
"fields": Object {
"blockName": "Block B",
"slug": "/super-block-one/block-b/challenge-two",
"slug": "/super-block-two/block-b/challenge-two",
},
"id": "i",
"isPrivate": false,
@@ -322,10 +348,217 @@ exports[`<Map /> snapshot: Map 1`] = `
"superBlock": "Super Block Two",
"title": "Challenge Two",
},
Object {
"block": "block-a",
"dashedName": "challenge-one",
"fields": Object {
"blockName": "Block A",
"slug": "/super-block-three/block-a/challenge-one",
},
"id": "j",
"isPrivate": false,
"isRequired": false,
"superBlock": "Super Block Three",
"title": "Challenge One",
},
Object {
"block": "block-c",
"dashedName": "challenge-two",
"fields": Object {
"blockName": "Block C",
"slug": "/super-block-three/block-c/challenge-two",
},
"id": "k",
"isPrivate": false,
"isRequired": false,
"superBlock": "Super Block Three",
"title": "Challenge Two",
},
]
}
superBlock="Super Block Two"
/>
<Connect(SuperBlock)
introNodes={
Array [
Object {
"fields": Object {
"slug": "/super-block-one/block-a",
},
"frontmatter": Object {
"block": "Block A",
"title": "Introduction to Block A",
},
},
Object {
"fields": Object {
"slug": "/super-block-one/block-b",
},
"frontmatter": Object {
"block": "Block B",
"title": "Introduction to Block B",
},
},
Object {
"fields": Object {
"slug": "/super-block-one/block-c",
},
"frontmatter": Object {
"block": "Block C",
"title": "Introduction to Block C",
},
},
]
}
nodes={
Array [
Object {
"block": "block-a",
"dashedName": "challenge-one",
"fields": Object {
"blockName": "Block A",
"slug": "/super-block-one/block-a/challenge-one",
},
"id": "a",
"isPrivate": false,
"isRequired": false,
"superBlock": "Super Block One",
"title": "Challenge One",
},
Object {
"block": "block-a",
"dashedName": "challenge-two",
"fields": Object {
"blockName": "Block A",
"slug": "/super-block-one/block-a/challenge-two",
},
"id": "b",
"isPrivate": false,
"isRequired": false,
"superBlock": "Super Block One",
"title": "Challenge Two",
},
Object {
"block": "block-b",
"dashedName": "challenge-one",
"fields": Object {
"blockName": "Block B",
"slug": "/super-block-one/block-b/challenge-one",
},
"id": "c",
"isPrivate": false,
"isRequired": false,
"superBlock": "Super Block One",
"title": "Challenge One",
},
Object {
"block": "block-b",
"dashedName": "challenge-two",
"fields": Object {
"blockName": "Block B",
"slug": "/super-block-one/block-b/challenge-two",
},
"id": "d",
"isPrivate": false,
"isRequired": false,
"superBlock": "Super Block One",
"title": "Challenge Two",
},
Object {
"block": "block-c",
"dashedName": "challenge-one",
"fields": Object {
"blockName": "Block C",
"slug": "/super-block-one/block-c/challenge-one",
},
"id": "e",
"isPrivate": true,
"isRequired": false,
"superBlock": "Super Block One",
"title": "Challenge One",
},
Object {
"block": "block-a",
"dashedName": "challenge-one",
"fields": Object {
"blockName": "Block A",
"slug": "/super-block-two/block-a/challenge-one",
},
"id": "f",
"isPrivate": false,
"isRequired": false,
"superBlock": "Super Block Two",
"title": "Challenge One",
},
Object {
"block": "block-a",
"dashedName": "challenge-two",
"fields": Object {
"blockName": "Block A",
"slug": "/super-block-two/block-a/challenge-two",
},
"id": "g",
"isPrivate": false,
"isRequired": false,
"superBlock": "Super Block Two",
"title": "Challenge Two",
},
Object {
"block": "block-b",
"dashedName": "challenge-one",
"fields": Object {
"blockName": "Block B",
"slug": "/super-block-two/block-b/challenge-one",
},
"id": "h",
"isPrivate": false,
"isRequired": false,
"superBlock": "Super Block Two",
"title": "Challenge One",
},
Object {
"block": "block-b",
"dashedName": "challenge-two",
"fields": Object {
"blockName": "Block B",
"slug": "/super-block-two/block-b/challenge-two",
},
"id": "i",
"isPrivate": false,
"isRequired": false,
"superBlock": "Super Block Two",
"title": "Challenge Two",
},
Object {
"block": "block-a",
"dashedName": "challenge-one",
"fields": Object {
"blockName": "Block A",
"slug": "/super-block-three/block-a/challenge-one",
},
"id": "j",
"isPrivate": false,
"isRequired": false,
"superBlock": "Super Block Three",
"title": "Challenge One",
},
Object {
"block": "block-c",
"dashedName": "challenge-two",
"fields": Object {
"blockName": "Block C",
"slug": "/super-block-three/block-c/challenge-two",
},
"id": "k",
"isPrivate": false,
"isRequired": false,
"superBlock": "Super Block Three",
"title": "Challenge Two",
},
]
}
superBlock="Super Block Three"
/>
<Spacer />
</ul>
</div>

View File

@@ -13,6 +13,7 @@ import { blockNameify } from '../../../../utils/blockNameify';
import GreenPass from '../../../assets/icons/GreenPass';
import GreenNotCompleted from '../../../assets/icons/GreenNotCompleted';
import IntroInformation from '../../../assets/icons/IntroInformation';
import { dasherize } from '../../../../../utils/slugs';
const mapStateToProps = (state, ownProps) => {
const expandedSelector = makeExpandedBlockSelector(ownProps.blockDashedName);
@@ -92,6 +93,11 @@ export class Block extends Component {
return (
<li
className={'map-challenge-title' + completedClass}
id={
challenge.title
? dasherize(challenge.title)
: dasherize(challenge.frontmatter.title)
}
key={'map-challenge' + challenge.fields.slug}
>
<span className='badge map-badge'>

View File

@@ -4,6 +4,7 @@ import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { uniq, find } from 'lodash';
import { dasherize } from '../../../../../utils/slugs';
import Block from './Block';
@@ -88,7 +89,10 @@ export class SuperBlock extends Component {
render() {
const { superBlock, isExpanded, toggleSuperBlock } = this.props;
return (
<li className={`superblock ${isExpanded ? 'open' : ''}`}>
<li
className={`superblock ${isExpanded ? 'open' : ''}`}
id={dasherize(superBlock)}
>
<button
aria-expanded={isExpanded}
className='map-title'

View File

@@ -28,7 +28,7 @@ exports[`<Block /> not expanded snapshot: block-not-expanded 1`] = `
/>
</span>
<span>
2/4
2/5
</span>
</div>
</button>
@@ -64,13 +64,14 @@ exports[`<Block expanded snapshot: block-expanded 1`] = `
/>
</span>
<span>
2/4
2/5
</span>
</div>
</button>
<ul>
<li
className="map-challenge-title"
id="introduction-to-block-a"
>
<span
className="badge map-badge"
@@ -94,6 +95,7 @@ exports[`<Block expanded snapshot: block-expanded 1`] = `
</li>
<li
className="map-challenge-title map-challenge-title-completed"
id="challenge-one"
>
<span
className="badge map-badge"
@@ -117,6 +119,7 @@ exports[`<Block expanded snapshot: block-expanded 1`] = `
</li>
<li
className="map-challenge-title"
id="challenge-two"
>
<span
className="badge map-badge"
@@ -140,6 +143,7 @@ exports[`<Block expanded snapshot: block-expanded 1`] = `
</li>
<li
className="map-challenge-title"
id="challenge-one"
>
<span
className="badge map-badge"
@@ -156,13 +160,14 @@ exports[`<Block expanded snapshot: block-expanded 1`] = `
</span>
<mockConstructor
onClick={[Function]}
to="/super-block-one/block-a/challenge-one"
to="/super-block-two/block-a/challenge-one"
>
Challenge One
</mockConstructor>
</li>
<li
className="map-challenge-title map-challenge-title-completed"
id="challenge-two"
>
<span
className="badge map-badge"
@@ -179,11 +184,35 @@ exports[`<Block expanded snapshot: block-expanded 1`] = `
</span>
<mockConstructor
onClick={[Function]}
to="/super-block-one/block-a/challenge-two"
to="/super-block-two/block-a/challenge-two"
>
Challenge Two
</mockConstructor>
</li>
<li
className="map-challenge-title"
id="challenge-one"
>
<span
className="badge map-badge"
>
<GreenNotCompleted
style={
Object {
"height": "15px",
"marginRight": "10px",
"width": "15px",
}
}
/>
</span>
<mockConstructor
onClick={[Function]}
to="/super-block-three/block-a/challenge-one"
>
Challenge One
</mockConstructor>
</li>
</ul>
</li>
`;

View File

@@ -3,6 +3,7 @@
exports[`<SuperBlock /> expanded snapshot: superBlock-expanded 1`] = `
<li
className="superblock open"
id="super-block-one"
>
<button
aria-expanded={true}
@@ -141,6 +142,7 @@ exports[`<SuperBlock /> expanded snapshot: superBlock-expanded 1`] = `
exports[`<SuperBlock /> not expanded snapshot: superBlock-not-expanded 1`] = `
<li
className="superblock "
id="super-block-one"
>
<button
aria-expanded={false}

View File

@@ -5,6 +5,7 @@ import { bindActionCreators } from 'redux';
import PropTypes from 'prop-types';
import uniq from 'lodash/uniq';
import { createSelector } from 'reselect';
import { scroller } from 'react-scroll';
import SuperBlock from './components/SuperBlock';
import Spacer from '../helpers/Spacer';
@@ -13,9 +14,11 @@ import './map.css';
import { ChallengeNode } from '../../redux/propTypes';
import { toggleSuperBlock, toggleBlock, resetExpansion } from './redux';
import { currentChallengeIdSelector } from '../../redux';
import { dasherize } from '../../../../utils/slugs';
const propTypes = {
currentChallengeId: PropTypes.string,
hash: PropTypes.string,
introNodes: PropTypes.arrayOf(
PropTypes.shape({
fields: PropTypes.shape({ slug: PropTypes.string.isRequired }),
@@ -52,17 +55,56 @@ function mapDispatchToProps(dispatch) {
}
export class Map extends Component {
componentDidMount() {
this.initializeExpandedState(this.props.currentChallengeId);
constructor(props) {
super(props);
this.state = { idToScrollto: null };
this.initializeExpandedState();
}
initializeExpandedState(currentChallengeId) {
this.props.resetExpansion();
const { superBlock, block } = currentChallengeId
? this.props.nodes.find(node => node.id === currentChallengeId)
: this.props.nodes[0];
this.props.toggleBlock(block);
this.props.toggleSuperBlock(superBlock);
componentDidMount() {
if (this.state.idToScrollto) {
window.scrollTo(0, 0);
scroller.scrollTo(this.state.idToScrollto, {
duration: 1500,
smooth: 'easeInOutQuint',
offset: -35
});
}
}
// As this happens in the constructor, it's necessary to manipulate state
// directly.
initializeExpandedState() {
const {
currentChallengeId,
hash,
nodes,
resetExpansion,
toggleBlock,
toggleSuperBlock
} = this.props;
resetExpansion();
let node;
// find the challenge that has the same superblock with hash
if (hash) {
node = nodes.find(node => dasherize(node.superBlock) === hash);
// eslint-disable-next-line react/no-direct-mutation-state
if (node) this.state = { idToScrollto: dasherize(node.superBlock) };
}
// if there is no hash or the hash did not match any challenge superblock
// and there was a currentChallengeId
if (!node && currentChallengeId) {
node = nodes.find(node => node.id === currentChallengeId);
// eslint-disable-next-line react/no-direct-mutation-state
if (node) this.state = { idToScrollto: dasherize(node.title) };
}
if (!node) node = nodes[0];
toggleBlock(node.block);
toggleSuperBlock(node.superBlock);
}
renderSuperBlocks(superBlocks) {

View File

@@ -3,6 +3,7 @@ import React from 'react';
import ShallowRenderer from 'react-test-renderer/shallow';
import { IndexPage } from '../../pages';
import mockChallengeNodes from '../../__mocks__/challenge-nodes';
describe('<Landing />', () => {
it('renders when visiting index page and logged out', () => {
@@ -14,6 +15,7 @@ describe('<Landing />', () => {
});
const loggedOutProps = {
data: { allChallengeNode: { edges: mockChallengeNodes } },
fetchState: {
complete: true,
error: null,

View File

@@ -1,28 +1,36 @@
import React, { Fragment } from 'react';
import { Grid, Row, Col, Image } from '@freecodecamp/react-bootstrap';
import { Grid, Row, Col } from '@freecodecamp/react-bootstrap';
import Helmet from 'react-helmet';
import Login from '../Header/components/Login';
import PropTypes from 'prop-types';
import { Link } from 'gatsby';
import { uniq } from 'lodash';
import { Spacer } from '../helpers';
import Login from '../Header/components/Login';
import './landing.css';
import '../Map/map.css';
const propTypes = {
edges: PropTypes.array
};
const BigCallToAction = () => (
<Row>
<Col sm={10} smOffset={1} xs={12}>
<Login block={true}>Sign in and get started.</Login>
<Login block={true}>Sign in and get started (it's free)</Login>
</Col>
</Row>
);
function Landing() {
export const Landing = ({ edges }) => {
const superBlocks = uniq(edges.map(element => element.node.superBlock));
return (
<Fragment>
<Helmet>
<title>Learn to code | freeCodeCamp.org</title>
</Helmet>
<main className='index-page'>
<Spacer size={2} />
<main className='landing-page'>
<Spacer />
<Grid>
<Row>
<Col sm={10} smOffset={1} xs={12}>
@@ -31,33 +39,36 @@ function Landing() {
</h1>
<Spacer />
<h2 className='medium-heading'>Learn to code.</h2>
<h2 className='medium-heading'>Build projects.</h2>
<h2 className='medium-heading'>Earn certifications.</h2>
<h2 className='medium-heading'>
Build projects and earn certifications.
</h2>
<h2 className='medium-heading'>
Grow your portfolio and get a developer job.
</h2>
<h2 className='medium-heading'>
It's all 100% free thanks to our nonprofit's donors.
Since 2014, more than 40,000 freeCodeCamp.org graduates have
gotten jobs at tech companies including:
</h2>
<div className='logo-row'>
<h2 className='medium-heading'>Apple</h2>
<h2 className='medium-heading'>Google</h2>
<h2 className='medium-heading'>Amazon</h2>
<h2 className='medium-heading'>Microsoft</h2>
<h2 className='medium-heading'>Spotify</h2>
</div>
</Col>
</Row>
<Spacer />
<BigCallToAction />
<Spacer size={2} />
<Image
alt='companies featuring freeCodeCamp.org'
className='img-center'
responsive={true}
src='https://cdn-media-1.freecodecamp.org/learn/as-seen-on.png'
/>
<Spacer />
<Row>
<Col sm={10} smOffset={1} xs={12}>
<h2 className='medium-heading'>
Since 2014, more than 40,000 freeCodeCamp.org graduates have
gotten jobs in tech.
</h2>
<h2 className='medium-heading'>Certifications:</h2>
<ul>
{superBlocks.map((superBlock, i) => (
<li className={'superblock'} key={i}>
<Link state={{ superBlock: superBlock }} to={`/learn`}>
<h2 className='medium-heading'>{superBlock}</h2>
</Link>
</li>
))}
</ul>
</Col>
</Row>
<Spacer />
@@ -65,8 +76,8 @@ function Landing() {
</main>
</Fragment>
);
}
};
Landing.displayName = 'Landing';
Landing.propTypes = propTypes;
export default Landing;

View File

@@ -1,47 +1,7 @@
.black-text {
color: var(--secondary-color);
font-weight: 400;
font-size: 40px;
}
.large-p {
font-size: 24px;
}
.img-center {
margin: 0 auto;
}
.landing-icon {
height: 100px;
width: 100px;
}
.landing-skill-icon {
color: var(--secondary-color);
margin-top: -15px;
padding-bottom: 15px;
height: 150px;
margin-bottom: 1.45rem;
}
.testimonial-image {
border-radius: 5px;
height: 200px;
width: 200px;
color: var(--secondary-color);
}
.testimonial-copy {
text-align: center;
font-size: 18px !important;
margin-left: 20px;
margin-right: 20px;
}
.underlined-link {
text-decoration: underline;
}
/* Buttons with a lot of text can overflow and mess up formatting on small
screens, this stops that unless the word itself is too large. */
@@ -49,13 +9,20 @@
white-space: pre-line;
}
@media (min-width: 991px) and (max-width: 1199px) {
.testimonial-copy {
height: 150px;
}
.logo-row {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-around;
align-content: center;
}
@media (min-width: 1200px) {
.testimonial-copy {
height: 100px;
}
.logo-row h2 {
height: 35px;
padding: 0 10px 0 10px;
}
.landing-page ul {
list-style: none;
padding-left: 0px;
}

View File

@@ -46,6 +46,7 @@ const loggedInProps = {
user: {
name: 'Development User'
},
location: { hash: '' },
data: {
challengeNode: {
fields: {