Skip to content

Commit

Permalink
416 417 homepage updates (#434)
Browse files Browse the repository at this point in the history
* started on scaffold

* completed how does it work page, added it to navlink and how it works section on the home page

* removed unused import from routes

* resolving conflicts from new merges

* resolve more conflicts

* Upgrade react packages to latest

* Remove deprecated react-router-redux package

* Added to nav bar

* Reverse react-router packages to previous versions

* Update react-google-maps withScriptjs import statement

* Fix react-modal accessibility issue

* Remove --browser chrome from cypress script

* Move to SFC, cleanup typo in routing

* changed navbar to not fluid to remove horizontal content overflow/scrolling on mobile

* WIP: Steps component (without animations)

* small firebase typo fixes

* added cypress tests and AutoSuggestInput(partial functionality) for steps component

* added vertical option for Steps on homepage and progress bars

* added basic step sliding animations with react-spring

* added animations to recycling rate bars

* updated recycling info section

* fixed centering on steps slider component

* updated styles on HeroCTA and Homepage

* added HeroCTA to tenant focused secondary homepage

* moved search bar from inside sliding steps component to below it

* made height of sliding steps component responsive to currently visible/active step content height

* set steps component default to hide internal prev/next buttons, added custom external buttons using a ref in HowItWorks(tenant homepage) page for improved visibility

* made next step's selector oscillate colors between yellow and green to make interactivity clear to users

* added autoSlide and autoSlideDelay options to steps component that automate sliding through the steps until a user clicks on a step navigation button

* more responsive font sizes for text over skyline image

* minor style changes to search bar section of tenant homepage

* remove RecyclingInfo component from homepage

* changed default autoSlideDelay on steps component to 4 seconds

* changed content of first step on homepage vertical steps slider

* fixed memory leak error by clearing intervals in steps component

* updated cypress tests for the how it works page

* refactored/removed some cypress tests to pass with new wireframe updates to UI

* refactored tenant or property manager choice on home page to be its own component, minor style change to buttons, and pointed property manager button link to manager-resources page

* set autoFocus to false on AutoSuggestInput component search bar so that the tenant homepage doesn't automatically scroll to the bottom on every visit

* removed unnecessary package-lock.json file that must have been accidentally committed to the deprecated how-does-it-work branch a long while ago

* removed unnecessary imports causing warnings that don't allow travis ci tests to pass

* fixing many travis ci warnings/errors

* fixing many travis ci warnings/errors

* hopefully last fixes for travis ci errors

* made steps component nav buttons accessible by keyboard, removed some more unused imports/vars

* removed deprecated SectionSlice component

* moved Steps component nav buttons to separate component: StepsMainNav

* made step selector buttons use semantic <button> tags instead of divs

* cleaning up and refactoring Steps component to not use refs anti-pattern and be more reusable

* more cleaning: destructuring props, binding this with es6 arrow functions instead of .bind(this)
  • Loading branch information
julian009 authored and hoop71 committed Nov 3, 2018
1 parent 8b40696 commit 7e7d9f4
Show file tree
Hide file tree
Showing 50 changed files with 1,957 additions and 273 deletions.
Binary file added .DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"react-router-redux": "^4.0.8",
"react-scripts": "1.1.5",
"react-share": "^2.0.0",
"react-spring": "^5.8.0",
"redux": "^4.0.0",
"redux-logger": "^3.0.6",
"redux-persist": "^5.10.0",
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/AutoSuggestInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class AutoSuggestInput extends Component {
type: 'text',
value: this.state.address,
onChange: this.onChange,
autoFocus: true,
autoFocus: false,
placeholder: 'Search your address'
};

Expand Down
17 changes: 17 additions & 0 deletions client/src/components/Banner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Row, Col } from 'react-bootstrap';

const Banner = props => (
<Row className="banner-row">
<Col xs={12}>
<div className="banner-content">{props.children}</div>
</Col>
</Row>
);

Banner.propTypes = {
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired
};

export default Banner;
58 changes: 8 additions & 50 deletions client/src/components/HeroCTA.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Grid, Row, Col, Button } from 'react-bootstrap';
import AutoSuggestInput from './AutoSuggestInput';
import { Grid } from 'react-bootstrap';

