Skip to content

Commit

Permalink
feat: responsive control bar and title bar [DHIS2-10138] (#1400)
Browse files Browse the repository at this point in the history
Partially implements DHIS2-10138
This PR addresses responsive changes in the view control bar and view title bar. Includes

* spacing around chips, dashboard search field, against edge, chip scroll bars
* remove action buttons (Edit, Share, Add filter)
* remove "+" button (new button)
* Width <=480 and collapsed control bar

Implementation notes:

* decoupled the View control bar from Edit control bar, since Edit mode doesn't need all the dnd functionality
* For js part of the implementation, a new react hook was added that listens to window resizes. The components that need to make responsive changes can then use that hook.
* The ChevronUp and ChevronDown icons are currently copied into this repo, but when we upgrade to ui6, then we'll switch to using the icons from there.
* ControlBar was simplified since it is no longer used for the Edit bar (no editMode needed)
* Where it made sense, I switched from inline css to css classes
* The "Show more/less" button on the control bar is now an icon instead of text. Accessibility changes were made to adapt to this, like using a element and adding aria-label. Also added a tooltip, suggested by Joe.
  • Loading branch information
jenniferarnesen authored Jan 11, 2021
1 parent ea0a896 commit 7b6af12
Show file tree
Hide file tree
Showing 36 changed files with 966 additions and 747 deletions.
17 changes: 10 additions & 7 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: 2020-12-10T12:16:21.390Z\n"
"PO-Revision-Date: 2020-12-10T12:16:21.391Z\n"
"POT-Creation-Date: 2021-01-05T09:32:21.165Z\n"
"PO-Revision-Date: 2021-01-05T09:32:21.165Z\n"

msgid "Untitled dashboard"
msgstr ""
Expand All @@ -29,13 +29,13 @@ msgid ""
"this dashboard?"
msgstr ""

msgid "Exit Print preview"
msgid "Save changes"
msgstr ""

msgid "Print preview"
msgid "Exit Print preview"
msgstr ""

msgid "Save changes"
msgid "Print preview"
msgstr ""

msgid "Translate"
Expand All @@ -50,10 +50,10 @@ msgstr ""
msgid "Search for a dashboard"
msgstr ""

msgid "Show more"
msgid "Show fewer dashboards"
msgstr ""

msgid "Show less"
msgid "Show more dashboards"
msgstr ""

msgid "No dashboards found. Use the + button to create a new dashboard."
Expand Down Expand Up @@ -174,6 +174,9 @@ msgstr ""
msgid "Show fewer"
msgstr ""

msgid "Show more"
msgstr ""

msgid "Insert"
msgstr ""

Expand Down
7 changes: 6 additions & 1 deletion src/AppWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Provider as ReduxProvider } from 'react-redux'
import { D2Shim } from '@dhis2/app-runtime-adapter-d2'
import { useDataEngine } from '@dhis2/app-runtime'

import WindowDimensionsProvider from './components/WindowDimensionsProvider'
import App from './components/App'
import configureStore from './configureStore'

Expand Down Expand Up @@ -46,7 +47,11 @@ const AppWrapper = () => {
// TODO: Handle errors in d2 initialization
return null
}
return <App d2={d2} dataEngine={dataEngine} />
return (
<WindowDimensionsProvider>
<App d2={d2} dataEngine={dataEngine} />
</WindowDimensionsProvider>
)
}}
</D2Shim>
</MuiThemeProvider>
Expand Down
30 changes: 8 additions & 22 deletions src/components/ControlBar/ControlBar.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import { colors } from '@dhis2/ui'

import cx from 'classnames'
import classes from './styles/ControlBar.module.css'

export const DRAG_HANDLE_HEIGHT = 7
Expand Down Expand Up @@ -48,34 +47,26 @@ class ControlBar extends React.Component {
}
}

renderDragHandle() {
return typeof this.props.onChangeHeight === 'function' ? (
renderDragHandle = () =>
typeof this.props.onChangeHeight === 'function' && (
<div
data-testid="controlbar-drag-handle"
className={classes.draghandle}
style={{ height: DRAG_HANDLE_HEIGHT }}
onMouseDown={this.onStartDrag}
/>
) : null
}
)

