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: changing View As on one item was incorrectly causing other items to also change view [DHIS2-9590] #1111

Merged
merged 12 commits into from
Oct 7, 2020
Merged
Show file tree
Hide file tree
Changes from 10 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
28 changes: 23 additions & 5 deletions src/actions/selected.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
SET_SELECTED_ID,
SET_SELECTED_ISLOADING,
SET_SELECTED_SHOWDESCRIPTION,
SET_SELECTED_ITEM_ACTIVE_TYPE,
CLEAR_SELECTED_ITEM_ACTIVE_TYPES,
sGetSelectedIsLoading,
sGetSelectedId,
} from '../reducers/selected'
Expand All @@ -16,7 +18,7 @@ import { acSetDashboardItems, acAppendDashboards } from './dashboards'
import { acClearItemFilters } from './itemFilters'
import { tGetMessages } from '../components/Item/MessagesItem/actions'
import { acReceivedSnackbarMessage, acCloseSnackbar } from './snackbar'
import { acAddVisualization } from './visualizations'
import { acAddVisualization, acClearVisualizations } from './visualizations'

import { apiFetchDashboard } from '../api/dashboards'
import { storePreferredDashboardId } from '../api/localStorage'
Expand Down Expand Up @@ -55,6 +57,20 @@ export const acSetSelectedShowDescription = value => ({
value,
})

export const acSetSelectedItemActiveType = (id, activeType) => {
const action = {
type: SET_SELECTED_ITEM_ACTIVE_TYPE,
id,
activeType,
}

return action
}

export const acClearSelectedItemActiveTypes = () => ({
type: CLEAR_SELECTED_ITEM_ACTIVE_TYPES,
})

// thunks
export const tSetSelectedDashboardById = id => async (dispatch, getState) => {
dispatch(acSetSelectedIsLoading(true))
Expand Down Expand Up @@ -84,6 +100,12 @@ export const tSetSelectedDashboardById = id => async (dispatch, getState) => {

storePreferredDashboardId(sGetUserUsername(getState()), id)

if (id !== sGetSelectedId(getState())) {
dispatch(acClearItemFilters())
dispatch(acClearVisualizations())
dispatch(acClearSelectedItemActiveTypes())
}

customDashboard.dashboardItems.forEach(item => {
switch (item.type) {
case REPORT_TABLE:
Expand All @@ -101,10 +123,6 @@ export const tSetSelectedDashboardById = id => async (dispatch, getState) => {
}
})

if (id !== sGetSelectedId(getState())) {
dispatch(acClearItemFilters())
}

dispatch(acSetSelectedId(id))

dispatch(acSetSelectedIsLoading(false))
Expand Down
14 changes: 4 additions & 10 deletions src/actions/visualizations.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import {
ADD_VISUALIZATION,
SET_ACTIVE_VISUALIZATION_TYPE,
CLEAR_VISUALIZATIONS,
} from '../reducers/visualizations'

export const acAddVisualization = value => ({
type: ADD_VISUALIZATION,
value,
})

export const acSetActiveVisualizationType = (id, activeType) => {
const action = {
type: SET_ACTIVE_VISUALIZATION_TYPE,
id,
activeType,
}

return action
}
export const acClearVisualizations = () => ({
type: CLEAR_VISUALIZATIONS,
})
27 changes: 6 additions & 21 deletions src/components/Item/VisualizationItem/DefaultPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,15 @@ class DefaultPlugin extends Component {
) &&
this.shouldPluginReload(prevProps)
) {
const prevVis = orObject(prevProps.visualization)
const currentVis = this.props.visualization

const useActiveType =
currentVis.activeType !== prevVis.activeType ||
currentVis.activeType !== this.props.item.type

if (
useActiveType ||
this.props.activeType !== prevProps.activeType ||
this.props.itemFilters !== prevProps.itemFilters
) {
pluginManager.unmount(
this.props.item,
prevVis.activeType || this.props.item.type
)
pluginManager.unmount(this.props.item, prevProps.activeType)

pluginManager.load(this.props.item, this.props.visualization, {
credentials: this.pluginCredentials,
activeType: useActiveType ? currentVis.activeType : null,
activeType: this.props.activeType,
})
}
}
Expand All @@ -83,9 +73,7 @@ class DefaultPlugin extends Component {
) {
pluginManager.load(this.props.item, this.props.visualization, {
credentials: this.pluginCredentials,
activeType: this.props.useActiveType
? this.getActiveType()
: null,
activeType: this.props.activeType,
options: this.props.options,
})
}
Expand All @@ -102,13 +90,10 @@ class DefaultPlugin extends Component {
this.props.visualization
)
) {
pluginManager.unmount(this.props.item, this.getActiveType())
pluginManager.unmount(this.props.item, this.props.activeType)
}
}