const HeroCTA = ({
openMap,
firebaseSearchAddressFlow,
clearInitialSearchResults,
firebaseCampaigns,
router
}) => (
const HeroCTA = () => (
<Grid fluid>
<div className="tinted" />
<Row className="hero-wrapper">
<Col className="hero-page-header" xs={12}>
<div className="hero-wrapper">
<div className="hero-page-header">
<div className="opacity-div">
<h1>NEED RECYCLING?</h1>
<h2 className="font-italic">Recruit, Request, Recycle</h2>
<Row>
<Col xs={10} xsOffset={1}>
<h3>
We have a mission to change Denver's low recycling rate by making it easy for you
and your neighbors to petition your landlord for recyling for your building.
</h3>
</Col>
</Row>
<h1>NEED RECYCLING IN YOUR APARTMENT OR CONDO?</h1>
<div className="font-italic">Recruit, Request, Recycle</div>
</div>
</Col>
</Row>
<Row>
<Col xs={12} className="hero-search">
<form>
<AutoSuggestInput
firebaseSearchAddressFlow={firebaseSearchAddressFlow}
clearInitialSearchResults={clearInitialSearchResults}
firebaseCampaigns={firebaseCampaigns}
router={router}
/>
<div className="text-center">
<Button className="map-btn" bsStyle="as-link" onClick={openMap}>
Explore Nearby Campaigns
</Button>
</div>
</form>
</Col>
</Row>
</div>
</div>
</Grid>
);

HeroCTA.propTypes = {
openMap: PropTypes.func.isRequired,
clearInitialSearchResults: PropTypes.func.isRequired,
firebaseCampaigns: PropTypes.shape({}).isRequired,
firebaseSearchAddressFlow: PropTypes.func.isRequired,
router: PropTypes.shape({}).isRequired
};

export default HeroCTA;
189 changes: 189 additions & 0 deletions client/src/components/HowItWorks/HowItWorks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Grid, Row, Col } from 'react-bootstrap';
import { connect } from 'react-redux';
import AutoSuggestInput from '../AutoSuggestInput';
import {
firebaseSearchAddressFlow,
clearInitialSearchResults
} from '../../redux/actions/firebaseInitialSearch';

// import screenShot from '../../images/screen-shot.png';
// import list from '../../images/list.png';
// import letter from '../../images/letter.png';
// import smallList from '../../images/small-list.png';
// import guideline1 from '../../images/guildline1.png';
// import guideline2 from '../../images/guideline2.png';

import HeroCTA from '../HeroCTA';
import Steps from './Steps';
import Footer from '../Footer/Footer';
import HowItWorksStepHeaderContent from './HowItWorksStepHeaderContent';
import HowItWorksStepContent from './HowItWorksStepContent';

const stepsData = [
{
title: 'Create or Join a Campaign',
icon: <i className="fa fa-bullhorn how-icon" />,
prevStepBtn: <i className="fa fa-angle-left" style={{ fontSize: '5rem' }} />,
nextStepBtn: <i className="fa fa-angle-right" style={{ fontSize: '5rem' }} />,
content: [
{ h3: 'Start by searching for your condo or apartment building address' },
{
p:
`Enter your condo or apartment building address in the search bar below to find out${' '}` +
`if there is a recycling campaign already active for your building. Then, follow${' '}` +
`the on screen instructions to either create a brand new campaign or sign the${' '}` +
'online petition for an existing one.'
}
]
},
{
title: 'Recruit Your Neighbors',
icon: <i className="fa fa-users how-icon" />,
prevStepBtn: <i className="fa fa-angle-left" style={{ fontSize: '5rem' }} />,
nextStepBtn: <i className="fa fa-angle-right" style={{ fontSize: '5rem' }} />,
content: [
{ h3: 'Power in Numbers' },
{
p:
`Now it's time to spread the word about your building's new recycling campaign to${' '}` +
`your neighbors. Gathering signatures from your fellow tenants let's your landlord${' '}` +
'know just how important recycling services are to your community!'
},
{ h3: 'Tools' },
{
ul: [
{
li:
`Print and post this petition in a public space in your apartment building, such${' '}` +
'as a laundry or mail room.'
}
]
}
]
},
{
title: 'Request Recyling From Your Landlord',
icon: <i className="fa fa-comment how-icon" />,
prevStepBtn: <i className="fa fa-angle-left" style={{ fontSize: '5rem' }} />,
nextStepBtn: <i className="fa fa-angle-right" style={{ fontSize: '5rem' }} />,
content: [
{
p:
`Submit the letter to your landlord along with the petition signatures. If${' '}` +
'possible, bring other neighbors along; there is great strength in numbers.'
}
]
},
{
title: 'Recycle!',
icon: <i className="fa fa-recycle how-icon" />,
prevStepBtn: <i className="fa fa-angle-left" style={{ fontSize: '5rem' }} />,
nextStepBtn: <i className="fa fa-angle-right" style={{ fontSize: '5rem' }} />,
content: [
{
p:
`Congratulations for getting recycling services for your building! Best practice${' '}` +
`shows that posting guidelines will help your neighbors recycle correctly. When a${' '}` +
`recycling bin is too contaminated your complex will either be given 1 week to${' '}` +
`clean out the recycling bin or be charged extra to take it to a landfill as trash.${' '}` +
`Common contaminants in the recycing bin include plastic bags because they jam up${' '}` +
`the machine and disposable ware such as red solo cups, disposable cutlery and${' '}` +
'disposable plates.'
}
]
}
];

