Fix/clean errors (#191)

* clean dev errors

* add check command

* yarn -> npm in travis

* fix all warnings

* fix base test

* 💄
This commit is contained in:
Lachlan Kermode
2021-01-17 18:16:28 +13:00
committed by GitHub
parent 1f20fd68ec
commit 1bbcd57e5e
35 changed files with 229 additions and 158 deletions

View File

@@ -5,11 +5,10 @@ cache:
directories: directories:
- node_modules - node_modules
before_script: before_script:
- npm install -g yarn
- cp example.config.js config.js - cp example.config.js config.js
install: install:
- yarn - npm install
script: script:
- yarn lint - npm run lint
- yarn build - npm run build
- yarn test - npm run test

View File

@@ -11,7 +11,7 @@
"test": "node scripts/test.js --silent", "test": "node scripts/test.js --silent",
"test:ava": "NODE_ENV=production ava --verbose", "test:ava": "NODE_ENV=production ava --verbose",
"test-watch": "ava --watch", "test-watch": "ava --watch",
"lint": "echo 'This is a placeholder for Travis'", "lint": "prettier --check \"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
"lint:fix": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}\"" "lint:fix": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}\""
}, },
"dependencies": { "dependencies": {

View File

@@ -1,4 +1,3 @@
/* global fetch, alert */
import { urlFromEnv } from "../common/utilities"; import { urlFromEnv } from "../common/utilities";
// TODO: relegate these URLs entirely to environment variables // TODO: relegate these URLs entirely to environment variables

View File

@@ -5,9 +5,11 @@ export const colors = {
white: "#fff", white: "#fff",
}; };
export default { const exports = {
fallbackEventColor: colors.fa_red, fallbackEventColor: colors.fa_red,
darkBackground: colors.black, darkBackground: colors.black,
primaryHighlight: colors.fa_red, primaryHighlight: colors.fa_red,
secondaryHighlight: colors.white, secondaryHighlight: colors.white,
}; };
export default exports;

View File

@@ -380,6 +380,7 @@ export function getFilterIdxFromColorSet(filter, coloringSet) {
coloringSet.map((set, idx) => { coloringSet.map((set, idx) => {
const foundIdx = set.indexOf(filter); const foundIdx = set.indexOf(filter);
if (foundIdx !== -1) filterIdx = idx; if (foundIdx !== -1) filterIdx = idx;
return null;
}); });
return filterIdx; return filterIdx;
} }

View File

