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

Fix #33 - Create Backend Provider and Data Context consumers/providers #37

Merged
merged 2 commits into from
Oct 31, 2020
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
16 changes: 10 additions & 6 deletions app/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import ErrorCatcher from './ErrorCatcher';
import Dashboard from './Dashboard';
import { WidgetsList } from './Widget';

import BackendProvider from './Backend/Provider';

// App Helmet: Controls HTML <head> elements with SideEffect
// - Set a default title and title template, translated
const AppHelmet = (props) => {
Expand All @@ -31,12 +33,14 @@ const AppProviders = (props) => {
return (
<TranslatorProvider translations={ props.translations }>
<HelmetProvider>
<AppHelmet language={ props.language } />
<Router>
<div className='App' id='router-container'>
{ props.children }
</div>
</Router>
<BackendProvider>
<AppHelmet language={ props.language } />
<Router>
<div className='App' id='router-container'>
{ props.children }
</div>
</Router>
</BackendProvider>
</HelmetProvider>
</TranslatorProvider>
);
Expand Down
31 changes: 31 additions & 0 deletions app/src/Backend/Bcn/BcnContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { createContext, useContext } from 'react';
import BcnData from './index';

const BcnContext = createContext();

const BcnProvider = ({children}) => (
<BcnContext.Provider value={BcnData}>
{children}
</BcnContext.Provider>
);

const useBcnData = () => {
const context = useContext(BcnContext);

if (context === undefined) {
throw new Error('BcnData must be used within a BcnProvider');
}

return context;
}

const BcnConsumer = ({children}) => children(useBcnData());

const withBcnDataHandler = (WrappedComponent) => (props) => (
<BcnConsumer>
{ context => <WrappedComponent {...props} bcnDataHandler={context} /> }
</BcnConsumer>
);

export default BcnProvider;
export { withBcnDataHandler, BcnConsumer, useBcnData, BcnContext };
31 changes: 31 additions & 0 deletions app/src/Backend/Charts/ChartsContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { createContext, useContext } from 'react';
import ChartsData from './index';

const ChartsContext = createContext();

const ChartsProvider = ({children}) => (
<ChartsContext.Provider value={ChartsData}>
{children}
</ChartsContext.Provider>
);

const useChartsData = () => {
const context = useContext(ChartsContext);

if (context === undefined) {
throw new Error('useChartsData must be used within a ChartsProvider');
}

return context;
};

const ChartsConsumer = ({children}) => children(useChartsData());

const withChartsDataHandler = (WrappedComponent) => (props) => (
<ChartsContext.Consumer>
{context => <WrappedComponent {...props} chartsDataHandler={context} />}
</ChartsContext.Consumer>
);

export default ChartsProvider;
export { withChartsDataHandler, ChartsConsumer, useChartsData, ChartsContext };
31 changes: 31 additions & 0 deletions app/src/Backend/Maps/MapsContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { createContext, useContext } from 'react';
import MapsData from './index';

const MapsContext = createContext();

const MapsProvider = ({children}) => (
<MapsContext.Provider value={MapsData}>
{children}
</MapsContext.Provider>
);

const useMapsData = () => {
const context = useContext(MapsContext);

if (context === undefined) {
throw new Error('MapsData must be used within a MapsProvider');
}

return context;
}

const MapsConsumer = ({children}) => children(useMapsData())

const withMapsDataHandler = (WrappedComponent) => (props) => (
<MapsConsumer>
{context => <WrappedComponent {...props} mapsDataHandler={context} />}
</MapsConsumer>
);

export default MapsProvider;
export { withMapsDataHandler, MapsConsumer, useMapsData, MapsContext };
19 changes: 19 additions & 0 deletions app/src/Backend/Provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from "react";

import BcnProvider from './Bcn/BcnContext';
import MapsProvider from './Maps/MapsContext';
import ChartsProvider from './Charts/ChartsContext';

const BackendProvider = ({children}) => {
return (
<BcnProvider>
<MapsProvider>
<ChartsProvider>
{children}
</ChartsProvider>
</MapsProvider>
</BcnProvider>
);
};

export default BackendProvider;
57 changes: 29 additions & 28 deletions app/src/Widget/List.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ import Paper from '@material-ui/core/Paper';
import MenuAddWidget from './MenuAddWidget';
import WidgetsTypes from './Widgets';

import MapData from '../Backend/Maps';
import ChartData from '../Backend/Charts';
import BcnData from '../Backend/Bcn';
import { withBcnDataHandler } from '../Backend/Bcn/BcnContext';
import { withMapsDataHandler } from '../Backend/Maps/MapsContext';
import { withChartsDataHandler } from '../Backend/Charts/ChartsContext';

import Throtle from '../Throtle';
import Slider from '../Slider';
import Loading from '../Loading';

import WidgetDataContextProvider from './DataContextProvider';
import WidgetDataContextProvider from './DataContextProvider';
import withPropHandler from './withPropHandler';

// GUID generator: used to create unique dtemporal IDs for widgets
Expand Down Expand Up @@ -134,29 +134,23 @@ class WidgetsList extends React.PureComponent {
// with excessive calls on mouse move
throtle = new Throtle();

// Map Data backend
// TODO: Handle errors
MapData = new MapData();

// Chart Data backend
// TODO: Handle errors
ChartData = new ChartData();

// BCN Data backend
// TODO: Handle errors
BcnData = new BcnData();

constructor(props) {
super(props);
// Generate initial list of unique IDs
if ('widgets' in props) {
this.updateIDs(props.widgets.length);
}

this.bcnData = new props.bcnDataHandler();
this.chartsData = new props.chartsDataHandler();
this.mapData = new props.mapsDataHandler();
}

// Fetch data once mounted
componentDidMount() {
this.MapData.days(
const { mapData, chartsData, bcnData } = this;

mapData.days(
days => {
// Are we on the last time serie element before update?
const isLast =
Expand Down Expand Up @@ -186,34 +180,35 @@ class WidgetsList extends React.PureComponent {

// Once data has been fetched, schedule next data update
// Mind the timer on unmount
this.MapData.scheduleNextUpdate();
mapData.scheduleNextUpdate();
});
this.ChartData.index(
chartsData.index(
chartsIndex => {

this.setState({ chartsIndex });

// Once data has been fetched, schedule next data update
// Mind the timer on unmount
this.ChartData.scheduleNextUpdate();
chartsData.scheduleNextUpdate();
});
this.BcnData.index(
bcnData.index(
bcnIndex => {

this.setState({ bcnIndex });

// Once data has been fetched, schedule next data update
// Mind the timer on unmount
this.BcnData.scheduleNextUpdate();
bcnData.scheduleNextUpdate();
});
}

// Cleanup side effects
componentWillUnmount() {
const { mapData, chartsData, bcnData } = this;

// Cancel next update timers
this.MapData.cancelUpdateSchedule();
this.ChartData.cancelUpdateSchedule();
this.BcnData.cancelUpdateSchedule();
bcnData.cancelUpdateSchedule();
chartsData.cancelUpdateSchedule();
mapData.cancelUpdateSchedule();

// Cancel possible throtle timer
this.throtle.clear();
Expand Down Expand Up @@ -355,12 +350,18 @@ const withShowMarkLabels = (Component) => {
// withPropHandler: Handle params from providers (route + localStorage) into props
// withStyles: Add `classes` prop for styling components
// withShowMarkLabels: Add `showMarkLabels` breakpoint to sho/hide Slider mark labels depending on sreen size
// withBcnDataHandler: Add `bcnDataHandler` prop to use bcn backend data
// withMapsDataHandler: Add `mapsDataHandler` prop to use maps backend data
// withChartsDataHandler: Add `chartsDataHandler` prop to use charts backend data
const WidgetsListWithPropHandler =
withPropHandler(
withStyles(useStyles)(
withShowMarkLabels(
WidgetsList
)));
withBcnDataHandler(
withMapsDataHandler(
withChartsDataHandler(
WidgetsList
))))));

// Manage some context providers details:
// - pathFilter: How to split `location` (Route `path` prop)
Expand Down
16 changes: 9 additions & 7 deletions app/src/Widget/Widgets/Bcn/EditDataset.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
import TreeItem from '@material-ui/lab/TreeItem';

import BcnData from '../../../Backend/Bcn';
import { withBcnDataHandler } from '../../../Backend/Bcn/BcnContext';

// From: https://material-ui.com/components/tree-view/#rich-object

Expand Down Expand Up @@ -38,22 +38,24 @@ class RecursiveTreeView extends React.Component {
this.props.onChange(value);
}
}

componentWillMount = () => {
const bcnData = new BcnData(this.props.bcnIndex);
const {value} = this.props;
const { value, bcnIndex, bcnDataHandler } = this.props;

const bcnData = new bcnDataHandler(bcnIndex);

this.setState({
value,
breadcrumb: bcnData
.findBreadcrumb(null, this.props.value)
.findBreadcrumb(null, value)
.map(node => `${!('values' in node) ? 'DISABLED-' : ''}${node.code}`),
});
}

render = (props) => {
const { classes, bcnIndex, ...restProps } = this.props;
const { breadcrumb, value } = this.state;

return (
<TreeView
className={classes.root}
Expand All @@ -72,4 +74,4 @@ class RecursiveTreeView extends React.Component {
}
}

export default withStyles(styles)(RecursiveTreeView);
export default withBcnDataHandler(withStyles(styles)(RecursiveTreeView));
23 changes: 14 additions & 9 deletions app/src/Widget/Widgets/Bcn/Widget.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
faEdit,
} from '@fortawesome/free-solid-svg-icons'

import BcnData from '../../../Backend/Bcn';
import { withBcnDataHandler } from '../../../Backend/Bcn/BcnContext';

import Chart from '../Common/Chart';

Expand Down Expand Up @@ -65,10 +65,10 @@ class ChartDataHandler extends React.Component {
super(props);

// Default values: first element of each's group
const { bcnIndex, dataset/*, section*/ } = props;
const { bcnDataHandler, bcnIndex, dataset/*, section*/ } = props;

// TODO: Handle errors
this.BcnData = new BcnData(bcnIndex);
this.BcnData = new bcnDataHandler(bcnIndex);
this.cancelDataUpdate = false;

this.state = {
Expand Down Expand Up @@ -134,21 +134,26 @@ class ChartDataHandler extends React.Component {
}

componentDidUpdate(prevProps, prevState) {
const {
state: { bcnData },
props: { bcnDataHandler, dataset, bcnIndex }
} = this;

// If bcnData is unset, gather new data
if( prevState.bcnData !== this.state.bcnData &&
this.state.bcnData === null ) {
if( prevState.bcnData !== bcnData &&
bcnData === null ) {
this.updateData();
}
else if (
// If params changed, unset bcnData
this.props.dataset !== prevProps.dataset ) {
dataset !== prevProps.dataset ) {
this.setState({
bcnData: null
});
}
else if ( this.props.bcnIndex !== prevProps.bcnIndex ) {
else if ( bcnIndex !== prevProps.bcnIndex ) {
// If bcnIndex changed, re-create backend with new data
this.BcnData = new BcnData(this.props.bcnIndex);
this.BcnData = new bcnDataHandler(bcnIndex);
this.setState({
bcnData: null
});
Expand Down Expand Up @@ -227,4 +232,4 @@ ChartDataHandler.propTypes = {
dataset: PropTypes.string.isRequired,
};

export default ChartDataHandler;
export default withBcnDataHandler(ChartDataHandler);
Loading