Skip to content
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"fast-sort": "^1.5.6",
"lodash-es": "^4.17.21",
"node-sass": "^4.13.1",
"react-app-polyfill": "^1.0.6",
"react-ga": "^2.7.0",
Expand Down
62 changes: 35 additions & 27 deletions src/lib/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import ReactGA from 'react-ga';
import sort from 'fast-sort';
import runtimeEnv from '@mars/heroku-js-runtime-env';
import { forceCheck } from 'react-lazyload';
import * as _ from 'lodash';

import BetaBanner from "./BetaBanner";
import Splash from "./Splash";
Expand All @@ -25,15 +26,16 @@ const Footer = lazy(() => import('./Footer'));
export default class App extends React.Component {

state = {
/** Array of visible feature points in maps and lists. (visibleFeatures) */
/** Array of all artwork objects that may show in maps and lists. */
allFeatures: [],
visFtrs: [],
/** Array of visible artwork IDs in maps and lists. */
visibleFeatureIds: [],
/** The type of view.
* Options: list, detail, map, filter
* Last two only display differently on mobile. */
viewType: "map",
/** Full object representing active artwork. */
activeFeature: null,
/** Integer representing active artwork ID. */
activeFeatureId: null,
/** Keep track of whether any filters are applied. */
isFiltered: false,
/** Array of year OptionTypes to filter features by. */
Expand Down Expand Up @@ -74,11 +76,11 @@ export default class App extends React.Component {
fetch(this.props.featuresDataSource)
.then(response => response.json())
.then(json => {
const visFtrs = json.features.map(f => {
const allFeatures = json.features.map(f => {
if (!isArtwork(f)) return null
return f
}).filter(Boolean)
this.setState({ allFeatures: visFtrs, visFtrs },
this.setState({ allFeatures, visibleFeatureIds: allFeatures.map(f => f.id) },
// Sort after first load.
() => { this.sortList() }
);
Expand All @@ -103,9 +105,9 @@ export default class App extends React.Component {
})
}

setVisibleFeatures = (visFtrs) => {
setVisibleFeatureIds = (visibleFeatureIds) => {
this.setState(
{visFtrs: visFtrs},
{ visibleFeatureIds },
() => { this.sortList() }
);
}
Expand Down Expand Up @@ -152,7 +154,7 @@ export default class App extends React.Component {
return keepForYear && keepForWard && keepForProgram;
})

this.setVisibleFeatures(visibleFeatures);
this.setVisibleFeatureIds(visibleFeatures.map(f => f.id));
}

handleSelectYears = (selectedOptions) => {
Expand Down Expand Up @@ -212,47 +214,47 @@ export default class App extends React.Component {
switch(this.state.sortType) {
case 'artist-asc':
default:
sortedList = sort(this.state.visFtrs).asc(u => u.properties.title ? u.properties.title.toLowerCase() : u.properties.title)
sortedList = sort(this.state.visibleFeatureIds).asc(id => _.find(this.state.allFeatures, { id }).properties?.title.toLowerCase())
break
case 'artist-desc':
sortedList = sort(this.state.visFtrs).desc(u => u.properties.title ? u.properties.title.toLowerCase() : u.properties.title)
sortedList = sort(this.state.visibleFeatureIds).desc(id => _.find(this.state.allFeatures, { id }).properties?.title.toLowerCase())
break
case 'year-asc':
sortedList = sort(this.state.visFtrs).asc(u => u.properties.year)
sortedList = sort(this.state.visibleFeatureIds).asc(id => _.find(this.state.allFeatures, { id }).properties?.year)
break
case 'year-desc':
sortedList = sort(this.state.visFtrs).desc(u => u.properties.year)
sortedList = sort(this.state.visibleFeatureIds).desc(id => _.find(this.state.allFeatures, { id }).properties?.year)
break
}
this.setState({visFtrs: sortedList})
this.setState({ visibleFeatureIds: sortedList })
}

handleMapClick = (feature) => {
handleMapClick = (featureId) => {
ReactGA.event({
category: 'Map',
action: 'Clicked feature',
label: 'ward or artwork',
})
this.setActiveFeature(feature)
this.setActiveFeatureId(featureId)
}


setActiveFeature = (feature) => {
setActiveFeatureId = (featureId) => {
this.setState({
activeFeature: feature,
activeFeatureId: featureId,
});
}


handleCloseFeature = () => {
const uid = this.state.activeFeature.properties.uid
const uid = this.state.allFeatures[this.state.activeFeatureId].properties.uid
if (typeof(document) !== 'undefined') {
const featureBtn = document.getElementById(uid)
featureBtn.scrollIntoView()
featureBtn.focus()
}
this.setState({
activeFeature: null
activeFeatureId: null
})
}

Expand All @@ -275,14 +277,17 @@ export default class App extends React.Component {
render() {
const {
showSplash,
visFtrs,
activeFeature,
allFeatures,
visibleFeatureIds,
activeFeatureId,
isMobileView,
isFiltered,
viewType,
showWardLayer,
} = this.state;

const activeFeature = _.find(allFeatures, { id: activeFeatureId })

return (
<div className="parent" id="start-map">
<BetaBanner isMobile={isMobileView}/>
Expand All @@ -299,8 +304,9 @@ export default class App extends React.Component {
<Suspense fallback={<div className="loading" />}>
<FeatureList
isMobile={isMobileView}
features={visFtrs}
onItemClick={this.setActiveFeature}
allFeatures={allFeatures}
featureIds={visibleFeatureIds}
onItemClick={this.setActiveFeatureId}
activeFeature={activeFeature}
/>
<FeatureDetail feature={activeFeature} onClose={this.handleCloseFeature} />
Expand All @@ -319,8 +325,9 @@ export default class App extends React.Component {
/>
<FeatureList
isMobile={isMobileView}
features={visFtrs}
onItemClick={this.setActiveFeature}
allFeatures={allFeatures}
featureIds={visibleFeatureIds}
onItemClick={this.setActiveFeatureId}
activeFeature={activeFeature}
/>
<FeatureDetail feature={activeFeature} onClose={this.handleCloseFeature} />
Expand All @@ -331,7 +338,8 @@ export default class App extends React.Component {
<InteractiveMap
isMobile={isMobileView}
onFeatureMapClick={this.handleMapClick}
features={visFtrs}
allFeatures={allFeatures}
visibleFeatureIds={visibleFeatureIds}
activeFeature={activeFeature}
showWardLayer={showWardLayer}
googleApiKey={this.props.googleApiKey}
Expand Down
16 changes: 9 additions & 7 deletions src/lib/components/FeatureList.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,38 @@
import React, { useEffect } from 'react';
import PropTypes from "prop-types";
import { forceCheck } from 'react-lazyload';
import * as _ from 'lodash';
import FeatureListItem from './FeatureListItem';


const FeatureList = ({ features, onItemClick, isMobile, activeFeature }) => {
const FeatureList = ({ allFeatures = [], featureIds = [], onItemClick, isMobile, activeFeature }) => {
useEffect(() => {
forceCheck()
});

return (
<div id="results" className="list-container" role="region" aria-live="polite">
<p className="text-right">{features.length} Results</p>
<p className="text-right">{featureIds.length} Results</p>
<ul id="list">
{features.map(feature =>
<FeatureListItem
{featureIds.map(id => {
const feature = _.find(allFeatures, { id })
return <FeatureListItem
key={feature.properties.uid}
feature={feature}
onClick={onItemClick}
isMobile={isMobile}
activeFeature={activeFeature}
/>
)}
})}
</ul>
</div>
);
}


FeatureListItem.propTypes = {
features: PropTypes.arrayOf(PropTypes.object),
featureIds: PropTypes.arrayOf(PropTypes.number),
onItemClick: PropTypes.func,
}

export default FeatureList
export default FeatureList
4 changes: 2 additions & 2 deletions src/lib/components/FeatureListItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const FeatureListItem = ({ feature, onClick, isMobile, activeFeature }) => {
value: uid,
})

onClick(feature)
onClick(feature.id)
}

const handleKeyPress = (event) => {
Expand Down Expand Up @@ -84,4 +84,4 @@ FeatureListItem.defaultProps = {
media: [],
}

export default FeatureListItem
export default FeatureListItem
18 changes: 11 additions & 7 deletions src/lib/components/InteractiveMap.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { createRef, lazy, Suspense } from 'react';
import PropTypes from "prop-types";
import { Map, Marker, GoogleApiWrapper } from '@nomadiclabs/google-maps-react';
import * as _ from 'lodash';

import * as constants from "../constants";

Expand All @@ -17,8 +18,8 @@ class InteractiveMap extends React.Component {
constructor(props) {
super(props)
this.state = {
prevActiveFeature: {},
features: this.props.features,
allFeatures: this.props.allFeatures,
visibleFeatureIds: this.props.visibleFeatureIds,
wards: {},
}
this.mapRef = createRef()
Expand All @@ -36,8 +37,8 @@ class InteractiveMap extends React.Component {
}

componentDidUpdate(prevProps) {
if (prevProps.features !== this.props.features) {
this.setState({ features: this.props.features })
if (prevProps.visibleFeatureIds !== this.props.visibleFeatureIds) {
this.setState({ visibleFeatureIds: this.props.visibleFeatureIds })
}

if (prevProps.showWardLayer !== this.props.showWardLayer) {
Expand Down Expand Up @@ -125,7 +126,7 @@ class InteractiveMap extends React.Component {

render() {
const { loaded, google, activeFeature, onFeatureMapClick } = this.props;
const { features } = this.state;
const { allFeatures, visibleFeatureIds } = this.state;
const zoom = activeFeature ? constants.MAP_ZOOM_LEVEL.FEATURE : constants.MAP_ZOOM_LEVEL.DEFAULT
const settings = { ...this.mapSettings, zoom }
const center = activeFeature ? null : constants.DEFAULT_MAP_CENTER;
Expand All @@ -146,7 +147,10 @@ class InteractiveMap extends React.Component {
>

{
features.map((feature, i) => {
visibleFeatureIds.map((id) => {
console.log(id)
const feature = _.find(allFeatures, { id });
if (!feature) { console.log(feature) }
const validPrograms = ["StART Support", "Partnership Program", "Outside the Box"]
const program = validPrograms.includes(feature.properties.program) ? feature.properties.program : "Other"
const isSelected = activeFeature && feature.properties.uid === activeFeature.properties.uid
Expand All @@ -162,7 +166,7 @@ class InteractiveMap extends React.Component {
key={feature.properties.uid}
icon={icon}
position={{ lng: feature.geometry.coordinates[0], lat: feature.geometry.coordinates[1] }}
onClick={ () => onFeatureMapClick(feature) }
onClick={ () => onFeatureMapClick(feature.id) }
zIndex={isSelected ? 2 : 1}
/>
)
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/MapMarkers.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const MapMarkers = ({ features, activeFeature, onFeatureMapClick }) => {
key={feature.properties.uid}
position={[feature.geometry.coordinates[1], feature.geometry.coordinates[0]]}
icon={icon}
onClick={() => onFeatureMapClick(feature) }
onClick={() => onFeatureMapClick(feature.id) }
zIndexOffset={isSelected ? 9999 : 0}
/>
)
Expand Down