getActiveType = () =>
this.props.visualization.activeType || this.props.item.type

render() {
const { classes, item, visualization, style } = this.props
const pluginIsAvailable = pluginManager.pluginIsAvailable(
Expand All @@ -131,12 +116,12 @@ DefaultPlugin.contextTypes = {
}

DefaultPlugin.propTypes = {
activeType: PropTypes.string,
classes: PropTypes.object,
item: PropTypes.object,
itemFilters: PropTypes.object,
options: PropTypes.object,
style: PropTypes.object,
useActiveType: PropTypes.bool,
visualization: PropTypes.object,
}

Expand Down
44 changes: 25 additions & 19 deletions src/components/Item/VisualizationItem/Item.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ import LoadingMask from './LoadingMask'

import * as pluginManager from './plugin'
import { sGetVisualization } from '../../../reducers/visualizations'
import { sGetSelectedItemActiveType } from '../../../reducers/selected'
import { sGetIsEditing } from '../../../reducers/editDashboard'
import {
sGetItemFiltersRoot,
DEFAULT_STATE_ITEM_FILTERS,
} from '../../../reducers/itemFilters'
import {
acAddVisualization,
acSetActiveVisualizationType,
} from '../../../actions/visualizations'
import { acAddVisualization } from '../../../actions/visualizations'
import { acSetSelectedItemActiveType } from '../../../actions/selected'
import {
VISUALIZATION,
MAP,
Expand Down Expand Up @@ -64,7 +64,7 @@ export class Item extends Component {
}

async componentDidMount() {
this.props.onVisualizationLoaded(
this.props.updateVisualization(
// TODO do not call fetch on the pluginManager, do it here as the manager will eventually be removed...
await pluginManager.fetch(this.props.item)
)
Expand Down Expand Up @@ -155,7 +155,7 @@ export class Item extends Component {

const props = {
...this.props,
useActiveType: !isEditMode(this.props.dashboardMode),
activeType,
visualization,
classes,
style: this.memoizedGetContentStyle(
Expand Down Expand Up @@ -253,13 +253,17 @@ export class Item extends Component {
)
}

onSelectActiveType = type => {
selectActiveType = type => {
type !== this.getActiveType() &&
this.props.onSelectActiveType(this.props.visualization.id, type)
this.props.selectActiveType(this.props.item.id, type)
}

getActiveType = () =>
this.props.visualization.activeType || this.props.item.type
getActiveType = () => {
if (this.props.isEditing) {
return this.props.item.type
}
return this.props.activeType || this.props.item.type
}

pluginIsAvailable = () =>
pluginManager.pluginIsAvailable(
Expand All @@ -283,7 +287,7 @@ export class Item extends Component {
<ItemHeaderButtons
item={item}
visualization={this.props.visualization}
onSelectActiveType={this.onSelectActiveType}
onSelectActiveType={this.selectActiveType}
onToggleFooter={this.onToggleFooter}
d2={this.d2}
activeType={this.getActiveType()}
Expand Down Expand Up @@ -323,13 +327,15 @@ Item.contextTypes = {
}

Item.propTypes = {
activeType: PropTypes.string,
dashboardMode: PropTypes.string,
isEditing: PropTypes.bool,
item: PropTypes.object,
itemFilters: PropTypes.object,
selectActiveType: PropTypes.func,
updateVisualization: PropTypes.func,
visualization: PropTypes.object,
onSelectActiveType: PropTypes.func,
onToggleItemExpanded: PropTypes.func,
onVisualizationLoaded: PropTypes.func,
}

Item.defaultProps = {
Expand All @@ -344,6 +350,8 @@ const mapStateToProps = (state, ownProps) => {
: DEFAULT_STATE_ITEM_FILTERS

return {
activeType: sGetSelectedItemActiveType(state, ownProps.item?.id),
isEditing: sGetIsEditing(state),
itemFilters,
visualization: sGetVisualization(
state,
Expand All @@ -352,11 +360,9 @@ const mapStateToProps = (state, ownProps) => {
}
}

const mapDispatchToProps = dispatch => ({
onVisualizationLoaded: visualization =>
dispatch(acAddVisualization(visualization)),
onSelectActiveType: (id, type, activeType) =>
dispatch(acSetActiveVisualizationType(id, type, activeType)),
})
const mapDispatchToProps = {
selectActiveType: acSetSelectedItemActiveType,
updateVisualization: acAddVisualization,
}

export default connect(mapStateToProps, mapDispatchToProps)(Item)
102 changes: 73 additions & 29 deletions src/reducers/__tests__/selected.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,98 @@ import reducer, {
SET_SELECTED_ID,
SET_SELECTED_ISLOADING,
SET_SELECTED_SHOWDESCRIPTION,
SET_SELECTED_ITEM_ACTIVE_TYPE,
CLEAR_SELECTED_ITEM_ACTIVE_TYPES,
DEFAULT_STATE_SELECTED_ITEM_ACTIVE_TYPES,
} from '../selected'

describe('selected dashboard reducer', () => {
const defaultState = {
id: null,
isLoading: false,
showDescription: false,
itemActiveTypes: {},
}

describe('reducer', () => {
it('should set the selected dashboard id', () => {
const id = 'my favorite dashboard'
const expectedState = Object.assign({}, defaultState, { id })
it('sets the selected dashboard id', () => {
const id = 'my favorite dashboard'
const expectedState = Object.assign({}, defaultState, { id })

const actualState = reducer(defaultState, {
type: SET_SELECTED_ID,
value: id,
})
const actualState = reducer(defaultState, {
type: SET_SELECTED_ID,
value: id,
})

expect(actualState).toEqual(expectedState)
})

expect(actualState).toEqual(expectedState)
it('sets the selected dashboard isLoading state', () => {
const isLoading = true
const expectedState = Object.assign({}, defaultState, {
isLoading,
})

it('should set the selected dashboard isLoading state', () => {
const isLoading = true
const expectedState = Object.assign({}, defaultState, {
isLoading,
})
const actualState = reducer(defaultState, {
type: SET_SELECTED_ISLOADING,
value: isLoading,
})

const actualState = reducer(defaultState, {
type: SET_SELECTED_ISLOADING,
value: isLoading,
})
expect(actualState).toEqual(expectedState)
})

expect(actualState).toEqual(expectedState)
it('sets the selected dashboard showDescription state', () => {
const showDescription = true
const expectedState = Object.assign({}, defaultState, {
showDescription,
})

it('should set the selected dashboard showDescription state', () => {
const showDescription = true
const expectedState = Object.assign({}, defaultState, {
showDescription,
})
const actualState = reducer(defaultState, {
type: SET_SELECTED_SHOWDESCRIPTION,
value: showDescription,
})

const actualState = reducer(defaultState, {
type: SET_SELECTED_SHOWDESCRIPTION,
value: showDescription,
})
expect(actualState).toEqual(expectedState)
})

it('sets an item active Type', () => {
const action = {
type: SET_SELECTED_ITEM_ACTIVE_TYPE,
id: 'rainbowDash',
activeType: 'PONY',
}

expect(actualState).toEqual(expectedState)
const expectedState = Object.assign({}, defaultState, {
itemActiveTypes: { rainbowDash: 'PONY' },
})

const actualState = reducer(defaultState, action)

expect(actualState).toEqual(expectedState)
})

it('clears the item active types', () => {
const action = {
type: SET_SELECTED_ITEM_ACTIVE_TYPE,
id: 'rainbowDash',
activeType: 'PONY',
}

const expectedState = Object.assign({}, defaultState, {
itemActiveTypes: { rainbowDash: 'PONY' },
})

const actualState = reducer(defaultState, action)

expect(actualState).toEqual(expectedState)

const updatedState = reducer(actualState, {
type: CLEAR_SELECTED_ITEM_ACTIVE_TYPES,
})

expect(updatedState).toEqual(
Object.assign({}, defaultState, {
itemActiveTypes: DEFAULT_STATE_SELECTED_ITEM_ACTIVE_TYPES,
})
)
})
})
Loading