const HowItWorks = props => {
let currentStep = 0;
if (props.location.state && props.location.action === 'PUSH') {
currentStep = props.location.state.currentStep;
}

const steps = stepsData.map((stepData, index) => ({
headerContent: (
<HowItWorksStepHeaderContent
title={stepData.title}
icon={stepData.icon}
stepIndex={index}
priority={2}
stacked
/>
),
content: <HowItWorksStepContent content={stepData.content} />,
prevStepBtn: stepData.prevStepBtn,
nextStepBtn: stepData.nextStepBtn
}));

return (
<div className="how-it-works-container">
<HeroCTA />
<div className="how-it-works-banner">
<div>
<h1 className="blue-color" style={{ marginLeft: '1em' }}>
Follow these easy steps!
</h1>
</div>
</div>

<Steps pulseNextStep currentStep={currentStep} steps={steps} />

<Grid className="search-container">
<Row>
<Col xs={12} md={10} mdOffset={1}>
<h3 className="call-to-action">Ready? Enter your address!</h3>
</Col>
</Row>
<Row>
<Col xs={12} md={10} mdOffset={1}>
<div style={{ textAlign: 'center' }}>
Start a campaign to bring recycling to your property! Search your address below to get
started.
</div>
</Col>
</Row>
<Row>
<Col xs={12} md={10} mdOffset={1}>
<AutoSuggestInput
firebaseSearchAddressFlow={props.firebaseSearchAddressFlow}
clearInitialSearchResults={props.clearInitialSearchResults}
router={props.router}
/>
</Col>
</Row>
</Grid>

<Footer />
</div>
);
};

HowItWorks.defaultProps = {
location: {
state: {
currentStep: 0
}
}
};

HowItWorks.propTypes = {
router: PropTypes.shape({}).isRequired,
location: PropTypes.shape({
state: PropTypes.shape({
currentStep: PropTypes.number
}),
action: PropTypes.string.isRequired
}).isRequired,
firebaseSearchAddressFlow: PropTypes.func.isRequired,
clearInitialSearchResults: PropTypes.func.isRequired
};

export default connect(
null,
{
firebaseSearchAddressFlow,
clearInitialSearchResults
}
)(HowItWorks);
46 changes: 46 additions & 0 deletions client/src/components/HowItWorks/HowItWorksStepContent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Row, Col, Grid } from 'react-bootstrap';

const buildContents = (content, lvl) => {
const result = [];
for (let i = 0; i < content.length; i += 1) {
const keys = Object.keys(content[i]);
if (keys && keys[0]) {
const DynamicTag = `${keys[0]}`;
const value = content[i][DynamicTag];
if (React.isValidElement(value) || typeof value !== 'object') {
result.push(
<DynamicTag key={`step-content-${keys[0]}-lvl-${lvl}-component-${i}`}>{value}</DynamicTag>
);
} else if (Array.isArray(value)) {
const newElem = (
<DynamicTag key={`step-content-${keys[0]}-lvl-${lvl}-component-${i}`}>
{buildContents(value, lvl + 1)}
</DynamicTag>
);
result.push(newElem);
}
}
}
return result;
};

const HowItWorksStepContent = ({ content }) => {
const result = buildContents(content, 0);
return (
<Grid>
<Row>
<Col xs={12} md={10} mdOffset={1}>
<div className="how-it-works-step-content-wrapper">{result}</div>
</Col>
</Row>
</Grid>
);
};

HowItWorksStepContent.propTypes = {
content: PropTypes.arrayOf(PropTypes.shape({})).isRequired
};

export default HowItWorksStepContent;
29 changes: 29 additions & 0 deletions client/src/components/HowItWorks/HowItWorksStepHeaderContent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import PropTypes from 'prop-types';

const HowItWorksStepHeaderContent = ({ title, icon, stepIndex, priority, stacked }) => {
const classes = stacked ? 'step-header-content stacked' : 'step-header-content';
const HeaderTag = `h${priority}`;
return (
<div className={classes}>
{icon}
<HeaderTag>{`${stepIndex + 1}. ${title}`}</HeaderTag>
</div>
);
};

HowItWorksStepHeaderContent.defaultProps = {
icon: null,
stacked: true,
priority: 2
};

HowItWorksStepHeaderContent.propTypes = {
title: PropTypes.string.isRequired,
icon: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
stepIndex: PropTypes.number.isRequired,
priority: PropTypes.number,
stacked: PropTypes.bool
};

export default HowItWorksStepHeaderContent;
Loading

0 comments on commit 7e7d9f4

Please sign in to comment.