diff --git a/src/assets/DimensionItemIcons/GenericIcon.js b/src/assets/DimensionItemIcons/GenericIcon.js new file mode 100644 index 000000000..4f3aea414 --- /dev/null +++ b/src/assets/DimensionItemIcons/GenericIcon.js @@ -0,0 +1,15 @@ +import React from 'react' + +export default ( + + + +) diff --git a/src/components/DataDimension/DataDimension.js b/src/components/DataDimension/DataDimension.js index 0410398ed..42009c895 100644 --- a/src/components/DataDimension/DataDimension.js +++ b/src/components/DataDimension/DataDimension.js @@ -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' diff --git a/src/components/DynamicDimension/DynamicDimension.js b/src/components/DynamicDimension/DynamicDimension.js index fb4c16dda..f7036174c 100644 --- a/src/components/DynamicDimension/DynamicDimension.js +++ b/src/components/DynamicDimension/DynamicDimension.js @@ -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 ( - - ) - } - - 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 ( - - {filterZone()} - - ) - } + return ( + + ) } 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 diff --git a/src/components/DynamicDimension/ItemSelector.js b/src/components/DynamicDimension/ItemSelector.js new file mode 100644 index 000000000..9e7f8bc85 --- /dev/null +++ b/src/components/DynamicDimension/ItemSelector.js @@ -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 = () => ( + <> +

{i18n.t('No items selected')}

+ + + ) + + return ( + { + 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 => ( + + ))} + + ) +} + +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 diff --git a/src/components/PeriodDimension/PeriodSelector.js b/src/components/PeriodDimension/PeriodSelector.js index 01e3cb09b..a42154c5c 100644 --- a/src/components/PeriodDimension/PeriodSelector.js +++ b/src/components/PeriodDimension/PeriodSelector.js @@ -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) @@ -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) diff --git a/src/components/PeriodDimension/styles/PeriodSelector.style.js b/src/components/styles/DimensionSelector.style.js similarity index 100% rename from src/components/PeriodDimension/styles/PeriodSelector.style.js rename to src/components/styles/DimensionSelector.style.js diff --git a/src/modules/dimensionSelectorHelper.js b/src/modules/dimensionSelectorHelper.js new file mode 100644 index 000000000..c127b3175 --- /dev/null +++ b/src/modules/dimensionSelectorHelper.js @@ -0,0 +1,5 @@ +export const TRANSFER_OPTIONS_WIDTH = '420px' + +export const TRANSFER_SELECTED_WIDTH = '298px' + +export const TRANSFER_HEIGHT = '512px'