From 7156639ec388113aba7c8cb53619175641bc90f0 Mon Sep 17 00:00:00 2001 From: efarooqui Date: Thu, 20 May 2021 18:44:26 -0700 Subject: [PATCH 1/5] Polygon shapes abstracted to be types of checkboxes; abstracted rendering of panel into one component; importing shapes from separate endpoint --- src/actions/index.js | 20 +++- src/common/constants.js | 22 +++- src/common/data/copy.json | 6 +- src/common/utilities.js | 18 ++- src/components/Layout.js | 1 + src/components/Toolbar.js | 107 ++++++++++-------- src/components/atoms/Checkbox.js | 11 +- .../controls/CategoriesListPanel.js | 32 ++---- src/components/controls/ShapesListPanel.js | 34 ++++++ src/components/controls/atoms/PanelTree.js | 32 ++++++ src/components/space/carto/Map.js | 16 +-- .../carto/atoms/{Shapes.js => Regions.js} | 24 ++-- src/components/time/atoms/DatetimePentagon.js | 2 +- src/components/time/atoms/DatetimeStar.js | 2 +- src/components/time/atoms/DatetimeTriangle.js | 2 +- src/components/time/atoms/Events.js | 13 ++- src/components/time/atoms/Markers.js | 7 +- src/reducers/app.js | 18 +++ src/reducers/validate/regionSchema.js | 8 ++ src/reducers/validate/shapeSchema.js | 6 +- src/reducers/validate/validators.js | 29 ++++- src/selectors/index.js | 14 ++- src/store/initial.js | 17 ++- 23 files changed, 308 insertions(+), 133 deletions(-) create mode 100644 src/components/controls/ShapesListPanel.js create mode 100644 src/components/controls/atoms/PanelTree.js rename src/components/space/carto/atoms/{Shapes.js => Regions.js} (50%) create mode 100644 src/reducers/validate/regionSchema.js diff --git a/src/actions/index.js b/src/actions/index.js index 7c05dbe..b0c7722 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -6,6 +6,7 @@ const EVENT_DATA_URL = urlFromEnv("EVENTS_EXT"); const ASSOCIATIONS_URL = urlFromEnv("ASSOCIATIONS_EXT"); const SOURCES_URL = urlFromEnv("SOURCES_EXT"); const SITES_URL = urlFromEnv("SITES_EXT"); +const REGIONS_URL = urlFromEnv("REGIONS_EXT"); const SHAPES_URL = urlFromEnv("SHAPES_EXT"); const domainMsg = (domainType) => @@ -79,6 +80,13 @@ export function fetchDomain() { .catch(() => handleError(domainMsg("sites"))); } + let regionsPromise = Promise.resolve([]); + if (features.USE_REGIONS) { + regionsPromise = fetch(REGIONS_URL) + .then((response) => response.json()) + .catch(() => handleError(domainMsg("regions"))); + } + let shapesPromise = Promise.resolve([]); if (features.USE_SHAPES) { shapesPromise = fetch(SHAPES_URL) @@ -91,6 +99,7 @@ export function fetchDomain() { associationsPromise, sourcesPromise, sitesPromise, + regionsPromise, shapesPromise, ]) .then((response) => { @@ -99,7 +108,8 @@ export function fetchDomain() { associations: response[1], sources: response[2], sites: response[3], - shapes: response[4], + regions: response[4], + shapes: response[5], notifications, }; if ( @@ -205,6 +215,14 @@ export function toggleAssociations(association, value, shouldColor) { }; } +export const TOGGLE_SHAPES = "TOGGLE_SHAPES"; +export function toggleShapes(shape) { + return { + type: TOGGLE_SHAPES, + shape, + }; +} + export const SET_LOADING = "SET_LOADING"; export function setLoading() { return { diff --git a/src/common/constants.js b/src/common/constants.js index e295e4b..371d923 100644 --- a/src/common/constants.js +++ b/src/common/constants.js @@ -4,10 +4,30 @@ export const ASSOCIATION_MODES = { FILTER: "FILTER", }; -export const TIMELINE_ONLY = "TIMELINE_ONLY"; +export const SHAPE = "SHAPE"; export const DEFAULT_TAB_ICONS = { CATEGORY: "widgets", NARRATIVE: "timeline", FILTER: "filter_list", + SHAPE: "change_history", +}; + +export const AVAILABLE_SHAPES = { + STAR: "STAR", + DIAMOND: "DIAMOND", + PENTAGON: "PENTAGON", + SQUARE: "SQUARE", + DOT: "DOT", + BAR: "BAR", + TRIANGLE: "TRIANGLE", +}; + +export const POLYGON_CLIP_PATH = { + STAR: + "polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%)", + DIAMOND: "polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)", + PENTAGON: + "polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%)", + TRIANGLE: "polygon(50% 0%, 0% 100%, 100% 100%)", }; diff --git a/src/common/data/copy.json b/src/common/data/copy.json index 9e4fe21..4567108 100644 --- a/src/common/data/copy.json +++ b/src/common/data/copy.json @@ -145,7 +145,11 @@ "categories": "Categories", "categories_label": "Categories", "explore_by_category__title": "Explore events by category", - "explore_by_category__description": "‘Categories’ refer to the victims of a given incident.

If no categories are selected, all datapoints are displayed." + "explore_by_category__description": "‘Categories’ refer to the victims of a given incident.

If no categories are selected, all datapoints are displayed.", + "shapes": "Shapes", + "shapes_label": "Shapes", + "explore_by_shapes__title": "Explore events by shape breakdown", + "explore_by_shape__description": "Shapes map to a given type of event that appears on the timeline.

Select the shape marker to toggle this type of event on / off" }, "timeline": { "labels_title": "Testimonies", diff --git a/src/common/utilities.js b/src/common/utilities.js index 4ee3bdc..1e97607 100644 --- a/src/common/utilities.js +++ b/src/common/utilities.js @@ -1,7 +1,7 @@ import moment from "moment"; import hash from "object-hash"; -import { ASSOCIATION_MODES } from "./constants"; +import { ASSOCIATION_MODES, POLYGON_CLIP_PATH } from "./constants"; let { DATE_FMT, TIME_FMT } = process.env; if (!DATE_FMT) DATE_FMT = "MM/DD/YYYY"; @@ -148,7 +148,7 @@ export function getFilterAncestors(filter) { const accumulatedPath = splitFilter.slice(0, index + 1).join("/"); ancestors.push(accumulatedPath); }); - // // The last element here will be the leaf node aka the filter passed in + // The last element here will be the leaf node aka the filter passed in ancestors.pop(); return ancestors; } @@ -510,3 +510,17 @@ export function setD3Locale(d3) { d3.timeFormatDefaultLocale(languages[language]); } } + +export function mapStyleByShape(shapes, activeShapes) { + const styledShapes = shapes.map((s) => { + const { colour, shape, title } = s; + const style = { + background: activeShapes.includes(title) ? colour : "none", + border: `1px solid ${colour}`, + clipPath: POLYGON_CLIP_PATH[shape], + }; + s.styles = style; + return s; + }); + return styledShapes; +} diff --git a/src/components/Layout.js b/src/components/Layout.js index 53ee5a9..cae6891 100644 --- a/src/components/Layout.js +++ b/src/components/Layout.js @@ -327,6 +327,7 @@ class Dashboard extends React.Component { actions.toggleAssociations("filters", filters), onCategoryFilter: (categories) => actions.toggleAssociations("categories", categories), + onShapeFilter: actions.toggleShapes, onSelectNarrative: this.setNarrative, }} /> diff --git a/src/components/Toolbar.js b/src/components/Toolbar.js index b7a80d9..f85e6b9 100644 --- a/src/components/Toolbar.js +++ b/src/components/Toolbar.js @@ -7,6 +7,7 @@ import * as selectors from "../selectors"; import { Tabs, TabPanel } from "react-tabs"; import FilterListPanel from "./controls/FilterListPanel"; import CategoriesListPanel from "./controls/CategoriesListPanel"; +import ShapesListPanel from "./controls/ShapesListPanel"; import BottomActions from "./controls/BottomActions"; import copy from "../common/data/copy.json"; import { @@ -17,7 +18,6 @@ import { addToColoringSet, removeFromColoringSet, } from "../common/utilities.js"; -import { DEFAULT_TAB_ICONS } from "../common/constants"; class Toolbar extends React.Component { constructor(props) { @@ -85,17 +85,10 @@ class Toolbar extends React.Component { renderToolbarNarrativePanel() { const { panels } = this.props.toolbarCopy; - const panelTitle = panels.narratives.label - ? panels.narratives.label - : copy[this.props.language].toolbar.narratives; - const panelDescription = panels.narratives.description - ? panels.narratives.description - : copy[this.props.language].toolbar.explore_by_narrative__description; - return ( -

{panelTitle}

-

{panelDescription}

+

{panels.narratives.label}

+

{panels.narratives.description}

{this.props.narratives.map((narr) => { return (
@@ -118,13 +111,6 @@ class Toolbar extends React.Component { renderToolbarCategoriesPanel() { const { panels } = this.props.toolbarCopy; - const panelTitle = panels.categories.label - ? panels.categories.label - : copy[this.props.language].toolbar.categories; - const panelDescription = panels.categories.description - ? panels.categories.description - : copy[this.props.language].toolbar.explore_by_category__description; - if (this.props.features.USE_CATEGORIES) { return ( @@ -133,8 +119,9 @@ class Toolbar extends React.Component { activeCategories={this.props.activeCategories} onCategoryFilter={this.props.methods.onCategoryFilter} language={this.props.language} - title={panelTitle} - description={panelDescription} + title={panels.categories.label} + description={panels.categories.description} + checkboxColor={this.props.categoriesCheckboxColor} /> ); @@ -143,13 +130,6 @@ class Toolbar extends React.Component { renderToolbarFilterPanel() { const { panels } = this.props.toolbarCopy; - const panelTitle = panels.filters.label - ? panels.filters.label - : copy[this.props.language].toolbar.filters; - const panelDescription = panels.filters.description - ? panels.filters.description - : copy[this.props.language].toolbar.explore_by_filter__description; - return ( ); } + renderToolbarShapePanel() { + const { panels } = this.props.toolbarCopy; + + if (this.props.features.USE_SHAPES) { + return ( + + + + ); + } + } + renderToolbarTab(_selected, label, iconKey) { const isActive = this.state._selected === _selected; const classes = isActive ? "toolbar-tab active" : "toolbar-tab"; @@ -196,6 +196,7 @@ class Toolbar extends React.Component { : null} {features.USE_CATEGORIES ? this.renderToolbarCategoriesPanel() : null} {features.USE_ASSOCIATIONS ? this.renderToolbarFilterPanel() : null} + {features.USE_SHAPES ? this.renderToolbarShapePanel() : null}
); @@ -228,31 +229,17 @@ class Toolbar extends React.Component { const narrativesExist = narratives && narratives.length !== 0; let title = copy[this.props.language].toolbar.title; if (process.env.display_title) title = process.env.display_title; - const { panels } = toolbarCopy; - const narrativesLabel = copy[this.props.language].toolbar.narratives_label; - const filtersLabel = panels.filters.label - ? panels.filters.label - : copy[this.props.language].toolbar.filters_label; - const categoriesLabel = panels.categories.label - ? panels.categories.label - : copy[this.props.language].toolbar.categories_label; - - const filterIcon = panels.filters.icon - ? panels.filters.icon - : DEFAULT_TAB_ICONS.FILTER; - const categoriesIcon = panels.categories.icon - ? panels.categories.icon - : DEFAULT_TAB_ICONS.CATEGORY; const narrativesIdx = 0; const categoriesIdx = narrativesExist ? 1 : 0; const filtersIdx = - narrativesExist && features.CATEGORIES_AS_FILTERS + narrativesExist && features.USE_CATEGORIES ? 2 - : narrativesExist || features.CATEGORIES_AS_FILTERS + : narrativesExist || features.USE_CATEGORIES ? 1 : 0; + const shapesIdx = filtersIdx + 1; return (
@@ -260,17 +247,32 @@ class Toolbar extends React.Component {
{narrativesExist - ? this.renderToolbarTab(narrativesIdx, narrativesLabel, "timeline") + ? this.renderToolbarTab( + narrativesIdx, + panels.narratives.label, + panels.narratives.icon + ) : null} - {features.CATEGORIES_AS_FILTERS + {features.USE_CATEGORIES ? this.renderToolbarTab( categoriesIdx, - categoriesLabel, - categoriesIcon + panels.categories.label, + panels.categories.icon ) : null} {features.USE_ASSOCIATIONS - ? this.renderToolbarTab(filtersIdx, filtersLabel, filterIcon) + ? this.renderToolbarTab( + filtersIdx, + panels.filters.label, + panels.filters.icon + ) + : null} + {features.USE_SHAPES + ? this.renderToolbarTab( + shapesIdx, + panels.shapes.label, + panels.shapes.icon + ) : null}
{ - const styles = { - background: isActive ? color : "none", - border: `1px solid ${color}`, - }; - +const Checkbox = ({ label, isActive, onClickCheckbox, color, styleProps }) => { return (
{label}
); diff --git a/src/components/controls/CategoriesListPanel.js b/src/components/controls/CategoriesListPanel.js index c86f6d7..a150884 100644 --- a/src/components/controls/CategoriesListPanel.js +++ b/src/components/controls/CategoriesListPanel.js @@ -1,6 +1,6 @@ import React from "react"; import marked from "marked"; -import Checkbox from "../atoms/Checkbox"; +import PanelTree from "./atoms/PanelTree"; const CategoriesListPanel = ({ categories, @@ -9,29 +9,8 @@ const CategoriesListPanel = ({ language, title, description, + checkboxColor, }) => { - function renderCategoryTree() { - return ( -
- {categories.map((cat) => { - return ( -
  • - onCategoryFilter(cat.title)} - /> -
  • - ); - })} -
    - ); - } - return (

    {title}

    @@ -40,7 +19,12 @@ const CategoriesListPanel = ({ __html: marked(description), }} /> - {renderCategoryTree()} +
    ); }; diff --git a/src/components/controls/ShapesListPanel.js b/src/components/controls/ShapesListPanel.js new file mode 100644 index 0000000..7fa2119 --- /dev/null +++ b/src/components/controls/ShapesListPanel.js @@ -0,0 +1,34 @@ +import React from "react"; +import marked from "marked"; +import PanelTree from "./atoms/PanelTree"; +import { mapStyleByShape } from "../../common/utilities"; + +const ShapesListPanel = ({ + shapes, + activeShapes, + onShapeFilter, + language, + title, + description, + checkboxColor, +}) => { + const styledShapes = mapStyleByShape(shapes, activeShapes); + return ( +
    +

    {title}

    +

    + +

    + ); +}; + +export default ShapesListPanel; diff --git a/src/components/controls/atoms/PanelTree.js b/src/components/controls/atoms/PanelTree.js new file mode 100644 index 0000000..2896056 --- /dev/null +++ b/src/components/controls/atoms/PanelTree.js @@ -0,0 +1,32 @@ +import React from "react"; +import Checkbox from "../../atoms/Checkbox"; + +const PanelTree = ({ data, activeValues, onSelect, defaultCheckboxColor }) => { + return ( +
    + {data.map((val) => { + const isActive = activeValues.includes(val.title); + const baseStyles = { + background: isActive ? defaultCheckboxColor : "none", + border: `1px solid ${defaultCheckboxColor}`, + }; + return ( +
  • + onSelect(val.title)} + styleProps={val.styles ? val.styles : baseStyles} + /> +
  • + ); + })} +
    + ); +}; + +export default PanelTree; diff --git a/src/components/space/carto/Map.js b/src/components/space/carto/Map.js index a1fa588..13cd0cb 100644 --- a/src/components/space/carto/Map.js +++ b/src/components/space/carto/Map.js @@ -8,7 +8,7 @@ import { connect } from "react-redux"; import * as selectors from "../../../selectors"; import Sites from "./atoms/Sites"; -import Shapes from "./atoms/Shapes"; +import Regions from "./atoms/Regions"; import Events from "./atoms/Events"; import Clusters from "./atoms/Clusters"; import SelectedEvents from "./atoms/SelectedEvents"; @@ -324,13 +324,13 @@ class Map extends React.Component { ); } - renderShapes() { + renderRegions() { return ( - ); } @@ -481,7 +481,7 @@ class Map extends React.Component { {this.renderTiles()} {this.renderMarkers()} {isShowingSites ? this.renderSites() : null} - {this.renderShapes()} + {this.renderRegions()} {this.renderNarratives()} {this.renderEvents()} {this.renderClusters()} @@ -510,7 +510,7 @@ function mapStateToProps(state) { narratives: selectors.selectNarratives(state), categories: selectors.getCategories(state), sites: selectors.selectSites(state), - shapes: selectors.selectShapes(state), + regions: selectors.selectRegions(state), }, app: { views: state.app.associations.views, @@ -532,7 +532,7 @@ function mapStateToProps(state) { dom: state.ui.dom, narratives: state.ui.style.narratives, mapSelectedEvents: state.ui.style.selectedEvents, - shapes: state.ui.style.shapes, + regions: state.ui.style.regions, eventRadius: state.ui.eventRadius, radial: state.ui.style.clusters.radial, filterColors: state.ui.coloring.colors, diff --git a/src/components/space/carto/atoms/Shapes.js b/src/components/space/carto/atoms/Regions.js similarity index 50% rename from src/components/space/carto/atoms/Shapes.js rename to src/components/space/carto/atoms/Regions.js index ef0297f..2ec9aa9 100644 --- a/src/components/space/carto/atoms/Shapes.js +++ b/src/components/space/carto/atoms/Regions.js @@ -1,13 +1,13 @@ import React from "react"; import { Portal } from "react-portal"; -function MapShapes({ svg, shapes, projectPoint, styles }) { - function renderShape(shape) { +function MapRegions({ svg, regions, projectPoint, styles }) { + function renderRegion(region) { const lineCoords = []; - const points = shape.points.map(projectPoint); + const points = region.points.map(projectPoint); points.forEach((p1, idx) => { - if (idx < shape.points.length - 1) { + if (idx < region.points.length - 1) { const p2 = points[idx + 1]; lineCoords.push({ x1: p1.x, @@ -19,29 +19,29 @@ function MapShapes({ svg, shapes, projectPoint, styles }) { }); return lineCoords.map((coords) => { - const shapeStyles = - shape.name in styles ? styles[shape.name] : styles.default; + const regionstyles = + region.name in styles ? styles[region.name] : styles.default; return ( ); }); } - if (!shapes || !shapes.length) return null; + if (!regions || !regions.length) return null; return ( - - {shapes.map(renderShape)} + + {regions.map(renderRegion)} ); } -export default MapShapes; +export default MapRegions; diff --git a/src/components/time/atoms/DatetimePentagon.js b/src/components/time/atoms/DatetimePentagon.js index 233f54f..fff72bc 100644 --- a/src/components/time/atoms/DatetimePentagon.js +++ b/src/components/time/atoms/DatetimePentagon.js @@ -7,7 +7,7 @@ const DatetimePentagon = ({ x, y, r, transform, onSelect, styleProps }) => { onClick={onSelect} className="event" x={x} - y={y - r} + y={y} style={styleProps} points={`${x},${y + s} ${x + s},${y} ${x + s},${y - s} ${x - s},${ y - s diff --git a/src/components/time/atoms/DatetimeStar.js b/src/components/time/atoms/DatetimeStar.js index 60c590f..7056901 100644 --- a/src/components/time/atoms/DatetimeStar.js +++ b/src/components/time/atoms/DatetimeStar.js @@ -15,7 +15,7 @@ const DatetimeStar = ({ onClick={onSelect} className="event" x={x} - y={y - r} + y={y} style={styleProps} points={`${x + s},${y - s} ${x - r},${y} ${x + r},${y} ${x - s},${ y - s diff --git a/src/components/time/atoms/DatetimeTriangle.js b/src/components/time/atoms/DatetimeTriangle.js index b94c42c..d9b6075 100644 --- a/src/components/time/atoms/DatetimeTriangle.js +++ b/src/components/time/atoms/DatetimeTriangle.js @@ -7,7 +7,7 @@ const DatetimeTriangle = ({ x, y, r, transform, onSelect, styleProps }) => { onClick={onSelect} className="event" x={x} - y={y - r} + y={y} style={styleProps} points={`${x},${y + s} ${x + s},${y - s} ${x - s},${y - s}`} transform={`rotate(180, ${x}, ${y})`} diff --git a/src/components/time/atoms/Events.js b/src/components/time/atoms/Events.js index ca11291..fb543e2 100644 --- a/src/components/time/atoms/Events.js +++ b/src/components/time/atoms/Events.js @@ -14,6 +14,7 @@ import { isLatitude, isLongitude, } from "../../../common/utilities"; +import { AVAILABLE_SHAPES } from "../../../common/constants"; function renderDot(event, styles, props) { const colorPercentages = calculateColorPercentages( @@ -158,17 +159,17 @@ const TimelineEvents = ({ let renderShape = isDot ? renderDot : renderBar; if (event.shape) { - if (event.shape === "bar") { + if (event.shape === AVAILABLE_SHAPES.BAR) { renderShape = renderBar; - } else if (event.shape === "diamond") { + } else if (event.shape === AVAILABLE_SHAPES.DIAMOND) { renderShape = renderDiamond; - } else if (event.shape === "star") { + } else if (event.shape === AVAILABLE_SHAPES.STAR) { renderShape = renderStar; - } else if (event.shape === "triangle") { + } else if (event.shape === AVAILABLE_SHAPES.TRIANGLE) { renderShape = renderTriangle; - } else if (event.shape === "pentagon") { + } else if (event.shape === AVAILABLE_SHAPES.PENTAGON) { renderShape = renderPentagon; - } else if (event.shape === "square") { + } else if (event.shape === AVAILABLE_SHAPES.SQUARE) { renderShape = renderSquare; } else { renderShape = renderDot; diff --git a/src/components/time/atoms/Markers.js b/src/components/time/atoms/Markers.js index 00f3012..e0c06ff 100644 --- a/src/components/time/atoms/Markers.js +++ b/src/components/time/atoms/Markers.js @@ -5,6 +5,7 @@ import { isLatitude, isLongitude, } from "../../../common/utilities"; +import { AVAILABLE_SHAPES } from "../../../common/constants"; const TimelineMarkers = ({ styles, @@ -72,11 +73,11 @@ const TimelineMarkers = ({ function renderMarkerForEvent(y) { switch (event.shape) { case "circle": - case "diamond": - case "star": + case AVAILABLE_SHAPES.DIAMOND: + case AVAILABLE_SHAPES.STAR: acc.push(renderCircle(y)); break; - case "bar": + case AVAILABLE_SHAPES.BAR: acc.push(renderBar(y)); break; default: diff --git a/src/reducers/app.js b/src/reducers/app.js index d137850..5337b96 100644 --- a/src/reducers/app.js +++ b/src/reducers/app.js @@ -8,6 +8,7 @@ import { UPDATE_COLORING_SET, CLEAR_FILTER, TOGGLE_ASSOCIATIONS, + TOGGLE_SHAPES, UPDATE_TIMERANGE, UPDATE_DIMENSIONS, UPDATE_NARRATIVE, @@ -153,6 +154,21 @@ function toggleAssociations(appState, action) { }; } +function toggleShapes(appState, action) { + let newShapes = [...appState.shapes]; + if (newShapes.includes(action.shape)) { + const idx = newShapes.indexOf(action.shape); + newShapes.splice(idx, 1); + } else { + newShapes.push(action.shape); + } + + return { + ...appState, + shapes: newShapes, + }; +} + function clearFilter(appState, action) { return { ...appState, @@ -275,6 +291,8 @@ function app(appState = initial.app, action) { return clearFilter(appState, action); case TOGGLE_ASSOCIATIONS: return toggleAssociations(appState, action); + case TOGGLE_SHAPES: + return toggleShapes(appState, action); case UPDATE_TIMERANGE: return updateTimeRange(appState, action); case UPDATE_DIMENSIONS: diff --git a/src/reducers/validate/regionSchema.js b/src/reducers/validate/regionSchema.js new file mode 100644 index 0000000..dad2d88 --- /dev/null +++ b/src/reducers/validate/regionSchema.js @@ -0,0 +1,8 @@ +import Joi from "joi"; + +const regionSchema = Joi.object().keys({ + name: Joi.string().required(), + items: Joi.array().required(), +}); + +export default regionSchema; diff --git a/src/reducers/validate/shapeSchema.js b/src/reducers/validate/shapeSchema.js index 637f773..c222b4f 100644 --- a/src/reducers/validate/shapeSchema.js +++ b/src/reducers/validate/shapeSchema.js @@ -1,8 +1,10 @@ import Joi from "joi"; const shapeSchema = Joi.object().keys({ - name: Joi.string().required(), - items: Joi.array().required(), + id: Joi.string().allow(""), + title: Joi.string().allow(""), + shape: Joi.string().allow(""), + colour: Joi.string().allow(""), }); export default shapeSchema; diff --git a/src/reducers/validate/validators.js b/src/reducers/validate/validators.js index c520e84..995fd2f 100644 --- a/src/reducers/validate/validators.js +++ b/src/reducers/validate/validators.js @@ -4,6 +4,7 @@ import createEventSchema from "./eventSchema"; import siteSchema from "./siteSchema"; import associationsSchema from "./associationsSchema"; import sourceSchema from "./sourceSchema"; +import regionSchema from "./regionSchema"; import shapeSchema from "./shapeSchema"; import { calcDatetime, capitalize } from "../../common/utilities"; @@ -53,6 +54,7 @@ export function validateDomain(domain, features) { sites: [], associations: [], sources: {}, + regions: [], shapes: [], notifications: domain ? domain.notifications : null, }; @@ -66,6 +68,7 @@ export function validateDomain(domain, features) { sites: [], associations: [], sources: [], + regions: [], shapes: [], }; @@ -114,14 +117,31 @@ export function validateDomain(domain, features) { validateArray(domain.sites, "sites", siteSchema); validateArray(domain.associations, "associations", associationsSchema); validateObject(domain.sources, "sources", sourceSchema); - validateObject(domain.shapes, "shapes", shapeSchema); + validateArray(domain.regions, "regions", regionSchema); + validateArray(domain.shapes, "shapes", shapeSchema); // NB: [lat, lon] array is best format for projecting into map - sanitizedDomain.shapes = sanitizedDomain.shapes.map((shape) => ({ - name: shape.name, - points: shape.items.map((coords) => coords.replace(/\s/g, "").split(",")), + sanitizedDomain.regions = sanitizedDomain.regions.map((region) => ({ + name: region.name, + points: region.items.map((coords) => coords.replace(/\s/g, "").split(",")), })); + sanitizedDomain.shapes = sanitizedDomain.shapes.reduce((acc, val) => { + if (!val.shape) { + discardedDomain.shapes.push({ + ...val, + error: makeError( + "events", + val.id, + "Invalid event shape. Please specify a shape for this type of event." + ), + }); + } else { + acc.push(val); + } + return acc; + }, []); + const duplicateAssociations = findDuplicateAssociations(domain.associations); // Duplicated associations if (duplicateAssociations.length > 0) { @@ -177,6 +197,5 @@ export function validateDomain(domain, features) { }); } }); - return sanitizedDomain; } diff --git a/src/selectors/index.js b/src/selectors/index.js index 6ad19d4..1e6f63c 100644 --- a/src/selectors/index.js +++ b/src/selectors/index.js @@ -8,7 +8,7 @@ import { createFilterPathString, } from "../common/utilities"; import { isTimeRangedIn } from "./helpers"; -import { ASSOCIATION_MODES, TIMELINE_ONLY } from "../common/constants"; +import { ASSOCIATION_MODES, SHAPE } from "../common/constants"; // Input selectors export const getEvents = (state) => state.domain.events; @@ -24,6 +24,7 @@ export const getActiveNarrative = (state) => state.app.associations.narrative; export const getSelected = (state) => state.app.selected; export const getSites = (state) => state.domain.sites; export const getSources = (state) => state.domain.sources; +export const getRegions = (state) => state.domain.regions; export const getShapes = (state) => state.domain.shapes; export const getFilters = (state) => state.domain.associations.filter( @@ -32,6 +33,7 @@ export const getFilters = (state) => export const getNotifications = (state) => state.domain.notifications; export const getActiveFilters = (state) => state.app.associations.filters; export const getActiveCategories = (state) => state.app.associations.categories; +export const getActiveShapes = (state) => state.app.shapes; export const getTimeRange = (state) => state.app.timeline.range; export const getTimelineDimensions = (state) => state.app.timeline.dimensions; export const selectNarrative = (state) => state.app.associations.narrative; @@ -56,10 +58,10 @@ export const selectSources = createSelector( } ); -export const selectShapes = createSelector( - [getShapes, getFeatures], - (shapes, features) => { - if (features.USE_SHAPES) return shapes; +export const selectRegions = createSelector( + [getRegions, getFeatures], + (regions, features) => { + if (features.USE_REGIONS) return regions; return []; } ); @@ -96,7 +98,7 @@ export const selectEvents = createSelector( ? (!event.latitude && !event.longitude) || isActiveTime : isActiveTime; if (isActiveTime && isActiveCategory) { - if (event.type === TIMELINE_ONLY || isActiveFilter) { + if (event.type === SHAPE || isActiveFilter) { acc[event.id] = { ...event }; } } diff --git a/src/store/initial.js b/src/store/initial.js index 40219f8..56bff22 100644 --- a/src/store/initial.js +++ b/src/store/initial.js @@ -33,6 +33,8 @@ const initial = { associations: [], sources: {}, sites: [], + shapes: [], + regions: [], notifications: [], }, @@ -63,6 +65,7 @@ const initial = { sites: true, }, }, + shapes: [], isMobile: /Mobi/.test(navigator.userAgent), language: "en-US", cluster: { @@ -126,6 +129,12 @@ const initial = { title: copy[language].toolbar.explore_by_narrative__title, description: copy[language].toolbar.explore_by_narrative__description, }, + shapes: { + icon: DEFAULT_TAB_ICONS.SHAPE, + label: copy[language].toolbar.shapes_label, + title: copy[language].toolbar.explore_by_shape__title, + description: copy[language].toolbar.explore_by_shape__description, + }, }, }, loading: false, @@ -141,6 +150,7 @@ const initial = { style: { categories: { default: global.fallbackEventColor, + checkboxColor: colors.white, }, narratives: { default: { @@ -149,7 +159,7 @@ const initial = { strokeWidth: 3, }, }, - shapes: { + regions: { default: { stroke: "blue", strokeWidth: 3, @@ -159,6 +169,9 @@ const initial = { clusters: { radial: false, }, + shapes: { + checkboxColor: colors.white, + }, }, card: { layout: { @@ -182,7 +195,7 @@ const initial = { USE_ASSOCIATIONS: false, USE_SITES: false, USE_SOURCES: false, - USE_SHAPES: false, + USE_REGIONS: false, GRAPH_NONLOCATED: false, HIGHLIGHT_GROUPS: false, }, From 8206edb310b9ed49df9c781fa8308184af0453d2 Mon Sep 17 00:00:00 2001 From: efarooqui Date: Fri, 21 May 2021 14:48:40 -0700 Subject: [PATCH 2/5] Polygon checkbox working active and inactive; need to hook up action to filter through events --- src/common/constants.js | 3 +-- src/common/utilities.js | 12 +++++++--- src/components/atoms/Checkbox.js | 4 ++-- src/components/controls/atoms/PanelTree.js | 7 ++++-- src/scss/toolbar.scss | 26 ++++++++++++++++------ 5 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/common/constants.js b/src/common/constants.js index 371d923..2601f2a 100644 --- a/src/common/constants.js +++ b/src/common/constants.js @@ -27,7 +27,6 @@ export const POLYGON_CLIP_PATH = { STAR: "polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%)", DIAMOND: "polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)", - PENTAGON: - "polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%)", + PENTAGON: "polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%)", TRIANGLE: "polygon(50% 0%, 0% 100%, 100% 100%)", }; diff --git a/src/common/utilities.js b/src/common/utilities.js index 1e97607..5afe61c 100644 --- a/src/common/utilities.js +++ b/src/common/utilities.js @@ -515,9 +515,15 @@ export function mapStyleByShape(shapes, activeShapes) { const styledShapes = shapes.map((s) => { const { colour, shape, title } = s; const style = { - background: activeShapes.includes(title) ? colour : "none", - border: `1px solid ${colour}`, - clipPath: POLYGON_CLIP_PATH[shape], + checkboxStyles: { + background: activeShapes.includes(title) ? colour : "black", + border: "none", + clipPath: POLYGON_CLIP_PATH[shape], + }, + containerStyles: { + background: colour, + clipPath: POLYGON_CLIP_PATH[shape], + }, }; s.styles = style; return s; diff --git a/src/components/atoms/Checkbox.js b/src/components/atoms/Checkbox.js index d555a38..e35bced 100644 --- a/src/components/atoms/Checkbox.js +++ b/src/components/atoms/Checkbox.js @@ -5,8 +5,8 @@ const Checkbox = ({ label, isActive, onClickCheckbox, color, styleProps }) => {
    {label}
    diff --git a/src/components/controls/atoms/PanelTree.js b/src/components/controls/atoms/PanelTree.js index 2896056..18c65af 100644 --- a/src/components/controls/atoms/PanelTree.js +++ b/src/components/controls/atoms/PanelTree.js @@ -7,8 +7,11 @@ const PanelTree = ({ data, activeValues, onSelect, defaultCheckboxColor }) => { {data.map((val) => { const isActive = activeValues.includes(val.title); const baseStyles = { - background: isActive ? defaultCheckboxColor : "none", - border: `1px solid ${defaultCheckboxColor}`, + checkboxStyles: { + background: isActive ? defaultCheckboxColor : "none", + border: `1px solid ${defaultCheckboxColor}`, + }, + containerStyles: {}, }; return (
  • Date: Fri, 21 May 2021 16:18:35 -0700 Subject: [PATCH 3/5] event.shape is initially shape ID, interpolated during domain fetch; adding base styles for checkbox inline with said component; generalized checkbox comp across three panels --- src/common/constants.js | 2 ++ src/components/Toolbar.js | 4 --- src/components/atoms/Checkbox.js | 19 ++++++++++++-- .../controls/CategoriesListPanel.js | 2 -- src/components/controls/ShapesListPanel.js | 2 -- src/components/controls/atoms/PanelTree.js | 11 ++------ src/components/time/atoms/Events.js | 16 ++++++------ src/reducers/validate/validators.js | 25 ++++++++++++++----- src/selectors/index.js | 1 + src/store/initial.js | 4 --- 10 files changed, 50 insertions(+), 36 deletions(-) diff --git a/src/common/constants.js b/src/common/constants.js index 2601f2a..0ad7ec5 100644 --- a/src/common/constants.js +++ b/src/common/constants.js @@ -30,3 +30,5 @@ export const POLYGON_CLIP_PATH = { PENTAGON: "polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%)", TRIANGLE: "polygon(50% 0%, 0% 100%, 100% 100%)", }; + +export const DEFAULT_CHECKBOX_COLOR = "#ffffff"; diff --git a/src/components/Toolbar.js b/src/components/Toolbar.js index f85e6b9..30a9d51 100644 --- a/src/components/Toolbar.js +++ b/src/components/Toolbar.js @@ -121,7 +121,6 @@ class Toolbar extends React.Component { language={this.props.language} title={panels.categories.label} description={panels.categories.description} - checkboxColor={this.props.categoriesCheckboxColor} /> ); @@ -159,7 +158,6 @@ class Toolbar extends React.Component { language={this.props.language} title={panels.shapes.label} description={panels.shapes.description} - checkboxColor={this.props.shapesCheckboxColor} /> ); @@ -327,8 +325,6 @@ function mapStateToProps(state) { maxNumOfColors: state.ui.coloring.maxNumOfColors, filterColors: state.ui.coloring.colors, eventRadius: state.ui.eventRadius, - categoriesCheckboxColor: state.ui.style.categories.checkboxColor, - shapesCheckboxColor: state.ui.style.shapes.checkboxColor, features: selectors.getFeatures(state), }; } diff --git a/src/components/atoms/Checkbox.js b/src/components/atoms/Checkbox.js index e35bced..27a8376 100644 --- a/src/components/atoms/Checkbox.js +++ b/src/components/atoms/Checkbox.js @@ -1,12 +1,27 @@ import React from "react"; +import { DEFAULT_CHECKBOX_COLOR } from "../../common/constants"; const Checkbox = ({ label, isActive, onClickCheckbox, color, styleProps }) => { + const checkboxColor = color ? color : DEFAULT_CHECKBOX_COLOR; + const baseStyles = { + checkboxStyles: { + background: isActive ? checkboxColor : "none", + border: `1px solid ${checkboxColor}`, + }, + containerStyles: {}, + }; + const containerStyles = styleProps + ? styleProps.containerStyles + : baseStyles.containerStyles; + const checkboxStyles = styleProps + ? styleProps.checkboxStyles + : baseStyles.checkboxStyles; return (
    {label}
    diff --git a/src/components/controls/CategoriesListPanel.js b/src/components/controls/CategoriesListPanel.js index a150884..43d1535 100644 --- a/src/components/controls/CategoriesListPanel.js +++ b/src/components/controls/CategoriesListPanel.js @@ -9,7 +9,6 @@ const CategoriesListPanel = ({ language, title, description, - checkboxColor, }) => { return (
    @@ -23,7 +22,6 @@ const CategoriesListPanel = ({ data={categories} activeValues={activeCategories} onSelect={onCategoryFilter} - defaultCheckboxColor={checkboxColor} />
    ); diff --git a/src/components/controls/ShapesListPanel.js b/src/components/controls/ShapesListPanel.js index 7fa2119..04cf2e2 100644 --- a/src/components/controls/ShapesListPanel.js +++ b/src/components/controls/ShapesListPanel.js @@ -10,7 +10,6 @@ const ShapesListPanel = ({ language, title, description, - checkboxColor, }) => { const styledShapes = mapStyleByShape(shapes, activeShapes); return ( @@ -25,7 +24,6 @@ const ShapesListPanel = ({ data={styledShapes} activeValues={activeShapes} onSelect={onShapeFilter} - defaultCheckboxColor={checkboxColor} />
  • ); diff --git a/src/components/controls/atoms/PanelTree.js b/src/components/controls/atoms/PanelTree.js index 18c65af..47796ae 100644 --- a/src/components/controls/atoms/PanelTree.js +++ b/src/components/controls/atoms/PanelTree.js @@ -1,18 +1,11 @@ import React from "react"; import Checkbox from "../../atoms/Checkbox"; -const PanelTree = ({ data, activeValues, onSelect, defaultCheckboxColor }) => { +const PanelTree = ({ data, activeValues, onSelect }) => { return (
    {data.map((val) => { const isActive = activeValues.includes(val.title); - const baseStyles = { - checkboxStyles: { - background: isActive ? defaultCheckboxColor : "none", - border: `1px solid ${defaultCheckboxColor}`, - }, - containerStyles: {}, - }; return (
  • { label={val.title} isActive={isActive} onClickCheckbox={() => onSelect(val.title)} - styleProps={val.styles ? val.styles : baseStyles} + styleProps={val.styles} />
  • ); diff --git a/src/components/time/atoms/Events.js b/src/components/time/atoms/Events.js index fb543e2..7f5a0e0 100644 --- a/src/components/time/atoms/Events.js +++ b/src/components/time/atoms/Events.js @@ -157,19 +157,21 @@ const TimelineEvents = ({ (isLatitude(event.latitude) && isLongitude(event.longitude)) || (features.GRAPH_NONLOCATED && event.projectOffset !== -1); + const { shape: eventShape } = event; + let renderShape = isDot ? renderDot : renderBar; - if (event.shape) { - if (event.shape === AVAILABLE_SHAPES.BAR) { + if (eventShape.shape) { + if (eventShape.shape === AVAILABLE_SHAPES.BAR) { renderShape = renderBar; - } else if (event.shape === AVAILABLE_SHAPES.DIAMOND) { + } else if (eventShape.shape === AVAILABLE_SHAPES.DIAMOND) { renderShape = renderDiamond; - } else if (event.shape === AVAILABLE_SHAPES.STAR) { + } else if (eventShape.shape === AVAILABLE_SHAPES.STAR) { renderShape = renderStar; - } else if (event.shape === AVAILABLE_SHAPES.TRIANGLE) { + } else if (eventShape.shape === AVAILABLE_SHAPES.TRIANGLE) { renderShape = renderTriangle; - } else if (event.shape === AVAILABLE_SHAPES.PENTAGON) { + } else if (eventShape.shape === AVAILABLE_SHAPES.PENTAGON) { renderShape = renderPentagon; - } else if (event.shape === AVAILABLE_SHAPES.SQUARE) { + } else if (eventShape.shape === AVAILABLE_SHAPES.SQUARE) { renderShape = renderSquare; } else { renderShape = renderDot; diff --git a/src/reducers/validate/validators.js b/src/reducers/validate/validators.js index 995fd2f..8299575 100644 --- a/src/reducers/validate/validators.js +++ b/src/reducers/validate/validators.js @@ -156,6 +156,7 @@ export function validateDomain(domain, features) { // append events with datetime and sort sanitizedDomain.events = sanitizedDomain.events.filter((event, idx) => { + let errorMsg = ""; event.id = idx; // event.associations comes in as a [association.ids...]; convert to actual association objects event.associations = event.associations.reduce((acc, id) => { @@ -165,19 +166,31 @@ export function validateDomain(domain, features) { if (foundAssociation) acc.push(foundAssociation); return acc; }, []); + + if (event.shape) { + const relatedShapeObj = sanitizedDomain.shapes.find( + (elem) => elem.id === event.shape + ); + if (!relatedShapeObj) + errorMsg = + "Failed to find related shape. Please verify shape type for event."; + else { + event.shape = relatedShapeObj; + } + } // if lat, long come in with commas, replace with decimal format event.latitude = event.latitude.replace(",", "."); event.longitude = event.longitude.replace(",", "."); event.datetime = calcDatetime(event.date, event.time); - if (!isValidDate(event.datetime)) { + if (!isValidDate(event.datetime)) + errorMsg = + "Invalid date. It's been dropped, as otherwise timemap won't work as expected."; + + if (errorMsg) { discardedDomain.events.push({ ...event, - error: makeError( - "events", - event.id, - "Invalid date. It's been dropped, as otherwise timemap won't work as expected." - ), + error: makeError("events", event.id, errorMsg), }); return false; } diff --git a/src/selectors/index.js b/src/selectors/index.js index 1e6f63c..d03237c 100644 --- a/src/selectors/index.js +++ b/src/selectors/index.js @@ -75,6 +75,7 @@ export const selectRegions = createSelector( export const selectEvents = createSelector( [getEvents, getActiveFilters, getActiveCategories, getTimeRange, getFeatures], (events, activeFilters, activeCategories, timeRange, features) => { + console.info("SELECTED EVENTS SELECTOR"); return events.reduce((acc, event) => { const isMatchingFilter = (event.associations && diff --git a/src/store/initial.js b/src/store/initial.js index 56bff22..d0bd782 100644 --- a/src/store/initial.js +++ b/src/store/initial.js @@ -150,7 +150,6 @@ const initial = { style: { categories: { default: global.fallbackEventColor, - checkboxColor: colors.white, }, narratives: { default: { @@ -169,9 +168,6 @@ const initial = { clusters: { radial: false, }, - shapes: { - checkboxColor: colors.white, - }, }, card: { layout: { From 8e8416136ecc921dac71aa1e3b2766932d535298 Mon Sep 17 00:00:00 2001 From: efarooqui Date: Fri, 21 May 2021 17:17:43 -0700 Subject: [PATCH 4/5] All filters working in conjunction; can filter on shape, filter, and category separately --- src/actions/index.js | 9 ++++++ src/common/utilities.js | 4 +-- .../controls/CategoriesListPanel.js | 2 ++ src/components/controls/ShapesListPanel.js | 2 ++ src/components/controls/atoms/PanelTree.js | 10 ++++--- src/reducers/app.js | 11 +++++++ src/selectors/index.js | 29 +++++++++++++++---- 7 files changed, 56 insertions(+), 11 deletions(-) diff --git a/src/actions/index.js b/src/actions/index.js index b0c7722..9730c21 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -121,6 +121,7 @@ export function fetchDomain() { } dispatch(toggleFetchingDomain()); dispatch(setInitialCategories(result.associations)); + dispatch(setInitialShapes(result.shapes)); return result; }) .catch((err) => { @@ -245,6 +246,14 @@ export function setInitialCategories(values) { }; } +export const SET_INITIAL_SHAPES = "SET_INITIAL_SHAPES"; +export function setInitialShapes(values) { + return { + type: SET_INITIAL_SHAPES, + values, + }; +} + export const UPDATE_TIMERANGE = "UPDATE_TIMERANGE"; export function updateTimeRange(timerange) { return { diff --git a/src/common/utilities.js b/src/common/utilities.js index 5afe61c..6aefd98 100644 --- a/src/common/utilities.js +++ b/src/common/utilities.js @@ -513,10 +513,10 @@ export function setD3Locale(d3) { export function mapStyleByShape(shapes, activeShapes) { const styledShapes = shapes.map((s) => { - const { colour, shape, title } = s; + const { colour, shape, id } = s; const style = { checkboxStyles: { - background: activeShapes.includes(title) ? colour : "black", + background: activeShapes.includes(id) ? colour : "black", border: "none", clipPath: POLYGON_CLIP_PATH[shape], }, diff --git a/src/components/controls/CategoriesListPanel.js b/src/components/controls/CategoriesListPanel.js index 43d1535..47c85b7 100644 --- a/src/components/controls/CategoriesListPanel.js +++ b/src/components/controls/CategoriesListPanel.js @@ -1,6 +1,7 @@ import React from "react"; import marked from "marked"; import PanelTree from "./atoms/PanelTree"; +import { ASSOCIATION_MODES } from "../../common/constants"; const CategoriesListPanel = ({ categories, @@ -22,6 +23,7 @@ const CategoriesListPanel = ({ data={categories} activeValues={activeCategories} onSelect={onCategoryFilter} + type={ASSOCIATION_MODES.CATEGORY} />
    ); diff --git a/src/components/controls/ShapesListPanel.js b/src/components/controls/ShapesListPanel.js index 04cf2e2..f72669c 100644 --- a/src/components/controls/ShapesListPanel.js +++ b/src/components/controls/ShapesListPanel.js @@ -2,6 +2,7 @@ import React from "react"; import marked from "marked"; import PanelTree from "./atoms/PanelTree"; import { mapStyleByShape } from "../../common/utilities"; +import { SHAPE } from "../../common/constants"; const ShapesListPanel = ({ shapes, @@ -24,6 +25,7 @@ const ShapesListPanel = ({ data={styledShapes} activeValues={activeShapes} onSelect={onShapeFilter} + type={SHAPE} /> ); diff --git a/src/components/controls/atoms/PanelTree.js b/src/components/controls/atoms/PanelTree.js index 47796ae..6da7098 100644 --- a/src/components/controls/atoms/PanelTree.js +++ b/src/components/controls/atoms/PanelTree.js @@ -1,11 +1,13 @@ import React from "react"; import Checkbox from "../../atoms/Checkbox"; +import { ASSOCIATION_MODES } from "../../../common/constants"; -const PanelTree = ({ data, activeValues, onSelect }) => { +const PanelTree = ({ data, activeValues, onSelect, type }) => { + // If the parent panel is of type 'CATEGORY': filter on title. If panel is 'SHAPE': filter on id + const onSelectionType = type === ASSOCIATION_MODES.CATEGORY ? "title" : "id"; return (
    {data.map((val) => { - const isActive = activeValues.includes(val.title); return (
  • { > onSelect(val.title)} + isActive={activeValues.includes(val[onSelectionType])} + onClickCheckbox={() => onSelect(val[onSelectionType])} styleProps={val.styles} />
  • diff --git a/src/reducers/app.js b/src/reducers/app.js index 5337b96..829e8a8 100644 --- a/src/reducers/app.js +++ b/src/reducers/app.js @@ -27,6 +27,7 @@ import { SET_LOADING, SET_NOT_LOADING, SET_INITIAL_CATEGORIES, + SET_INITIAL_SHAPES, UPDATE_SEARCH_QUERY, } from "../actions"; @@ -272,6 +273,14 @@ function setInitialCategories(appState, action) { }; } +function setInitialShapes(appState, action) { + const shapeIds = action.values.map((sh) => sh.id); + return { + ...appState, + shapes: shapeIds, + }; +} + function updateSearchQuery(appState, action) { return { ...appState, @@ -331,6 +340,8 @@ function app(appState = initial.app, action) { return setNotLoading(appState); case SET_INITIAL_CATEGORIES: return setInitialCategories(appState, action); + case SET_INITIAL_SHAPES: + return setInitialShapes(appState, action); case UPDATE_SEARCH_QUERY: return updateSearchQuery(appState, action); default: diff --git a/src/selectors/index.js b/src/selectors/index.js index d03237c..a5e3fe9 100644 --- a/src/selectors/index.js +++ b/src/selectors/index.js @@ -73,9 +73,22 @@ export const selectRegions = createSelector( * 3. exist in an active category */ export const selectEvents = createSelector( - [getEvents, getActiveFilters, getActiveCategories, getTimeRange, getFeatures], - (events, activeFilters, activeCategories, timeRange, features) => { - console.info("SELECTED EVENTS SELECTOR"); + [ + getEvents, + getActiveFilters, + getActiveCategories, + getActiveShapes, + getTimeRange, + getFeatures, + ], + ( + events, + activeFilters, + activeCategories, + activeShapes, + timeRange, + features + ) => { return events.reduce((acc, event) => { const isMatchingFilter = (event.associations && @@ -98,8 +111,14 @@ export const selectEvents = createSelector( isActiveTime = features.GRAPH_NONLOCATED ? (!event.latitude && !event.longitude) || isActiveTime : isActiveTime; - if (isActiveTime && isActiveCategory) { - if (event.type === SHAPE || isActiveFilter) { + const isActiveShape = + event.shape && activeShapes.includes(event.shape.id); + if (event.type === SHAPE) { + if (isActiveShape && isActiveCategory && isActiveTime) { + acc[event.id] = { ...event }; + } + } else { + if (isActiveFilter && isActiveCategory && isActiveTime) { acc[event.id] = { ...event }; } } From 65f32e8e0cc1faebd7349b4d32e2aedf67349571 Mon Sep 17 00:00:00 2001 From: efarooqui Date: Mon, 24 May 2021 22:09:51 -0700 Subject: [PATCH 5/5] Minor edit to containerStyles for Checkbox component --- src/components/atoms/Checkbox.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/atoms/Checkbox.js b/src/components/atoms/Checkbox.js index 27a8376..f90c1fa 100644 --- a/src/components/atoms/Checkbox.js +++ b/src/components/atoms/Checkbox.js @@ -8,11 +8,8 @@ const Checkbox = ({ label, isActive, onClickCheckbox, color, styleProps }) => { background: isActive ? checkboxColor : "none", border: `1px solid ${checkboxColor}`, }, - containerStyles: {}, }; - const containerStyles = styleProps - ? styleProps.containerStyles - : baseStyles.containerStyles; + const containerStyles = styleProps ? styleProps.containerStyles : {}; const checkboxStyles = styleProps ? styleProps.checkboxStyles : baseStyles.checkboxStyles;