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:
|
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
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user