mirror of
https://github.com/bellingcat/ukraine-timemap.git
synced 2026-06-07 19:08:37 +03:00
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:
@@ -5,11 +5,10 @@ cache:
|
||||
directories:
|
||||
- node_modules
|
||||
before_script:
|
||||
- npm install -g yarn
|
||||
- cp example.config.js config.js
|
||||
install:
|
||||
- yarn
|
||||
- npm install
|
||||
script:
|
||||
- yarn lint
|
||||
- yarn build
|
||||
- yarn test
|
||||
- npm run lint
|
||||
- npm run build
|
||||
- npm run test
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"test": "node scripts/test.js --silent",
|
||||
"test:ava": "NODE_ENV=production ava --verbose",
|
||||
"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}\""
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* global fetch, alert */
|
||||
import { urlFromEnv } from "../common/utilities";
|
||||
|
||||
// TODO: relegate these URLs entirely to environment variables
|
||||
|
||||
@@ -5,9 +5,11 @@ export const colors = {
|
||||
white: "#fff",
|
||||
};
|
||||
|
||||
export default {
|
||||
const exports = {
|
||||
fallbackEventColor: colors.fa_red,
|
||||
darkBackground: colors.black,
|
||||
primaryHighlight: colors.fa_red,
|
||||
secondaryHighlight: colors.white,
|
||||
};
|
||||
|
||||
export default exports;
|
||||
|
||||
@@ -380,6 +380,7 @@ export function getFilterIdxFromColorSet(filter, coloringSet) {
|
||||
coloringSet.map((set, idx) => {
|
||||
const foundIdx = set.indexOf(filter);
|
||||
if (foundIdx !== -1) filterIdx = idx;
|
||||
return null;
|
||||
});
|
||||
return filterIdx;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from "react";
|
||||
import Popup from "./presentational/Popup";
|
||||
import copy from "../common/data/copy.json";
|
||||
|
||||
export default ({ isOpen, onClose, language, styles }) => (
|
||||
const Infopopup = ({ isOpen, onClose, language, styles }) => (
|
||||
<Popup
|
||||
title={copy[language].legend.default.header}
|
||||
content={copy[language].legend.default.intro}
|
||||
@@ -11,3 +11,5 @@ export default ({ isOpen, onClose, language, styles }) => (
|
||||
styles={styles}
|
||||
/>
|
||||
);
|
||||
|
||||
export default Infopopup;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* global alert, Event */
|
||||
import React from "react";
|
||||
|
||||
import { bindActionCreators } from "redux";
|
||||
@@ -11,7 +10,6 @@ import LoadingOverlay from "./Overlay/Loading";
|
||||
import Map from "./Map.jsx";
|
||||
import Toolbar from "./Toolbar/Layout";
|
||||
import CardStack from "./CardStack.jsx";
|
||||
// import {CardStack} from '@forensic-architecture/design-system'
|
||||
import NarrativeControls from "./presentational/Narrative/Controls.js";
|
||||
import InfoPopup from "./InfoPopup.jsx";
|
||||
import Popup from "./presentational/Popup";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global L, Event */
|
||||
/* global L */
|
||||
import React from "react";
|
||||
import { Portal } from "react-portal";
|
||||
import Supercluster from "supercluster";
|
||||
|
||||
@@ -16,11 +16,11 @@ export default class Notification extends React.Component {
|
||||
if (!items) return "";
|
||||
return (
|
||||
<div>
|
||||
{items.map((item) => {
|
||||
{items.map((item, idx) => {
|
||||
if (item.error) {
|
||||
return <p>{item.error.message}</p>;
|
||||
return <p key={idx}>{item.error.message}</p>;
|
||||
}
|
||||
return "";
|
||||
return null;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
@@ -47,11 +47,12 @@ export default class Notification extends React.Component {
|
||||
if (notificationsToRender.length > 0) {
|
||||
return (
|
||||
<div className="notification-wrapper">
|
||||
{this.props.notifications.map((notification) => {
|
||||
{this.props.notifications.map((notification, idx) => {
|
||||
return (
|
||||
<div
|
||||
className="notification"
|
||||
onClick={() => this.toggleDetails()}
|
||||
key={idx}
|
||||
>
|
||||
<button
|
||||
onClick={this.props.onToggle}
|
||||
|
||||
@@ -5,7 +5,7 @@ import Md from "./Md";
|
||||
import Spinner from "../presentational/Spinner";
|
||||
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 shiftW = el ? el.getBoundingClientRect().width : 0;
|
||||
|
||||
@@ -71,7 +71,7 @@ export default ({ media, viewIdx, translations, switchLanguage, langIdx }) => {
|
||||
</div>
|
||||
);
|
||||
case "Document":
|
||||
return <iframe className="source-document" src={path} />;
|
||||
return <iframe title={path} className="source-document" src={path} />;
|
||||
default:
|
||||
return (
|
||||
<NoSource
|
||||
@@ -92,3 +92,5 @@ export default ({ media, viewIdx, translations, switchLanguage, langIdx }) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Content;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
export default ({ viewIdx, paths, onShiftHandler }) => {
|
||||
const OverlayControls = ({ viewIdx, paths, onShiftHandler }) => {
|
||||
const backArrow =
|
||||
viewIdx !== 0 ? (
|
||||
<div className="back" onClick={() => onShiftHandler(-1)}>
|
||||
@@ -28,3 +28,5 @@ export default ({ viewIdx, paths, onShiftHandler }) => {
|
||||
}
|
||||
return <div className="media-gallery-controls" />;
|
||||
};
|
||||
|
||||
export default OverlayControls;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* global fetch */
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import marked from "marked";
|
||||
|
||||
@@ -108,7 +108,7 @@ class SourceOverlay extends React.Component {
|
||||
{url ? (
|
||||
<span>
|
||||
<i className="material-icons left">link</i>
|
||||
<a href={url} target="_blank">
|
||||
<a href={url} target="_blank" rel="noreferrer">
|
||||
Link to original URL
|
||||
</a>
|
||||
</span>
|
||||
|
||||
@@ -49,7 +49,7 @@ class Search extends React.Component {
|
||||
|
||||
return (
|
||||
<div
|
||||
class={
|
||||
className={
|
||||
"search-outer-container" +
|
||||
(this.props.narrative ? " narrative-mode " : "")
|
||||
}
|
||||
@@ -58,11 +58,13 @@ class Search extends React.Component {
|
||||
<i className="material-icons">search</i>
|
||||
</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
|
||||
class="search-bar-input"
|
||||
className="search-bar-input"
|
||||
onChange={this.updateSearchQuery}
|
||||
type="text"
|
||||
/>
|
||||
@@ -74,7 +76,7 @@ class Search extends React.Component {
|
||||
close
|
||||
</i>
|
||||
</div>
|
||||
<div class="search-results">
|
||||
<div className="search-results">
|
||||
{searchResults.map((result) => {
|
||||
return (
|
||||
<SearchRow
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
export default ({ showing, onClickHandler, timelineDims }) => {
|
||||
if (!showing) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const StateOptions = ({ showing, onClickHandler, timelineDims }) => {
|
||||
const [checked, setChecked] = useState(false);
|
||||
const handleCheck = () => setChecked(!checked);
|
||||
const onNarrativise = () => onClickHandler(checked);
|
||||
|
||||
if (!showing) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="stateoptions-panel" style={{ bottom: timelineDims.height }}>
|
||||
<div>
|
||||
@@ -26,3 +26,5 @@ export default ({ showing, onClickHandler, timelineDims }) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StateOptions;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from "react";
|
||||
|
||||
export default ({ showing, children }) => {
|
||||
return (
|
||||
const StaticPage = ({ showing, children }) => (
|
||||
<div className={`cover-container ${showing ? "showing" : ""}`}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StaticPage;
|
||||
|
||||
@@ -158,10 +158,14 @@ class TemplateCover extends React.Component {
|
||||
className="cover-logo-container"
|
||||
href="https://forensic-architecture.org"
|
||||
>
|
||||
<img className="cover-logo" src={falogo} />
|
||||
<img
|
||||
className="cover-logo"
|
||||
src={falogo}
|
||||
alt="Forensic Architecture logo"
|
||||
/>
|
||||
</a>
|
||||
<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>
|
||||
</div>
|
||||
<div className="cover-content">
|
||||
|
||||
@@ -38,7 +38,7 @@ class TimelineCategories extends React.Component {
|
||||
return (
|
||||
<>
|
||||
<g
|
||||
class="tick"
|
||||
className="tick"
|
||||
style={{ strokeWidth }}
|
||||
opacity="0.5"
|
||||
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} />
|
||||
</g>
|
||||
<g
|
||||
class="tick"
|
||||
className="tick"
|
||||
opacity="1"
|
||||
transform={`translate(0,${this.props.getCategoryY(cat)})`}
|
||||
>
|
||||
@@ -66,11 +66,11 @@ class TimelineCategories extends React.Component {
|
||||
: this.renderCategory(fallbackLabel, 0);
|
||||
|
||||
return (
|
||||
<g class="yAxis">
|
||||
<g className="yAxis">
|
||||
{renderedCategories}
|
||||
<rect
|
||||
ref={this.grabRef}
|
||||
class="drag-grabber"
|
||||
className="drag-grabber"
|
||||
x={dims.marginLeft}
|
||||
y={dims.marginTop}
|
||||
width={dims.width - dims.marginLeft - dims.width_controls}
|
||||
|
||||
@@ -6,7 +6,8 @@ import InfoIcon from "../presentational/Icons/Info";
|
||||
|
||||
function BottomActions(props) {
|
||||
function renderToggles() {
|
||||
return [
|
||||
return (
|
||||
<>
|
||||
<div className="bottom-action-block">
|
||||
{props.features.USE_SITES ? (
|
||||
<SitesIcon
|
||||
@@ -14,19 +15,23 @@ function BottomActions(props) {
|
||||
onClickHandler={props.sites.toggle}
|
||||
/>
|
||||
) : null}
|
||||
</div>,
|
||||
</div>
|
||||
,
|
||||
<div className="botttom-action-block">
|
||||
<InfoIcon
|
||||
isActive={props.info.enabled}
|
||||
onClickHandler={props.info.toggle}
|
||||
/>
|
||||
</div>,
|
||||
</div>
|
||||
,
|
||||
<div className="botttom-action-block">
|
||||
{props.features.USE_COVER ? (
|
||||
<CoverIcon onClickHandler={props.cover.toggle} />
|
||||
) : null}
|
||||
</div>,
|
||||
];
|
||||
</div>
|
||||
,
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return <div className="bottom-actions">{renderToggles()}</div>;
|
||||
|
||||
@@ -3,7 +3,7 @@ import marked from "marked";
|
||||
import Checkbox from "../presentational/Checkbox";
|
||||
import copy from "../../common/data/copy.json";
|
||||
|
||||
export default ({
|
||||
const CategoriesListPanel = ({
|
||||
categories,
|
||||
activeCategories,
|
||||
onCategoryFilter,
|
||||
@@ -45,3 +45,5 @@ export default ({
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CategoriesListPanel;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
export default ({ label, isActive, onClickCheckbox, color }) => {
|
||||
const Checkbox = ({ label, isActive, onClickCheckbox, color }) => {
|
||||
const styles = {
|
||||
background: isActive ? color : "none",
|
||||
border: `1px solid ${color}`,
|
||||
@@ -15,3 +15,5 @@ export default ({ label, isActive, onClickCheckbox, color }) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Checkbox;
|
||||
|
||||
@@ -8,7 +8,7 @@ const CoverIcon = ({ isActive, isDisabled, onClickHandler }) => {
|
||||
|
||||
return (
|
||||
<button className={classes} onClick={onClickHandler}>
|
||||
<i class="material-icons">info</i>
|
||||
<i className="material-icons">info</i>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -62,6 +62,7 @@ function Cluster({
|
||||
if (!isLatitude(latitude) || !isLongitude(longitude)) return null;
|
||||
|
||||
return (
|
||||
<svg>
|
||||
<g
|
||||
className="cluster-event"
|
||||
transform={`translate(${x}, ${y})`}
|
||||
@@ -71,7 +72,10 @@ function Cluster({
|
||||
>
|
||||
<ColoredMarkers
|
||||
radius={size}
|
||||
colorPercentMap={zipColorsToPercentages(filterColors, colorPercentages)}
|
||||
colorPercentMap={zipColorsToPercentages(
|
||||
filterColors,
|
||||
colorPercentages
|
||||
)}
|
||||
styles={{
|
||||
...styles,
|
||||
}}
|
||||
@@ -79,6 +83,7 @@ function Cluster({
|
||||
/>
|
||||
{hovered ? renderHover(cluster) : null}
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -125,6 +130,7 @@ function ClusterEvents({
|
||||
|
||||
return (
|
||||
<Portal node={svg}>
|
||||
<svg>
|
||||
<g className="cluster-locations">
|
||||
{isRadial ? <DefsClusters /> : null}
|
||||
{clusters.map((c) => {
|
||||
@@ -149,6 +155,7 @@ function ClusterEvents({
|
||||
);
|
||||
})}
|
||||
</g>
|
||||
</svg>
|
||||
</Portal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
|
||||
const MapDefsMarkers = () => (
|
||||
<svg>
|
||||
<defs>
|
||||
<marker
|
||||
id="arrow"
|
||||
@@ -22,9 +23,13 @@ const MapDefsMarkers = () => (
|
||||
markerHeight="6"
|
||||
orient="auto"
|
||||
>
|
||||
<path d="M0,3v-3l6,3l-6,3z" style={{ fill: "black", fillOpacity: 0.2 }} />
|
||||
<path
|
||||
d="M0,3v-3l6,3l-6,3z"
|
||||
style={{ fill: "black", fillOpacity: 0.2 }}
|
||||
/>
|
||||
</marker>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default MapDefsMarkers;
|
||||
|
||||
@@ -171,6 +171,7 @@ function MapEvents({
|
||||
}, false);
|
||||
|
||||
return (
|
||||
<svg>
|
||||
<g
|
||||
className={`location-event ${narrative ? "no-hover" : ""}`}
|
||||
transform={`translate(${x}, ${y})`}
|
||||
@@ -185,12 +186,15 @@ function MapEvents({
|
||||
{extraRender ? extraRender() : null}
|
||||
{isSelected ? null : renderBorder()}
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Portal node={svg}>
|
||||
<svg>
|
||||
<g className="event-locations">{locations.map(renderLocation)}</g>
|
||||
</svg>
|
||||
</Portal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
export default ({ isDisabled, direction, onClickHandler }) => {
|
||||
const Adjust = ({ isDisabled, direction, onClickHandler }) => {
|
||||
return (
|
||||
<div
|
||||
className={`narrative-adjust ${direction}`}
|
||||
@@ -12,3 +12,5 @@ export default ({ isDisabled, direction, onClickHandler }) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Adjust;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
export default ({ onClickHandler, closeMsg }) => {
|
||||
const Close = ({ onClickHandler, closeMsg }) => {
|
||||
return (
|
||||
<div className="narrative-close" onClick={onClickHandler}>
|
||||
<button className="side-menu-burg is-active">
|
||||
@@ -10,3 +10,5 @@ export default ({ onClickHandler, closeMsg }) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Close;
|
||||
|
||||
@@ -3,7 +3,7 @@ import Card from "./Card";
|
||||
import Adjust from "./Adjust";
|
||||
import Close from "./Close";
|
||||
|
||||
export default ({ narrative, methods }) => {
|
||||
const NarrativeControls = ({ narrative, methods }) => {
|
||||
if (!narrative) return null;
|
||||
|
||||
const { current, steps } = narrative;
|
||||
@@ -30,3 +30,5 @@ export default ({ narrative, methods }) => {
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NarrativeControls;
|
||||
|
||||
@@ -3,7 +3,7 @@ import marked from "marked";
|
||||
|
||||
const fontSize = window.innerWidth > 1000 ? 14 : 18;
|
||||
|
||||
export default ({
|
||||
const Popup = ({
|
||||
content = [],
|
||||
styles = {},
|
||||
isOpen = true,
|
||||
@@ -29,10 +29,12 @@ export default ({
|
||||
</button>
|
||||
<h2>{title}</h2>
|
||||
</div>
|
||||
{content.map((t) => (
|
||||
<div dangerouslySetInnerHTML={{ __html: marked(t) }} />
|
||||
{content.map((t, idx) => (
|
||||
<div key={idx} dangerouslySetInnerHTML={{ __html: marked(t) }} />
|
||||
))}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Popup;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
export default ({
|
||||
const DatetimeBar = ({
|
||||
highlights,
|
||||
events,
|
||||
x,
|
||||
@@ -41,3 +41,5 @@ export default ({
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatetimeBar;
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import React from "react";
|
||||
|
||||
export default ({ x, y, r, transform, onSelect, styleProps, extraRender }) => {
|
||||
const DatetimeSquare = ({
|
||||
x,
|
||||
y,
|
||||
r,
|
||||
transform,
|
||||
onSelect,
|
||||
styleProps,
|
||||
extraRender,
|
||||
}) => {
|
||||
return (
|
||||
<rect
|
||||
onClick={onSelect}
|
||||
@@ -14,3 +22,5 @@ export default ({ x, y, r, transform, onSelect, styleProps, extraRender }) => {
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatetimeSquare;
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
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;
|
||||
return (
|
||||
<polygon
|
||||
@@ -15,3 +23,5 @@ export default ({ x, y, r, transform, onSelect, styleProps, extraRender }) => {
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatetimeStar;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
export default ({
|
||||
const Project = ({
|
||||
offset,
|
||||
id,
|
||||
start,
|
||||
@@ -26,3 +26,5 @@ export default ({
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Project;
|
||||
|
||||
@@ -28,6 +28,7 @@ const TimelineZoomControls = ({ extent, zoomLevels, dims, onApplyZoom }) => {
|
||||
x="60"
|
||||
y={idx * 15 + 20}
|
||||
onClick={() => onApplyZoom(zoom)}
|
||||
key={idx}
|
||||
>
|
||||
{zoom.label}
|
||||
</text>
|
||||
|
||||
@@ -12,5 +12,5 @@ it("renders an option to view categories", () => {
|
||||
<App />
|
||||
</Provider>
|
||||
);
|
||||
expect(screen.getByText("Categories")).toBeInTheDocument();
|
||||
expect(screen.getByText("Filters")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user