Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/move widget selectors to parent level #3672

Merged
merged 1 commit into from
Dec 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions app/javascript/components/map/selectors.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { createSelector, createStructuredSelector } from 'reselect';
import isEmpty from 'lodash/isEmpty';

import {
filterWidgetsByCategoryAndLayers,
getActiveWidget
} from 'components/widgets/selectors';
import { getActiveWidget, getWidgets } from 'pages/dashboards/selectors';

// get list data
const selectLoading = state => state.mapOld.loading || state.geostore.loading;
Expand All @@ -19,13 +16,9 @@ const selectBounds = state =>
(state.geostore.data && state.geostore.data.bounds) || null;

export const getMapSettings = createSelector(
[
selectSettings,
filterWidgetsByCategoryAndLayers,
getActiveWidget,
selectQuery
],
[selectSettings, getWidgets, getActiveWidget, selectQuery],
(settings, widgets, widget, query) => {
if (!widgets) return settings;
const widgetUrlState = query && query[widget];
const activeWidget = widgets.find(w => w.widget === widget);
const widgetSettings = activeWidget && activeWidget.settings;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ class ShowAnalysis extends PureComponent {
downloadUrls,
hasLayers,
hasWidgets,
zoomLevel
zoomLevel,
widgets
} = this.props;

return (
Expand Down Expand Up @@ -167,7 +168,7 @@ class ShowAnalysis extends PureComponent {
<ul className="draw-stats">
{data && data.map(d => this.renderStatItem(d))}
</ul>
<Widgets simple analysis />
<Widgets simple analysis widgets={widgets} />
<div className="disclaimers">
{zoomLevel < 11 && (
<p>
Expand Down Expand Up @@ -219,6 +220,7 @@ ShowAnalysis.propTypes = {
hasLayers: PropTypes.bool,
hasWidgets: PropTypes.bool,
downloadUrls: PropTypes.array,
widgets: PropTypes.array,
zoomLevel: PropTypes.number
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import { createSelector, createStructuredSelector } from 'reselect';

import { buildLocationName, buildFullLocationName } from 'utils/format';

import { getActiveLayers, getMapZoom } from 'components/maps/map/selectors';
import {
getActiveLayers,
getMapZoom,
getWidgetsWithLayerParams
} from 'components/maps/map/selectors';
import { getWidgetLayers } from 'components/maps/components/analysis/selectors';

const selectLocation = state => state.location && state.location.payload;
Expand Down Expand Up @@ -148,5 +152,6 @@ export const getShowAnalysisProps = createStructuredSelector({
layers: getActiveLayers,
downloadUrls: getDownloadLinks,
error: selectError,
widgets: getWidgetsWithLayerParams,
zoomLevel: getMapZoom
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import flatMap from 'lodash/flatMap';
import {
getAllBoundaries,
getActiveBoundaryDatasets,
getAllLayers
getAllLayers,
getWidgetsWithLayerParams
} from 'components/maps/map/selectors';
import { filterWidgetsByCategoryAndLayers } from 'components/widgets/selectors';

import layersIcon from 'assets/icons/layers.svg';
import analysisIcon from 'assets/icons/analysis.svg';
Expand Down Expand Up @@ -54,7 +54,7 @@ export const getShowDraw = createSelector(
);

export const getWidgetLayers = createSelector(
filterWidgetsByCategoryAndLayers,
getWidgetsWithLayerParams,
widgets => {
const activeWidgets =
widgets &&
Expand Down
59 changes: 49 additions & 10 deletions app/javascript/components/maps/map/selectors.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { createSelector, createStructuredSelector } from 'reselect';
import flatten from 'lodash/flatten';
import isEmpty from 'lodash/isEmpty';
import moment from 'moment';
import intersection from 'lodash/intersection';

import { parseWidgetsWithOptions } from 'components/widgets/selectors';
import { initialState } from './reducers';
import basemaps, { labels } from './basemaps-schema';

Expand All @@ -24,9 +27,6 @@ const selectGeostore = state => state.geostore.data || null;
const selectSelectedInteractionId = state => state.popup.selected;
const selectInteractions = state => state.popup.interactions;

// get widgets
const selectWidgetActiveSettings = state => state.widgets.settings;

// CONSTS
export const getBasemaps = () => basemaps;
export const getLabels = () => labels;
Expand Down Expand Up @@ -319,18 +319,57 @@ export const getActiveLayers = createSelector(getAllLayers, layers => {
return layers.filter(l => !l.confirmedOnly);
});

// get widgets related to map layers and use them to build the layers
export const getWidgetsWithLayerParams = createSelector(
[parseWidgetsWithOptions, getAllLayers],
(widgets, layers) => {
if (!widgets || !widgets.length || !layers || !layers.length) return null;
const layerIds = layers && layers.map(l => l.id);
const filteredWidgets = widgets.filter(w => {
const layerIntersection = intersection(w.config.layers, layerIds);
return w.config.analysis && layerIntersection && layerIntersection.length;
});
return filteredWidgets.map(w => {
const widgetLayer =
layers && layers.find(l => w.config && w.config.layers.includes(l.id));
const { params, decodeParams } = widgetLayer || {};
const startDate =
(params && params.startDate) ||
(decodeParams && decodeParams.startDate);
const startYear =
startDate && parseInt(moment(startDate).format('YYYY'), 10);
const endDate =
(params && params.endDate) || (decodeParams && decodeParams.endDate);
const endYear = endDate && parseInt(moment(endDate).format('YYYY'), 10);

return {
...w,
settings: {
...w.settings,
...params,
...decodeParams,
...(startYear && {
startYear
}),
...(endYear && {
endYear
})
}
};
});
}
);

// flatten datasets into layers for the layer manager
export const getActiveLayersWithWidgetSettings = createSelector(
[getAllLayers, selectWidgetActiveSettings],
(layers, widgetSettings) => {
[getAllLayers, getWidgetsWithLayerParams],
(layers, widgets) => {
if (isEmpty(layers)) return [];
if (isEmpty(widgetSettings)) return layers;
if (isEmpty(widgets)) return layers;
return layers.map(l => {
const layerWidgetState =
widgetSettings &&
Object.values(widgetSettings).find(
w => w.layers && w.layers.includes(l.id)
);
widgets &&
Object.values(widgets).find(w => w.layers && w.layers.includes(l.id));
const { updateLayer } = layerWidgetState || {};
return {
...l,
Expand Down
76 changes: 1 addition & 75 deletions app/javascript/components/widgets/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@ import sortBy from 'lodash/sortBy';
import uniq from 'lodash/uniq';
import concat from 'lodash/concat';
import lowerCase from 'lodash/lowerCase';
import camelCase from 'lodash/camelCase';
import intersection from 'lodash/intersection';
import moment from 'moment';

import { getAllLayers } from 'components/maps/map/selectors';

import tropicalIsos from 'data/tropical-isos.json';
import colors from 'data/colors.json';
Expand All @@ -19,7 +14,6 @@ export const selectAnalysis = (state, { analysis }) => analysis;
export const selectAllLocation = state => state.location;
export const selectLocationType = state =>
state.location && state.location.payload && state.location.payload.type;
export const selectQuery = state => state.location && state.location.query;
export const selectWidgetFromQuery = state =>
state.location && state.location.query && state.location.query.widget;
export const selectEmbed = (state, { embed }) => embed;
Expand Down Expand Up @@ -139,11 +133,6 @@ export const isTropicalLocation = createSelector([selectLocation], location =>
tropicalIsos.includes(location && location.adm0)
);

export const getCategory = createSelector(
[selectQuery],
query => (query && query.category) || 'summary'
);

export const getNoWidgetsMessage = createSelector(
[getLocationName, selecteNoWidgetsMessage],
(locationName, message) =>
Expand Down Expand Up @@ -172,7 +161,7 @@ export const parseWidgets = createSelector(
export const filterWidgetsByLocation = createSelector(
[parseWidgets, selectLocationType, getAdminLevel],
(widgets, type, adminLevel) => {
if (!widgets) return null;
if (!widgets || !type) return null;
return widgets.filter(w => {
const { types, admins } = w.config || {};
return (
Expand Down Expand Up @@ -291,80 +280,17 @@ export const parseWidgetsWithOptions = createSelector(
}
);

export const filterWidgetsByCategoryAndLayers = createSelector(
[parseWidgetsWithOptions, getCategory, getAllLayers, selectAnalysis],
(widgets, category, layers, analysis) => {
if (!widgets) return null;
let filteredWidgets = widgets;
if (analysis) {
const layerIds = layers && layers.map(l => l.id);
filteredWidgets = widgets.filter(w => {
const layerIntersection = intersection(w.config.layers, layerIds);
return (
w.config.analysis && layerIntersection && layerIntersection.length
);
});
filteredWidgets = filteredWidgets.map(w => {
const widgetLayer =
layers &&
layers.find(l => w.config && w.config.layers.includes(l.id));
const { params, decodeParams } = widgetLayer || {};
const startDate =
(params && params.startDate) ||
(decodeParams && decodeParams.startDate);
const startYear =
startDate && parseInt(moment(startDate).format('YYYY'), 10);
const endDate =
(params && params.endDate) || (decodeParams && decodeParams.endDate);
const endYear = endDate && parseInt(moment(endDate).format('YYYY'), 10);

return {
...w,
settings: {
...w.settings,
...params,
...decodeParams,
...(startYear && {
startYear
}),
...(endYear && {
endYear
})
}
};
});
} else {
filteredWidgets = widgets.filter(w =>
w.config.categories.includes(category)
);
}

return sortBy(filteredWidgets, `config.sortOrder[${camelCase(category)}]`);
}
);

export const getActiveWidget = createSelector(
[filterWidgetsByCategoryAndLayers, selectQuery],
(widgets, query) => {
if (query && query.widget) return query.widget;
return widgets && widgets.length && widgets[0].widget;
}
);

export const getWidgetsProps = createStructuredSelector({
loading: selectLoading,
whitelists: selectWhitelists,
whitelist: getActiveWhitelist,
activeWidget: getActiveWidget,
category: getCategory,
allLocation: selectAllLocation,
location: selectLocation,
locationType: selectLocationType,
locationData: getActiveLocationData,
locationObject: getLocationObject,
locationName: getLocationName,
childLocationData: getChildLocationData,
widgets: filterWidgetsByCategoryAndLayers,
noWidgetsMessage: getNoWidgetsMessage,
isTropical: isTropicalLocation
});
10 changes: 8 additions & 2 deletions app/javascript/pages/dashboards/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ class Page extends PureComponent {
widgetAnchor,
setMapZoom,
handleCategoryChange,
noWidgetsMessage
noWidgetsMessage,
widgets,
activeWidget
} = this.props;

return (
Expand Down Expand Up @@ -60,6 +62,8 @@ class Page extends PureComponent {
<Widgets
className="dashboard-widgets"
noWidgetsMessage={noWidgetsMessage}
widgets={widgets}
activeWidget={activeWidget}
/>
</div>
<div className={`map-panel ${showMapMobile ? '-open-mobile' : ''}`}>
Expand Down Expand Up @@ -100,7 +104,9 @@ Page.propTypes = {
widgetAnchor: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
setMapZoom: PropTypes.func,
noWidgetsMessage: PropTypes.string,
handleCategoryChange: PropTypes.func
handleCategoryChange: PropTypes.func,
widgets: PropTypes.array,
activeWidget: PropTypes.string
};

export default Page;
24 changes: 24 additions & 0 deletions app/javascript/pages/dashboards/selectors.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { createSelector, createStructuredSelector } from 'reselect';
import replace from 'lodash/replace';
import upperFirst from 'lodash/upperFirst';
import camelCase from 'lodash/camelCase';
import sortBy from 'lodash/sortBy';

import { parseWidgetsWithOptions } from 'components/widgets/selectors';

import CATEGORIES from 'data/categories.json';

Expand All @@ -9,6 +13,7 @@ const selectShowMap = state => !!state.map.showMapMobile;
const selectCategory = state =>
(state.location && state.location.query && state.location.query.category) ||
'summary';
export const selectQuery = state => state.location && state.location.query;

export const getLinks = createSelector([selectCategory], activeCategory =>
CATEGORIES.map(category => ({
Expand All @@ -29,10 +34,29 @@ export const getNoWidgetsMessage = createSelector(
category => `${upperFirst(category)} data for {location} coming soon`
);

export const getWidgets = createSelector(
[parseWidgetsWithOptions, selectCategory],
(widgets, category) =>
sortBy(
widgets.filter(w => w.config.categories.includes(category)),
`config.sortOrder[${camelCase(category)}]`
)
);

export const getActiveWidget = createSelector(
[getWidgets, selectQuery],
(widgets, query) => {
if (query && query.widget) return query.widget;
return widgets && widgets.length && widgets[0].widget;
}
);

export const getDashboardsProps = createStructuredSelector({
showMapMobile: selectShowMap,
category: selectCategory,
links: getLinks,
widgets: getWidgets,
activeWidget: getActiveWidget,
widgetAnchor: getWidgetAnchor,
noWidgetsMessage: getNoWidgetsMessage
});