Skip to content

Commit

Permalink
fix: chip label with stage name and correct suffix (#11)
Browse files Browse the repository at this point in the history
Implements TECH-1009

Key features
* Add stageName to the chip label in cases where multiple data elements have the same name. This is the same functionality used by ProgramDimensionsList (code is duplicated for now, but will consider refactoring when adding stageName to modal and the visualization columns)
* Style the chip such that the suffix showing #selected/conditions always shows in full, and the name-stageName part being truncated with overflow ellipsis.
* move TooltipContent->renderConditions to function getConditions in modules/conditions. This was so that the functionality could be shared with the chip label component (ChipBase)
* refactor: switch DefaultAxis to use useSelector/useDispatch instead of connect. This resulted in renderChipsSelector being moved to reducers/ui.js
  • Loading branch information
jenniferarnesen authored Mar 10, 2022
1 parent 02ed171 commit bd68007
Show file tree
Hide file tree
Showing 9 changed files with 373 additions and 280 deletions.
3 changes: 3 additions & 0 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ msgstr "Searching for \"{{searchText}}\""
msgid "No results found"
msgstr "No results found"

msgid "all"
msgstr "all"

msgid "{{count}} selected"
msgid_plural "{{count}} selected"
msgstr[0] "{{count}} selected"
Expand Down
33 changes: 13 additions & 20 deletions src/components/DndContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
acSetUiLayout,
acSetUiDraggingId,
} from '../actions/ui.js'
import { parseConditionsStringToArray } from '../modules/conditions.js'
import { sGetMetadata } from '../reducers/metadata.js'
import {
sGetUiLayout,
Expand Down Expand Up @@ -141,33 +140,26 @@ const OuterDndContext = ({ children }) => {
// is a temporary workaround
// until the backend is updated to return programStageId.dimensionId
// in analytics response.metadata.items
let name
let dimensionType
let dimension
if (metadata[id]) {
name = metadata[id].name || ''
dimensionType = metadata[id].dimensionType || null
dimension = metadata[id]
} else {
const [rawDimensionId] = id.split('.').reverse()
name = metadata[rawDimensionId]?.name || ''
dimensionType = metadata[rawDimensionId]?.dimensionType || null
dimension = metadata[rawDimensionId]
}

if (!dimension) {
return null
}

if (sourceAxis === DIMENSION_PANEL_SOURCE) {
return (
<div className={cx(styles.overlay, styles.dimensionItem)}>
<DimensionItemBase
name={name}
dimensionType={dimensionType}
dragging={true}
/>
<DimensionItemBase {...dimension} dragging={true} />
</div>
)
}

const numberOfConditions =
parseConditionsStringToArray(chipConditions.condition).length ||
(chipConditions.legendSet ? 1 : 0)

return (
<div
className={cx(
Expand All @@ -178,15 +170,16 @@ const OuterDndContext = ({ children }) => {
[chipStyles.chipEmpty]:
sourceAxis === AXIS_ID_FILTERS &&
!chipItems.length &&
!numberOfConditions,
!chipConditions.condition?.length &&
!chipConditions.legendSet,
}
)}
>
<ChipBase
dimensionName={name}
dimensionType={dimensionType}
dimension={dimension}
items={chipItems}
numberOfConditions={numberOfConditions}
conditions={chipConditions}
metadata={metadata}
/>
</div>
)
Expand Down
28 changes: 15 additions & 13 deletions src/components/Layout/Chip.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import cx from 'classnames'
import PropTypes from 'prop-types'
import React from 'react'
import { useSelector } from 'react-redux'
import { parseConditionsStringToArray } from '../../modules/conditions.js'
import { sGetLoadError } from '../../reducers/loader.js'
import { sGetMetadata } from '../../reducers/metadata.js'
import {
sGetUiItemsByDimension,
sGetUiConditionsByDimension,
} from '../../reducers/ui.js'
import DimensionMenu from '../DimensionMenu/DimensionMenu.js'
import { ChipBase } from './ChipBase.js'
import styles from './styles/Chip.module.css'
import { default as TooltipContent } from './TooltipContent.js'
Expand All @@ -25,14 +26,14 @@ const Chip = ({
isLast,
overLastDropZone,
onClick,
contextMenu,
activeIndex,
}) => {
const {
id: dimensionId,
name: dimensionName,
dimensionType,
valueType,
optionSet,
} = dimension

const {
Expand All @@ -51,13 +52,15 @@ const Chip = ({
name: dimensionName,
dimensionType,
valueType,
optionSet,
},
})
const globalLoadError = useSelector(sGetLoadError)
const metadata = useSelector(sGetMetadata)
const conditions =
useSelector((state) =>
sGetUiConditionsByDimension(state, dimension.id)
) || []
) || {}
const items =
useSelector((state) => sGetUiItemsByDimension(state, dimension.id)) ||
[]
Expand Down Expand Up @@ -97,10 +100,6 @@ const Chip = ({

const renderTooltipContent = () => <TooltipContent dimension={dimension} />

const numberOfConditions =
parseConditionsStringToArray(conditions.condition).length ||
(conditions.legendSet ? 1 : 0)

if (globalLoadError && !dimensionName) {
return null
}
Expand All @@ -118,7 +117,8 @@ const Chip = ({
[styles.chipEmpty]:
axisId === AXIS_ID_FILTERS &&
!items.length &&
!numberOfConditions,
!conditions.condition?.length &&
!conditions.legendSet,
[styles.active]: isDragging,
[styles.insertBefore]: insertPosition === BEFORE,
[styles.insertAfter]: insertPosition === AFTER,
Expand All @@ -141,16 +141,19 @@ const Chip = ({
onMouseOut={onMouseOut}
>
<ChipBase
dimensionName={dimensionName}
dimensionType={dimensionType}
numberOfConditions={numberOfConditions}
dimension={dimension}
conditions={conditions}
items={items}
metadata={metadata}
/>
</div>
)}
</Tooltip>
}
{contextMenu}
<DimensionMenu
dimensionId={dimensionId}
currentAxisId={axisId}
/>
</div>
</div>
</div>
Expand All @@ -162,7 +165,6 @@ Chip.propTypes = {
onClick: PropTypes.func.isRequired,
activeIndex: PropTypes.number,
axisId: PropTypes.string,
contextMenu: PropTypes.object,
isLast: PropTypes.bool,
overLastDropZone: PropTypes.bool,
}
Expand Down
122 changes: 90 additions & 32 deletions src/components/Layout/ChipBase.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,106 @@
import { DIMENSION_ID_ORGUNIT } from '@dhis2/analytics'
import i18n from '@dhis2/d2-i18n'
import cx from 'classnames'
import PropTypes from 'prop-types'
import React from 'react'
import {
VALUE_TYPE_BOOLEAN,
VALUE_TYPE_TRUE_ONLY,
getConditions,
} from '../../modules/conditions.js'
import { DIMENSION_TYPE_PERIOD } from '../../modules/dimensionConstants.js'
import { DimensionIcon } from '../MainSidebar/DimensionItem/DimensionIcon.js'
import styles from './styles/Chip.module.css'

const VALUE_TYPE_TRUE_ONLY_NUM_OPTIONS = 2
const VALUE_TYPE_BOOLEAN_NUM_OPTIONS = 3

// Presentational component used by dnd - do not add redux or dnd functionality

const renderChipLabelSuffix = (items, numberOfConditions) => {
let itemsLabel = ''
if (items.length) {
itemsLabel = i18n.t('{{count}} selected', {
count: items.length,
})
} else if (numberOfConditions) {
itemsLabel = i18n.t('{{count}} conditions', {
count: numberOfConditions,
defaultValue: '{{count}} condition',
defaultValue_plural: '{{count}} conditions',
})
export const ChipBase = ({ dimension, conditions, items, metadata }) => {
const { id, name, dimensionType, optionSet, valueType, stageName } =
dimension

const getChipSuffix = () => {
if (
(id === DIMENSION_ID_ORGUNIT ||
dimensionType === DIMENSION_TYPE_PERIOD) &&
!items.length
) {
return ''
}

const all = i18n.t('all')

if (!conditions.condition && !conditions.legendSet && !items.length) {
return `: ${all}`
}

const conds = getConditions({ conditions, metadata, dimension })

if (
(valueType === VALUE_TYPE_TRUE_ONLY &&
conds.length === VALUE_TYPE_TRUE_ONLY_NUM_OPTIONS) ||
(valueType === VALUE_TYPE_BOOLEAN &&
conds.length === VALUE_TYPE_BOOLEAN_NUM_OPTIONS)
) {
return `: ${all}`
}

const numberOfConditions = conds.length

if (optionSet || items.length) {
const selected = items.length || numberOfConditions
const suffix = i18n.t('{{count}} selected', {
count: selected,
defaultValue: '{{count}} selected',
defaultValue_plural: '{{count}} selected',
})
return `: ${suffix}`
} else if (numberOfConditions) {
const suffix = i18n.t('{{count}} conditions', {
count: numberOfConditions,
defaultValue: '{{count}} condition',
defaultValue_plural: '{{count}} conditions',
})
return `: ${suffix}`
}

return ''
}
return itemsLabel ? `: ${itemsLabel}` : ''
}

export const ChipBase = ({
dimensionName,
dimensionType,
items,
numberOfConditions,
}) => (
<div className={cx(styles.chipBase)}>
<div className={styles.leftIcon}>
<DimensionIcon dimensionType={dimensionType} />
return (
<div className={cx(styles.chipBase)}>
<div className={styles.leftIcon}>
<DimensionIcon dimensionType={dimensionType} />
</div>
<span className={styles.label}>
<span className={styles.primary}>{name}</span>
{stageName && (
<span className={styles.secondary}>{stageName}</span>
)}
</span>
<span className={styles.suffix}>{getChipSuffix()}</span>
</div>
<span className={styles.label}>{dimensionName}</span>
<span className={styles.label}>
{renderChipLabelSuffix(items, numberOfConditions)}
</span>
</div>
)
)
}

ChipBase.propTypes = {
dimensionName: PropTypes.string,
dimensionType: PropTypes.string,
conditions: PropTypes.object,
dimension: PropTypes.shape({
dimensionType: PropTypes.string,
id: PropTypes.string,
name: PropTypes.string,
optionSet: PropTypes.string,
stageName: PropTypes.string,
valueType: PropTypes.string,
}),
items: PropTypes.array,
numberOfConditions: PropTypes.number,
metadata: PropTypes.object,
}

ChipBase.defaultProps = {
conditions: {},
items: [],
metadata: {},
}
Loading

0 comments on commit bd68007

Please sign in to comment.