Skip to content

Commit

Permalink
feat: Dynamic Dimension using ui-core components (DHIS2-8831) (#469)
Browse files Browse the repository at this point in the history
Material UI replaced by ui-core

BREAKING CHANGE: onDeselect, onReorder deprecated. d2 renamed to context. dialogId renamed to dimensionId.
  • Loading branch information
martinkrulltott authored Jun 3, 2020
1 parent e35973f commit 1d3ff34
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 124 deletions.
15 changes: 15 additions & 0 deletions src/assets/DimensionItemIcons/GenericIcon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react'

export default (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 16 16"
>
<path
fill="#6E7A8A"
d="M11,5 L11,11 L5,11 L5,5 L11,5 Z M10,6 L6,6 L6,10 L10,10 L10,6 Z"
/>
</svg>
)
2 changes: 1 addition & 1 deletion src/components/DataDimension/DataDimension.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
import debounce from 'lodash/debounce'
import isEqual from 'lodash/isEqual'

import ItemSelector from '../ItemSelector/ItemSelector'
import ItemSelector from '../ItemSelector/ItemSelector' //TODO: Deprecate ItemSelector once this dependency is removed?
import DataTypes from './DataTypesSelector'
import Groups from './Groups'
import FilterField from '../FilterField'
Expand Down
160 changes: 41 additions & 119 deletions src/components/DynamicDimension/DynamicDimension.js
Original file line number Diff line number Diff line change
@@ -1,139 +1,61 @@
import React, { Component } from 'react'
import React, { useEffect, useState } from 'react'
import PropTypes from 'prop-types'

import ItemSelector from '../ItemSelector/ItemSelector'
import FilterField from '../FilterField'
import ItemSelector from './ItemSelector'
import { apiFetchItemsByDimension } from '../../api/dimensions'

export class DynamicDimension extends Component {
state = {
filterText: '',
nextPage: null,
items: [],
unselectedIds: [],
selectedIds: [],
}

componentDidMount = async () => {
const items = await apiFetchItemsByDimension(
this.props.d2,
this.props.dialogId
)

this.setState({ items })
}

onClearFilter = () => {
this.setState({ filterText: '' })
}

onFilterTextChange = filterText => {
const filteredItems = this.state.items.map(
item =>
item.name.toLowerCase().includes(filterText.toLowerCase()) &&
item.id
)

this.setState({
filterText,
unselectedIds: filteredItems,
})
}

selectItems = selectedIds => {
const unselectedIds = this.state.unselectedIds.filter(
id => !selectedIds.includes(id)
)
this.setState({ unselectedIds })

const itemsToAdd = this.state.items.filter(di =>
selectedIds.includes(di.id)
)

this.props.onSelect({
dimensionId: this.props.dialogId,
items: [
...this.props.selectedItems.filter(
item => !selectedIds.includes(item.id)
),
...itemsToAdd,
],
})
}

deselectItems = ids => {
const unselectedIds = [
...new Set([...this.state.unselectedIds, ...ids]),
]
this.setState({ unselectedIds })

this.props.onDeselect({
dimensionId: this.props.dialogId,
itemIdsToRemove: ids,
export const DynamicDimension = ({
context,
dimensionId,
onSelect,
selectedItems,
rightFooter,
}) => {
const [items, setItems] = useState([])

useEffect(() => {
if (!items || !items.length) {
getItems()
}
}, [])

const getItems = async () =>
setItems(await apiFetchItemsByDimension(context, dimensionId))
// TODO: This needs to be refactored to use a loading spinner once Transfer supports it: https://jira.dhis2.org/browse/TECH-379

const selectItems = items => {
const formattedItems = items.map(item => ({
id: item.value,
name: item.label,
}))
onSelect({
dimensionId: dimensionId,
items: formattedItems,
})
}

reorderItems = itemIds =>
this.props.onReorder({
dimensionId: this.props.dialogId,
itemIds,
})

getUnselectedItems = () =>
this.state.items.filter(
item => !this.props.selectedItems.find(i => i.id === item.id)
)

render = () => {
const filterZone = () => {
return (
<FilterField
text={this.state.filterText}
onFilterTextChange={this.onFilterTextChange}
onClearFilter={this.onClearFilter}
/>
)
}

const unselected = {
items: this.getUnselectedItems(),
onSelect: this.selectItems,
filterText: this.state.filterText,
}

const selected = {
items: this.props.selectedItems,
dialogId: this.props.dialogId,
onDeselect: this.deselectItems,
onReorder: this.reorderItems,
}

return (
<ItemSelector
itemClassName="dynamic-dimension"
unselected={unselected}
selected={selected}
>
{filterZone()}
</ItemSelector>
)
}
return (
<ItemSelector
onSelect={selectItems}
allItems={items}
initialSelectedItems={selectedItems}
rightFooter={rightFooter}
// TODO: Pass in a func prop to fetch items, instead of fetching them on this level, to enable the loading spinner?
/>
)
}

DynamicDimension.propTypes = {
d2: PropTypes.object.isRequired,
dialogId: PropTypes.string.isRequired,
context: PropTypes.object.isRequired,
dimensionId: PropTypes.string.isRequired,
selectedItems: PropTypes.array.isRequired,
onDeselect: PropTypes.func.isRequired,
onReorder: PropTypes.func.isRequired,
onSelect: PropTypes.func.isRequired,
rightFooter: PropTypes.node,
}

DynamicDimension.defaultProps = {
selectedItems: [],
onSelect: Function.prototype,
onDeselect: Function.prototype,
onReorder: Function.prototype,
}

export default DynamicDimension
79 changes: 79 additions & 0 deletions src/components/DynamicDimension/ItemSelector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import { Transfer } from '@dhis2/ui-core'
import i18n from '@dhis2/d2-i18n'

import styles from '../styles/DimensionSelector.style'
import { TransferOption } from '../TransferOption'
import GenericIcon from '../../assets/DimensionItemIcons/GenericIcon'
import {
TRANSFER_HEIGHT,
TRANSFER_OPTIONS_WIDTH,
TRANSFER_SELECTED_WIDTH,
} from '../../modules/dimensionSelectorHelper'

const ItemSelector = ({
allItems,
onSelect,
initialSelectedItems,
leftHeader,
rightFooter,
}) => {
const [selectedItems, setSelectedItems] = useState(
initialSelectedItems.map(item => ({
label: item.name,
value: item.id,
key: item.id,
}))
)

const renderEmptySelection = () => (
<>
<p className="emptySelection">{i18n.t('No items selected')}</p>
<style jsx>{styles}</style>
</>
)

return (
<Transfer
onChange={({ selected }) => {
setSelectedItems(selected)
onSelect(selected)
}}
selected={selectedItems}
leftHeader={leftHeader}
filterable
enableOrderChange
height={TRANSFER_HEIGHT}
optionsWidth={TRANSFER_OPTIONS_WIDTH}
selectedWidth={TRANSFER_SELECTED_WIDTH}
selectedEmptyComponent={renderEmptySelection()}
rightFooter={rightFooter}
// TODO: Add a filter placeholer once the Transfer component supports this (https://github.com/dhis2/ui/issues/131)
// TODO: Add rightHeader "Selected Periods" once the Transfer component supports this (https://github.com/dhis2/ui-core/issues/885)
>
{allItems.map(item => (
<TransferOption
label={item.name}
value={item.id}
key={item.id}
icon={GenericIcon}
/>
))}
</Transfer>
)
}

ItemSelector.propTypes = {
allItems: PropTypes.arrayOf(PropTypes.object).isRequired,
onSelect: PropTypes.func.isRequired,
initialSelectedItems: PropTypes.arrayOf(PropTypes.object),
leftHeader: PropTypes.node,
rightFooter: PropTypes.node,
}

ItemSelector.defaultProps = {
initialSelectedItems: [],
}

export default ItemSelector
13 changes: 9 additions & 4 deletions src/components/PeriodDimension/PeriodSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ import FixedPeriodFilter from './FixedPeriodFilter'
import RelativePeriodFilter from './RelativePeriodFilter'
import { MONTHS, getRelativePeriodsOptionsById } from './utils/relativePeriods'
import { MONTHLY, getFixedPeriodsOptionsById } from './utils/fixedPeriods'
import styles from './styles/PeriodSelector.style'
import styles from '../styles/DimensionSelector.style'
import { TransferOption } from '../TransferOption'
import PeriodIcon from '../../assets/DimensionItemIcons/PeriodIcon'
import {
TRANSFER_HEIGHT,
TRANSFER_OPTIONS_WIDTH,
TRANSFER_SELECTED_WIDTH,
} from '../../modules/dimensionSelectorHelper'

const defaultRelativePeriodType = getRelativePeriodsOptionsById(MONTHS)
const defaultFixedPeriodType = getFixedPeriodsOptionsById(MONTHLY)
Expand Down Expand Up @@ -148,9 +153,9 @@ class PeriodSelector extends Component {
selected={this.state.selectedPeriods}
leftHeader={this.renderHeader()}
enableOrderChange
height="512px"
optionsWidth="420px"
selectedWidth="298px"
height={TRANSFER_HEIGHT}
optionsWidth={TRANSFER_OPTIONS_WIDTH}
selectedWidth={TRANSFER_SELECTED_WIDTH}
selectedEmptyComponent={this.renderEmptySelection()}
rightFooter={this.props.rightFooter}
// TODO: Add rightHeader "Selected Periods" once the Transfer component supports this (https://github.com/dhis2/ui-core/issues/885)
Expand Down
5 changes: 5 additions & 0 deletions src/modules/dimensionSelectorHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const TRANSFER_OPTIONS_WIDTH = '420px'

export const TRANSFER_SELECTED_WIDTH = '298px'

export const TRANSFER_HEIGHT = '512px'

0 comments on commit 1d3ff34

Please sign in to comment.