render() {
const height = Math.max(this.props.height, 0) + DRAG_HANDLE_HEIGHT

const rootStyle = Object.assign(
{
height,
backgroundColor: this.props.editMode
? colors.yellow050
: colors.white,
paddingBottom: DRAG_HANDLE_HEIGHT,
},
// Disable animations while dragging
this.state.dragging ? { transition: 'none' } : {}
const rootClass = cx(
classes.root,
this.state.dragging && classes.dragging
)

return (
<div style={rootStyle} className={classes.root}>
<div style={{ height }} className={rootClass}>
<div className={classes.content}>{this.props.children}</div>
{this.renderDragHandle()}
</div>
Expand All @@ -89,11 +80,6 @@ ControlBar.propTypes = {
*/
children: PropTypes.node.isRequired,

/**
* If true, the background color of the control bar changes to indicate that edit mode is active.
*/
editMode: PropTypes.bool.isRequired,

/**
* Callback function that is called when the control bar is resized.
* The callback receives one argument: The new height in pixels.
Expand Down
131 changes: 89 additions & 42 deletions src/components/ControlBar/DashboardsBar.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import React, { useState, useEffect } from 'react'
import React, { useState, useEffect, createRef } from 'react'
import { connect } from 'react-redux'
import { Link, withRouter } from 'react-router-dom'
import cx from 'classnames'

import arraySort from 'd2-utilizr/lib/arraySort'
import PropTypes from 'prop-types'

import ControlBar from './ControlBar'
import ControlBar, { DRAG_HANDLE_HEIGHT } from './ControlBar'
import Chip from './DashboardItemChip'
import AddCircleIcon from '../../icons/AddCircle'
import Filter from './Filter'
Expand All @@ -18,18 +17,21 @@ import {
getControlBarHeight,
getNumRowsFromHeight,
} from './controlBarDimensions'
import { useWindowDimensions } from '../WindowDimensionsProvider'
import { sGetDashboardsFilter } from '../../reducers/dashboardsFilter'
import { sGetControlBarUserRows } from '../../reducers/controlBar'
import { sGetAllDashboards } from '../../reducers/dashboards'
import { sGetSelectedId } from '../../reducers/selected'
import { acSetControlBarUserRows } from '../../actions/controlBar'
import { apiPostControlBarRows } from '../../api/controlBar'

import isSmallScreen from '../../modules/isSmallScreen'

import classes from './styles/DashboardsBar.module.css'

export const MAX_ROW_COUNT = 10

export const DashboardsBar = ({
const DashboardsBar = ({
userRows,
onChangeHeight,
history,
Expand All @@ -38,6 +40,8 @@ export const DashboardsBar = ({
filterText,
}) => {
const [rows, setRows] = useState(userRows)
const { width } = useWindowDimensions()
const ref = createRef()

useEffect(() => {
setRows(userRows)
Expand All @@ -58,12 +62,20 @@ export const DashboardsBar = ({

const onEndDrag = () => apiPostControlBarRows(rows)

const scrollToTop = () => {
if (isMaxHeight()) {
ref.current.scroll(0, 0)
}
}

const toggleMaxHeight = () => {
const newRows = isMaxHeight() ? userRows : MAX_ROW_COUNT
scrollToTop()
setRows(newRows)
}

const cancelMaxHeight = () => {
scrollToTop()
setRows(userRows)
}

Expand All @@ -89,50 +101,85 @@ export const DashboardsBar = ({
]
}

const overflowYClass = isMaxHeight()
? classes.overflowYAuto
: classes.overflowYHidden
const containerClass = cx(
classes.container,
isMaxHeight() ? classes.expanded : classes.collapsed
)

const viewableRows =
isSmallScreen(width) && !isMaxHeight() ? MIN_ROW_COUNT : rows

const rowHeightProp = {
height: getRowsHeight(viewableRows) + FIRST_ROW_PADDING_HEIGHT,
}

const getDashboardChips = () => {
const chips = getFilteredDashboards().map(dashboard => (
<Chip
key={dashboard.id}
label={dashboard.displayName}
starred={dashboard.starred}
dashboardId={dashboard.id}
selected={dashboard.id === selectedId}
onClick={cancelMaxHeight}
/>
))
if (isSmallScreen(width)) {
const chipContainerClasses = cx(
classes.chipContainer,
isMaxHeight() ? classes.expanded : classes.collapsed
)
return (
<div className={chipContainerClasses} style={rowHeightProp}>
{chips}
</div>
)
} else {
return chips
}
}

return (
<ControlBar
height={getControlBarHeight(rows)}
onChangeHeight={adjustHeight}
onEndDrag={onEndDrag}
editMode={false}
>
<>
<ControlBar
height={getControlBarHeight(viewableRows)}
onChangeHeight={!isSmallScreen(width) ? adjustHeight : null}
onEndDrag={onEndDrag}
>
<div className={containerClass} ref={ref} style={rowHeightProp}>
<div className={classes.controls}>
<Link
className={classes.newLink}
to={'/new'}
data-test="link-new-dashboard"
>
<AddCircleIcon />
</Link>
<Filter
onKeypressEnter={onSelectDashboard}
onToggleMaxHeight={toggleMaxHeight}
isMaxHeight={isMaxHeight()}
/>
</div>
{getDashboardChips()}
</div>
<ShowMoreButton
onClick={toggleMaxHeight}
isMaxHeight={isMaxHeight()}
disabled={userRows === MAX_ROW_COUNT}
/>
</ControlBar>
<div
className={cx(classes.container, overflowYClass)}
style={{
height: getRowsHeight(rows) + FIRST_ROW_PADDING_HEIGHT,
marginTop:
getControlBarHeight(
isSmallScreen(width) && !isMaxHeight()
? MIN_ROW_COUNT
: userRows
) + DRAG_HANDLE_HEIGHT,
}}
>
<div className={classes.leftControls}>
<Link
className={classes.newLink}
to={'/new'}
data-test="link-new-dashboard"
>
<AddCircleIcon />
</Link>
<Filter onKeypressEnter={onSelectDashboard} />
</div>
{getFilteredDashboards().map(dashboard => (
<Chip
key={dashboard.id}
label={dashboard.displayName}
starred={dashboard.starred}
dashboardId={dashboard.id}
selected={dashboard.id === selectedId}
onClick={cancelMaxHeight}
/>
))}
</div>
<ShowMoreButton
onClick={toggleMaxHeight}
isMaxHeight={isMaxHeight()}
disabled={userRows === MAX_ROW_COUNT}
/>
</ControlBar>
</>
)
}

Expand Down
Loading

0 comments on commit 7b6af12

Please sign in to comment.