Skip to content

Commit

Permalink
feat: fetch items chart data on app level (#271)
Browse files Browse the repository at this point in the history
Relates to DHIS2-6320

Changes include:
- New approach for rendering chart items: fetching full analytical object (AO) on app level and passing it down to plugins to make filtering possible and avoid redundant network requests.
- Using progressive loader for `Visualization/Item` component instead of its content component, so that fetching AO happens only when component is mounted.
- Mounting chart to div inside `DefaultPlugin` component for correct further unmount.
- Reenabling new `ChartPlugin` component for CHART visualization type.
- Pass dynamic height via styles prop to plugins instead of setting height of div containing the plugin.
  • Loading branch information
neeilya authored Apr 3, 2019
1 parent 7bea49b commit a2e3e94
Show file tree
Hide file tree
Showing 10 changed files with 1,222 additions and 736 deletions.
7 changes: 5 additions & 2 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2019-03-01T11:56:20.216Z\n"
"PO-Revision-Date: 2019-03-01T11:56:20.216Z\n"
"POT-Creation-Date: 2019-04-02T12:31:20.966Z\n"
"PO-Revision-Date: 2019-04-02T12:31:20.966Z\n"

msgid "Dashboard"
msgstr ""
Expand Down Expand Up @@ -74,6 +74,9 @@ msgstr ""
msgid "Unable to load the plugin for this item"
msgstr ""

msgid "No data to display"
msgstr ""

msgid "There are no items on this dashboard"
msgstr ""

Expand Down
12 changes: 11 additions & 1 deletion src/__tests__/util.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { validateReducer, getBaseUrl } from '../modules/util';
import { validateReducer, getBaseUrl, getWithoutId } from '../modules/util';

describe('util', () => {
describe('validateReducer', () => {
Expand Down Expand Up @@ -50,4 +50,14 @@ describe('util', () => {
expect(actual).toEqual(baseUrl);
});
});

describe('getWithoutId', () => {
it('removes id property from an object', () => {
const object = { id: 'SOME_ID', someProp: 'someValue' };
const expectedResult = { someProp: 'someValue' };
const actualResult = getWithoutId(object);

expect(actualResult).toEqual(expectedResult);
});
});
});
21 changes: 13 additions & 8 deletions src/components/Item/Item.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ListItem from './ListItem/Item';
import TextItem from './TextItem/Item';
import AppItem from './AppItem/Item';
import SpacerItem from './SpacerItem/Item';
import ProgressiveLoadingContainer from './ProgressiveLoadingContainer';
import {
APP,
REPORT_TABLE,
Expand Down Expand Up @@ -52,13 +53,17 @@ export const Item = props => {
const GridItem = getGridItem(props.item.type);

return (
<GridItem
item={props.item}
editMode={props.editMode}
itemFilter={
props.editMode ? DEFAULT_STATE_ITEM_FILTER : props.itemFilter
}
onToggleItemExpanded={props.onToggleItemExpanded}
/>
<ProgressiveLoadingContainer>
<GridItem
item={props.item}
editMode={props.editMode}
itemFilter={
props.editMode
? DEFAULT_STATE_ITEM_FILTER
: props.itemFilter
}
onToggleItemExpanded={props.onToggleItemExpanded}
/>
</ProgressiveLoadingContainer>
);
};
46 changes: 27 additions & 19 deletions src/components/Item/VisualizationItem/DefaultPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import i18n from 'd2-i18n';

import * as pluginManager from './plugin';
import { getBaseUrl, orObject } from '../../../modules/util';
import { getGridItemDomId } from '../../ItemGrid/gridUtil';
import { itemTypeMap } from '../../../modules/itemTypes';

const pluginCredentials = d2 => {
return {
Expand All @@ -15,6 +17,12 @@ const pluginCredentials = d2 => {
class DefaultPlugin extends Component {
pluginCredentials = null;

constructor(props, context) {
super(props);

this.d2 = context.d2;
}

shouldPluginReload = prevProps => {
// TODO - fix this hack, to handle bug with multiple
// rerendering while switching between dashboards.
Expand Down Expand Up @@ -45,9 +53,7 @@ class DefaultPlugin extends Component {

const useActiveType =
currentVis.activeType !== prevVis.activeType ||
currentVis.activeType !== this.props.item.type
? true
: false;
currentVis.activeType !== this.props.item.type;

if (
useActiveType ||
Expand All @@ -58,32 +64,28 @@ class DefaultPlugin extends Component {
prevVis.activeType || this.props.item.type
);

pluginManager.load(
this.props.item,
this.pluginCredentials,
useActiveType ? currentVis.activeType : null,
this.props.itemFilter
pluginManager.loadPlugin(
itemTypeMap[this.getActiveType()].plugin,
this.props.visualization,
this.pluginCredentials
);
}
}
};

componentDidMount() {
this.pluginCredentials = pluginCredentials(this.context.d2);
this.pluginCredentials = pluginCredentials(this.d2);

if (
pluginManager.pluginIsAvailable(
this.props.item,
this.props.visualization
)
) {
pluginManager.load(
this.props.item,
this.pluginCredentials,
!this.props.editMode
? orObject(this.props.visualization).activeType
: null,
this.props.itemFilter
pluginManager.loadPlugin(
itemTypeMap[this.getActiveType()].plugin,
this.props.visualization,
this.pluginCredentials
);
}
}
Expand Down Expand Up @@ -114,17 +116,19 @@ class DefaultPlugin extends Component {
};

render() {
const { classes, item, visualization } = this.props;
const { classes, item, visualization, style } = this.props;
const pluginIsAvailable = pluginManager.pluginIsAvailable(
item,
visualization
);

return !pluginIsAvailable ? (
return pluginIsAvailable ? (
<div id={getGridItemDomId(item.id)} style={style} />
) : (
<div className={classes.textDiv}>
{i18n.t('Unable to load the plugin for this item')}
</div>
) : null;
);
}
}

Expand All @@ -133,11 +137,15 @@ DefaultPlugin.contextTypes = {
};

DefaultPlugin.propTypes = {
style: PropTypes.object,
item: PropTypes.object,
itemFilter: PropTypes.object,
visualization: PropTypes.object,
};

DefaultPlugin.defaultProps = {
style: {},
item: {},
itemFilter: {},
visualization: {},
};
Expand Down
78 changes: 64 additions & 14 deletions src/components/Item/VisualizationItem/Item.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,25 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withStyles } from '@material-ui/core/styles';
import LaunchIcon from '@material-ui/icons/Launch';
import ChartPlugin from 'data-visualizer-plugin';
import i18n from 'd2-i18n';

import * as pluginManager from './plugin';
import { getGridItemDomId } from '../../ItemGrid/gridUtil';
import { sGetVisualization } from '../../../reducers/visualizations';
import { sGetItemFilterRoot } from '../../../reducers/itemFilter';
import { acReceivedActiveVisualization } from '../../../actions/selected';
import { itemTypeMap } from '../../../modules/itemTypes';
import {
acReceivedActiveVisualization,
acReceivedVisualization,
} from '../../../actions/selected';
import { CHART, itemTypeMap } from '../../../modules/itemTypes';
import ItemHeader, { HEADER_HEIGHT } from '../ItemHeader';
import ItemFooter from './ItemFooter';
import VisualizationItemHeaderButtons from './ItemHeaderButtons';
import DefaultPlugin from './DefaultPlugin';
import { colors } from '../../../modules/colors';
import ProgressiveLoadingContainer from '../ProgressiveLoadingContainer';
import uniqueId from 'lodash/uniqueId';
import memoizeOne from '../../../modules/memoizeOne';
import { getVisualizationConfig } from './plugin';

const styles = {
icon: {
Expand All @@ -44,6 +48,7 @@ const styles = {
export class Item extends Component {
state = {
showFooter: false,
configLoaded: false,
};

constructor(props, context) {
Expand All @@ -52,6 +57,16 @@ export class Item extends Component {
this.d2 = context.d2;
}

async componentDidMount() {
this.props.onVisualizationLoaded(
await pluginManager.fetch(this.props.item, this.props.itemFilter)
);

this.setState({
configLoaded: true,
});
}

getUniqueKey = memoizeOne(() => uniqueId());

pluginCredentials = null;
Expand Down Expand Up @@ -111,6 +126,13 @@ export class Item extends Component {
);
};

getConfig = () =>
getVisualizationConfig(
this.props.visualization,
this.props.item.type,
this.getActiveType()
);

getActionButtons = () =>
pluginManager.pluginIsAvailable(
this.props.item,
Expand All @@ -135,6 +157,38 @@ export class Item extends Component {
: null;
};

getPluginComponent = () => {
const config = this.getConfig();
const style = this.getContentStyle();
const activeType = this.getActiveType();
const { item, itemFilter, classes } = this.props;

if (config) {
return activeType === CHART ? (
<ChartPlugin
d2={this.d2}
config={config}
filters={itemFilter}
style={style}
/>
) : (
<DefaultPlugin
activeType={activeType}
item={item}
style={style}
visualization={config}
itemFilter={itemFilter}
/>
);
}

return (
<div className={classes.textDiv}>
{i18n.t('No data to display')}
</div>
);
};

render() {
const { item, editMode, itemFilter } = this.props;
const { showFooter } = this.state;
Expand All @@ -146,18 +200,12 @@ export class Item extends Component {
actionButtons={this.getActionButtons()}
editMode={editMode}
/>
<ProgressiveLoadingContainer
id={getGridItemDomId(item.id)}
key={
this.getUniqueKey(
itemFilter
) /* remount the progressive loader every time itemFilter changes */
}
<div
key={this.getUniqueKey(itemFilter)}
className="dashboard-item-content"
style={this.getContentStyle()}
>
<DefaultPlugin {...this.props} />
</ProgressiveLoadingContainer>
{this.state.configLoaded && this.getPluginComponent()}
</div>
{!editMode && showFooter ? <ItemFooter item={item} /> : null}
</Fragment>
);
Expand Down Expand Up @@ -193,6 +241,8 @@ const mapStateToProps = (state, ownProps) => ({
});

const mapDispatchToProps = dispatch => ({
onVisualizationLoaded: visualization =>
dispatch(acReceivedVisualization(visualization)),
onSelectVisualization: (id, type, activeType) =>
dispatch(acReceivedActiveVisualization(id, type, activeType)),
});
Expand Down
Loading

0 comments on commit a2e3e94

Please sign in to comment.