@@ -2,7 +2,7 @@ import React from "react";
import Popup from "./presentational/Popup"; import Popup from "./presentational/Popup";
import copy from "../common/data/copy.json"; import copy from "../common/data/copy.json";
export default ({ isOpen, onClose, language, styles }) => ( const Infopopup = ({ isOpen, onClose, language, styles }) => (
<Popup <Popup
title={copy[language].legend.default.header} title={copy[language].legend.default.header}
content={copy[language].legend.default.intro} content={copy[language].legend.default.intro}
@@ -11,3 +11,5 @@ export default ({ isOpen, onClose, language, styles }) => (
styles={styles} styles={styles}
/> />
); );
export default Infopopup;

View File

@@ -1,4 +1,3 @@
/* global alert, Event */
import React from "react"; import React from "react";
import { bindActionCreators } from "redux"; import { bindActionCreators } from "redux";
@@ -11,7 +10,6 @@ import LoadingOverlay from "./Overlay/Loading";
import Map from "./Map.jsx"; import Map from "./Map.jsx";
import Toolbar from "./Toolbar/Layout"; import Toolbar from "./Toolbar/Layout";
import CardStack from "./CardStack.jsx"; import CardStack from "./CardStack.jsx";
// import {CardStack} from '@forensic-architecture/design-system'
import NarrativeControls from "./presentational/Narrative/Controls.js"; import NarrativeControls from "./presentational/Narrative/Controls.js";
import InfoPopup from "./InfoPopup.jsx"; import InfoPopup from "./InfoPopup.jsx";
import Popup from "./presentational/Popup"; import Popup from "./presentational/Popup";

View File

@@ -1,4 +1,4 @@
/* global L, Event */ /* global L */
import React from "react"; import React from "react";
import { Portal } from "react-portal"; import { Portal } from "react-portal";
import Supercluster from "supercluster"; import Supercluster from "supercluster";

View File

@@ -16,11 +16,11 @@ export default class Notification extends React.Component {
if (!items) return ""; if (!items) return "";
return ( return (
<div> <div>
{items.map((item) => { {items.map((item, idx) => {
if (item.error) { if (item.error) {
return <p>{item.error.message}</p>; return <p key={idx}>{item.error.message}</p>;
} }
return ""; return null;
})} })}
</div> </div>
); );
@@ -47,11 +47,12 @@ export default class Notification extends React.Component {
if (notificationsToRender.length > 0) { if (notificationsToRender.length > 0) {
return ( return (
<div className="notification-wrapper"> <div className="notification-wrapper">
{this.props.notifications.map((notification) => { {this.props.notifications.map((notification, idx) => {
return ( return (
<div <div
className="notification" className="notification"
onClick={() => this.toggleDetails()} onClick={() => this.toggleDetails()}
key={idx}
> >
<button <button
onClick={this.props.onToggle} onClick={this.props.onToggle}

View File

@@ -5,7 +5,7 @@ import Md from "./Md";
import Spinner from "../presentational/Spinner"; import Spinner from "../presentational/Spinner";
import NoSource from "../presentational/NoSource"; import NoSource from "../presentational/NoSource";
export default ({ media, viewIdx, translations, switchLanguage, langIdx }) => { const Content = ({ media, viewIdx, translations, switchLanguage, langIdx }) => {
const el = document.querySelector(".source-media-gallery"); const el = document.querySelector(".source-media-gallery");
const shiftW = el ? el.getBoundingClientRect().width : 0; const shiftW = el ? el.getBoundingClientRect().width : 0;
@@ -71,7 +71,7 @@ export default ({ media, viewIdx, translations, switchLanguage, langIdx }) => {
</div> </div>
); );
case "Document": case "Document":
return <iframe className="source-document" src={path} />; return <iframe title={path} className="source-document" src={path} />;
default: default:
return ( return (
<NoSource <NoSource
@@ -92,3 +92,5 @@ export default ({ media, viewIdx, translations, switchLanguage, langIdx }) => {
</div> </div>
); );
}; };
export default Content;

View File

@@ -1,6 +1,6 @@
import React from "react"; import React from "react";
export default ({ viewIdx, paths, onShiftHandler }) => { const OverlayControls = ({ viewIdx, paths, onShiftHandler }) => {
const backArrow = const backArrow =
viewIdx !== 0 ? ( viewIdx !== 0 ? (
<div className="back" onClick={() => onShiftHandler(-1)}> <div className="back" onClick={() => onShiftHandler(-1)}>
@@ -28,3 +28,5 @@ export default ({ viewIdx, paths, onShiftHandler }) => {
} }
return <div className="media-gallery-controls" />; return <div className="media-gallery-controls" />;
}; };
export default OverlayControls;

View File

@@ -1,4 +1,3 @@
/* global fetch */
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import marked from "marked"; import marked from "marked";

View File

@@ -108,7 +108,7 @@ class SourceOverlay extends React.Component {
{url ? ( {url ? (
<span> <span>
<i className="material-icons left">link</i> <i className="material-icons left">link</i>
<a href={url} target="_blank"> <a href={url} target="_blank" rel="noreferrer">
Link to original URL Link to original URL
</a> </a>
</span> </span>

View File

@@ -49,7 +49,7 @@ class Search extends React.Component {
return ( return (
<div <div
class={ className={
"search-outer-container" + "search-outer-container" +
(this.props.narrative ? " narrative-mode " : "") (this.props.narrative ? " narrative-mode " : "")
} }
@@ -58,11 +58,13 @@ class Search extends React.Component {
<i className="material-icons">search</i> <i className="material-icons">search</i>
</div> </div>
<div <div
class={"search-bar-overlay" + (this.state.isFolded ? " folded" : "")} className={
"search-bar-overlay" + (this.state.isFolded ? " folded" : "")
}
> >
<div class="search-input-container"> <div className="search-input-container">
<input <input
class="search-bar-input" className="search-bar-input"
onChange={this.updateSearchQuery} onChange={this.updateSearchQuery}
type="text" type="text"
/> />
@@ -74,7 +76,7 @@ class Search extends React.Component {
close close
</i> </i>
</div> </div>
<div class="search-results"> <div className="search-results">
{searchResults.map((result) => { {searchResults.map((result) => {
return ( return (
<SearchRow <SearchRow

View File

@@ -1,14 +1,14 @@
import React, { useState } from "react"; import React, { useState } from "react";
export default ({ showing, onClickHandler, timelineDims }) => { const StateOptions = ({ showing, onClickHandler, timelineDims }) => {
if (!showing) {
return null;
}
const [checked, setChecked] = useState(false); const [checked, setChecked] = useState(false);
const handleCheck = () => setChecked(!checked); const handleCheck = () => setChecked(!checked);
const onNarrativise = () => onClickHandler(checked); const onNarrativise = () => onClickHandler(checked);
if (!showing) {
return null;
}
return ( return (
<div className="stateoptions-panel" style={{ bottom: timelineDims.height }}> <div className="stateoptions-panel" style={{ bottom: timelineDims.height }}>
<div> <div>
@@ -26,3 +26,5 @@ export default ({ showing, onClickHandler, timelineDims }) => {
</div> </div>
); );
}; };
export default StateOptions;

View File

@@ -1,9 +1,9 @@
import React from "react"; import React from "react";
export default ({ showing, children }) => { const StaticPage = ({ showing, children }) => (
return ( <div className={`cover-container ${showing ? "showing" : ""}`}>
<div className={`cover-container ${showing ? "showing" : ""}`}> {children}
{children} </div>
</div> );
);
}; export default StaticPage;

View File

@@ -158,10 +158,14 @@ class TemplateCover extends React.Component {
className="cover-logo-container" className="cover-logo-container"
href="https://forensic-architecture.org" href="https://forensic-architecture.org"
> >
<img className="cover-logo" src={falogo} /> <img
className="cover-logo"
src={falogo}
alt="Forensic Architecture logo"
/>
</a> </a>
<a className="cover-logo-container" href="https://bellingcat.com"> <a className="cover-logo-container" href="https://bellingcat.com">
<img className="cover-logo" src={bcatlogo} /> <img className="cover-logo" src={bcatlogo} alt="Bellingcat logo" />
</a> </a>
</div> </div>
<div className="cover-content"> <div className="cover-content">

View File

@@ -38,7 +38,7 @@ class TimelineCategories extends React.Component {
return ( return (
<> <>
<g <g
class="tick" className="tick"
style={{ strokeWidth }} style={{ strokeWidth }}
opacity="0.5" opacity="0.5"
transform={`translate(0,${this.props.getCategoryY(cat)})`} transform={`translate(0,${this.props.getCategoryY(cat)})`}
@@ -46,7 +46,7 @@ class TimelineCategories extends React.Component {
<line x1={dims.marginLeft} x2={dims.width - dims.width_controls} /> <line x1={dims.marginLeft} x2={dims.width - dims.width_controls} />
</g> </g>
<g <g
class="tick" className="tick"
opacity="1" opacity="1"
transform={`translate(0,${this.props.getCategoryY(cat)})`} transform={`translate(0,${this.props.getCategoryY(cat)})`}
> >
@@ -66,11 +66,11 @@ class TimelineCategories extends React.Component {
: this.renderCategory(fallbackLabel, 0); : this.renderCategory(fallbackLabel, 0);
return ( return (
<g class="yAxis"> <g className="yAxis">
{renderedCategories} {renderedCategories}
<rect <rect
ref={this.grabRef} ref={this.grabRef}
class="drag-grabber" className="drag-grabber"
x={dims.marginLeft} x={dims.marginLeft}
y={dims.marginTop} y={dims.marginTop}
width={dims.width - dims.marginLeft - dims.width_controls} width={dims.width - dims.marginLeft - dims.width_controls}

View File

@@ -6,27 +6,32 @@ import InfoIcon from "../presentational/Icons/Info";
function BottomActions(props) { function BottomActions(props) {
function renderToggles() { function renderToggles() {
return [ return (
<div className="bottom-action-block"> <>
{props.features.USE_SITES ? ( <div className="bottom-action-block">
<SitesIcon {props.features.USE_SITES ? (
isActive={props.sites.enabled} <SitesIcon
onClickHandler={props.sites.toggle} isActive={props.sites.enabled}
onClickHandler={props.sites.toggle}
/>
) : null}
</div>
,
<div className="botttom-action-block">
<InfoIcon
isActive={props.info.enabled}
onClickHandler={props.info.toggle}
/> />
) : null} </div>
</div>, ,
<div className="botttom-action-block"> <div className="botttom-action-block">
<InfoIcon {props.features.USE_COVER ? (
isActive={props.info.enabled} <CoverIcon onClickHandler={props.cover.toggle} />
onClickHandler={props.info.toggle} ) : null}
/> </div>
</div>, ,
<div className="botttom-action-block"> </>
{props.features.USE_COVER ? ( );
<CoverIcon onClickHandler={props.cover.toggle} />
) : null}
</div>,
];
} }
return <div className="bottom-actions">{renderToggles()}</div>; return <div className="bottom-actions">{renderToggles()}</div>;

View File

@@ -3,7 +3,7 @@ import marked from "marked";
import Checkbox from "../presentational/Checkbox"; import Checkbox from "../presentational/Checkbox";
import copy from "../../common/data/copy.json"; import copy from "../../common/data/copy.json";
export default ({ const CategoriesListPanel = ({
categories, categories,
activeCategories, activeCategories,
onCategoryFilter, onCategoryFilter,
@@ -45,3 +45,5 @@ export default ({
</div> </div>
); );
}; };
export default CategoriesListPanel;

View File

@@ -1,6 +1,6 @@
import React from "react"; import React from "react";
export default ({ label, isActive, onClickCheckbox, color }) => { const Checkbox = ({ label, isActive, onClickCheckbox, color }) => {
const styles = { const styles = {
background: isActive ? color : "none", background: isActive ? color : "none",
border: `1px solid ${color}`, border: `1px solid ${color}`,
@@ -15,3 +15,5 @@ export default ({ label, isActive, onClickCheckbox, color }) => {
</div> </div>
); );
}; };
export default Checkbox;

View File

@@ -8,7 +8,7 @@ const CoverIcon = ({ isActive, isDisabled, onClickHandler }) => {
return ( return (
<button className={classes} onClick={onClickHandler}> <button className={classes} onClick={onClickHandler}>
<i class="material-icons">info</i> <i className="material-icons">info</i>
</button> </button>
); );
}; };

View File

@@ -62,23 +62,28 @@ function Cluster({
if (!isLatitude(latitude) || !isLongitude(longitude)) return null; if (!isLatitude(latitude) || !isLongitude(longitude)) return null;
return ( return (
<g <svg>
className="cluster-event" <g
transform={`translate(${x}, ${y})`} className="cluster-event"
onClick={(e) => onClick({ id: clusterId, latitude, longitude })} transform={`translate(${x}, ${y})`}
onMouseEnter={() => setHovered(true)} onClick={(e) => onClick({ id: clusterId, latitude, longitude })}
onMouseLeave={() => setHovered(false)} onMouseEnter={() => setHovered(true)}
> onMouseLeave={() => setHovered(false)}
<ColoredMarkers >
radius={size} <ColoredMarkers
colorPercentMap={zipColorsToPercentages(filterColors, colorPercentages)} radius={size}
styles={{ colorPercentMap={zipColorsToPercentages(
...styles, filterColors,
}} colorPercentages
className="cluster-event-marker" )}
/> styles={{
{hovered ? renderHover(cluster) : null} ...styles,
</g> }}
className="cluster-event-marker"
/>
{hovered ? renderHover(cluster) : null}
</g>
</svg>
); );
} }
@@ -125,30 +130,32 @@ function ClusterEvents({
return ( return (
<Portal node={svg}> <Portal node={svg}>
<g className="cluster-locations"> <svg>
{isRadial ? <DefsClusters /> : null} <g className="cluster-locations">
{clusters.map((c) => { {isRadial ? <DefsClusters /> : null}
const pointCount = c.properties.point_count; {clusters.map((c) => {
const clusterSize = calcClusterSize(pointCount, totalPoints); const pointCount = c.properties.point_count;
return ( const clusterSize = calcClusterSize(pointCount, totalPoints);
<Cluster return (
onClick={onSelect} <Cluster
getClusterChildren={getClusterChildren} onClick={onSelect}
coloringSet={coloringSet} getClusterChildren={getClusterChildren}
cluster={c} coloringSet={coloringSet}
filterColors={filterColors} cluster={c}
size={clusterSize} filterColors={filterColors}
projectPoint={projectPoint} size={clusterSize}
totalPoints={totalPoints} projectPoint={projectPoint}
styles={{ totalPoints={totalPoints}
...styles, styles={{
fillOpacity: calcClusterOpacity(pointCount, totalPoints), ...styles,
}} fillOpacity: calcClusterOpacity(pointCount, totalPoints),
renderHover={() => renderHover(pointCount, clusterSize)} }}
/> renderHover={() => renderHover(pointCount, clusterSize)}
); />
})} );
</g> })}
</g>
</svg>
</Portal> </Portal>
); );
} }

View File

@@ -1,30 +1,35 @@
import React from "react"; import React from "react";
const MapDefsMarkers = () => ( const MapDefsMarkers = () => (
<defs> <svg>
<marker <defs>
id="arrow" <marker
viewBox="0 0 6 6" id="arrow"
refX="3" viewBox="0 0 6 6"
refY="3" refX="3"
markerWidth="6" refY="3"
markerHeight="6" markerWidth="6"
orient="auto" markerHeight="6"
> orient="auto"
<path d="M0,3v-3l6,3l-6,3z" style={{ fill: "red" }} /> >
</marker> <path d="M0,3v-3l6,3l-6,3z" style={{ fill: "red" }} />
<marker </marker>
id="arrow-off" <marker
viewBox="0 0 6 6" id="arrow-off"
refX="3" viewBox="0 0 6 6"
refY="3" refX="3"
markerWidth="6" refY="3"
markerHeight="6" markerWidth="6"
orient="auto" markerHeight="6"
> orient="auto"
<path d="M0,3v-3l6,3l-6,3z" style={{ fill: "black", fillOpacity: 0.2 }} /> >
</marker> <path
</defs> d="M0,3v-3l6,3l-6,3z"
style={{ fill: "black", fillOpacity: 0.2 }}
/>
</marker>
</defs>
</svg>
); );
export default MapDefsMarkers; export default MapDefsMarkers;

View File

@@ -171,26 +171,30 @@ function MapEvents({
}, false); }, false);
return ( return (
<g <svg>
className={`location-event ${narrative ? "no-hover" : ""}`} <g
transform={`translate(${x}, ${y})`} className={`location-event ${narrative ? "no-hover" : ""}`}
onClick={(e) => handleEventSelect(e, location)} transform={`translate(${x}, ${y})`}
> onClick={(e) => handleEventSelect(e, location)}
{features.COLOR_BY_ASSOCIATION >
? renderLocationSlicesByAssociation(location) {features.COLOR_BY_ASSOCIATION
: null} ? renderLocationSlicesByAssociation(location)
{features.COLOR_BY_CATEGORY : null}
? renderLocationSlicesByCategory(location) {features.COLOR_BY_CATEGORY
: null} ? renderLocationSlicesByCategory(location)
{extraRender ? extraRender() : null} : null}
{isSelected ? null : renderBorder()} {extraRender ? extraRender() : null}
</g> {isSelected ? null : renderBorder()}
</g>
</svg>
); );
} }
return ( return (
<Portal node={svg}> <Portal node={svg}>
<g className="event-locations">{locations.map(renderLocation)}</g> <svg>
<g className="event-locations">{locations.map(renderLocation)}</g>
</svg>
</Portal> </Portal>
); );
} }

View File

@@ -1,6 +1,6 @@
import React from "react"; import React from "react";
export default ({ isDisabled, direction, onClickHandler }) => { const Adjust = ({ isDisabled, direction, onClickHandler }) => {
return ( return (
<div <div
className={`narrative-adjust ${direction}`} className={`narrative-adjust ${direction}`}
@@ -12,3 +12,5 @@ export default ({ isDisabled, direction, onClickHandler }) => {
</div> </div>
); );
}; };
export default Adjust;

View File

@@ -1,6 +1,6 @@
import React from "react"; import React from "react";
export default ({ onClickHandler, closeMsg }) => { const Close = ({ onClickHandler, closeMsg }) => {
return ( return (
<div className="narrative-close" onClick={onClickHandler}> <div className="narrative-close" onClick={onClickHandler}>
<button className="side-menu-burg is-active"> <button className="side-menu-burg is-active">
@@ -10,3 +10,5 @@ export default ({ onClickHandler, closeMsg }) => {
</div> </div>
); );
}; };
export default Close;

View File

@@ -3,7 +3,7 @@ import Card from "./Card";
import Adjust from "./Adjust"; import Adjust from "./Adjust";
import Close from "./Close"; import Close from "./Close";
export default ({ narrative, methods }) => { const NarrativeControls = ({ narrative, methods }) => {
if (!narrative) return null; if (!narrative) return null;
const { current, steps } = narrative; const { current, steps } = narrative;
@@ -30,3 +30,5 @@ export default ({ narrative, methods }) => {
</> </>
); );
}; };
export default NarrativeControls;

View File

@@ -3,7 +3,7 @@ import marked from "marked";
const fontSize = window.innerWidth > 1000 ? 14 : 18; const fontSize = window.innerWidth > 1000 ? 14 : 18;
export default ({ const Popup = ({
content = [], content = [],
styles = {}, styles = {},
isOpen = true, isOpen = true,
@@ -29,10 +29,12 @@ export default ({
</button> </button>
<h2>{title}</h2> <h2>{title}</h2>
</div> </div>
{content.map((t) => ( {content.map((t, idx) => (
<div dangerouslySetInnerHTML={{ __html: marked(t) }} /> <div key={idx} dangerouslySetInnerHTML={{ __html: marked(t) }} />
))} ))}
{children} {children}
</div> </div>
</div> </div>
); );
export default Popup;

View File

@@ -1,6 +1,6 @@
import React from "react"; import React from "react";
export default ({ const DatetimeBar = ({
highlights, highlights,
events, events,
x, x,
@@ -41,3 +41,5 @@ export default ({
</> </>
); );
}; };
export default DatetimeBar;

View File

@@ -1,6 +1,14 @@
import React from "react"; import React from "react";
export default ({ x, y, r, transform, onSelect, styleProps, extraRender }) => { const DatetimeSquare = ({
x,
y,
r,
transform,
onSelect,
styleProps,
extraRender,
}) => {
return ( return (
<rect <rect
onClick={onSelect} onClick={onSelect}
@@ -14,3 +22,5 @@ export default ({ x, y, r, transform, onSelect, styleProps, extraRender }) => {
/> />
); );
}; };
export default DatetimeSquare;

View File

@@ -1,6 +1,14 @@
import React from "react"; import React from "react";
export default ({ x, y, r, transform, onSelect, styleProps, extraRender }) => { const DatetimeStar = ({
x,
y,
r,
transform,
onSelect,
styleProps,
extraRender,
}) => {
const s = (r * 2) / 3; const s = (r * 2) / 3;
return ( return (
<polygon <polygon
@@ -15,3 +23,5 @@ export default ({ x, y, r, transform, onSelect, styleProps, extraRender }) => {
/> />
); );
}; };
export default DatetimeStar;

View File

@@ -1,6 +1,6 @@
import React from "react"; import React from "react";
export default ({ const Project = ({
offset, offset,
id, id,
start, start,
@@ -26,3 +26,5 @@ export default ({
/> />
); );
}; };
export default Project;

View File

@@ -28,6 +28,7 @@ const TimelineZoomControls = ({ extent, zoomLevels, dims, onApplyZoom }) => {
x="60" x="60"
y={idx * 15 + 20} y={idx * 15 + 20}
onClick={() => onApplyZoom(zoom)} onClick={() => onApplyZoom(zoom)}
key={idx}
> >
{zoom.label} {zoom.label}
</text> </text>

View File

@@ -12,5 +12,5 @@ it("renders an option to view categories", () => {
<App /> <App />
</Provider> </Provider>
); );
expect(screen.getByText("Categories")).toBeInTheDocument(); expect(screen.getByText("Filters")).toBeInTheDocument();
}); });