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

feat: add fullscreen option to visualization items [DHIS2-9879] #1358

Merged
merged 31 commits into from
Jan 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3267d92
chore: refactor Visualization item to separate Item from Plugin
jenniferarnesen Dec 1, 2020
cf6dbb3
feat: fullscreen start
jenniferarnesen Dec 7, 2020
a23479b
feat: some cleanup
jenniferarnesen Dec 7, 2020
4e08293
test: update snapshots
jenniferarnesen Dec 7, 2020
e6d5ea8
fix: use ref instead of css selectors
jenniferarnesen Dec 8, 2020
282d153
fix: do things the react way
jenniferarnesen Dec 8, 2020
ed7540a
fix: add missing translations
jenniferarnesen Dec 8, 2020
32f5b26
fix: cleanup
jenniferarnesen Dec 8, 2020
43233cc
fix: clean up event listener
jenniferarnesen Dec 8, 2020
93bdea7
fix: fullscreen icon - temporary for demo only
jenniferarnesen Dec 8, 2020
8b0c1d6
test: add test for fullscreen button
jenniferarnesen Dec 8, 2020
2734cef
fix: remove surrounding div
jenniferarnesen Dec 8, 2020
5d3d6e3
fix: consolelog
jenniferarnesen Dec 8, 2020
ce13772
fix: remove unused file
jenniferarnesen Dec 8, 2020
9ddad11
fix: update snapshot
jenniferarnesen Dec 8, 2020
1c3ef15
fix: use class var to hold element selector
jenniferarnesen Dec 8, 2020
b9405ea
feat: exit fs icon
jenniferarnesen Dec 8, 2020
238547e
fix: test
jenniferarnesen Dec 8, 2020
13af174
Merge branch 'master' into feat/fullscreen-latest
jenniferarnesen Dec 10, 2020
e01b83f
fix: dom el doesnt match in print mode
jenniferarnesen Dec 10, 2020
470e625
Merge branch 'master' into feat/fullscreen-latest
jenniferarnesen Dec 10, 2020
3bb88c9
chore: tests to check for fullscreen option
jenniferarnesen Dec 14, 2020
0abaefe
fix: safari fs
jenniferarnesen Dec 14, 2020
b482fbc
fix: add css from chrome to support safari
jenniferarnesen Dec 14, 2020
575fe8a
chore: cleanup
jenniferarnesen Dec 14, 2020
04469bf
fix: missed ?
jenniferarnesen Dec 14, 2020
b689cbd
Merge branch 'master' into feat/fullscreen-latest
jenniferarnesen Dec 15, 2020
ae0637e
Merge branch 'master' into feat/fullscreen-latest
jenniferarnesen Jan 5, 2021
2384dbb
fix: use viewport height instead of % to improve ff fs rendering
jenniferarnesen Jan 5, 2021
b1b5a17
Merge branch 'master' into feat/fullscreen-latest
jenniferarnesen Jan 5, 2021
7ccacbe
Merge branch 'master' into feat/fullscreen-latest
jenniferarnesen Jan 5, 2021
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
5 changes: 5 additions & 0 deletions .browserslistrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
> 0.5%
last 2 versions
Firefox ESR
not ie 11
not dead
8 changes: 8 additions & 0 deletions cypress/assets/backends/sierraLeone_236.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
export const dashboards = {
'Antenatal Care': {
route: '#/nghVC4wtyzi',
items: {
text: {
itemUid: 'ILRTXgXvurM',
},
chart: {
itemUid: 'azz0KRlHgLs',
},
},
},
Delivery: {
route: '#/iMnYyBfSxmM',
Expand Down
15 changes: 15 additions & 0 deletions cypress/integration/ui/fullscreen.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Feature: Item context menu fullscreen

Background:
Given I open the "Antenatal Care" dashboard
And the "Antenatal Care" dashboard displays in view mode

@nonmutating
Scenario: Text item does not have a context menu
Then the text item does not have a context menu

@nonmutating
Scenario: Chart item has a fullscreen option
Then the chart item has a fullscreen option in the context menu


18 changes: 18 additions & 0 deletions cypress/integration/ui/fullscreen/fullscreen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Then } from 'cypress-cucumber-preprocessor/steps'
import {
getDashboardItem,
itemMenuButton,
clickMenuButton,
} from '../../../selectors/dashboardItem'
import { dashboards } from '../../../assets/backends'

Then('the text item does not have a context menu', () => {
getDashboardItem(dashboards['Antenatal Care'].items.text.itemUid)
.find(itemMenuButton)
.should('not.exist')
})

Then('the chart item has a fullscreen option in the context menu', () => {
clickMenuButton(dashboards['Antenatal Care'].items.chart.itemUid)
cy.contains('View fullscreen').should('be.visible')
})
5 changes: 2 additions & 3 deletions cypress/selectors/dashboardItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ export const tableSel = '.pivot-table-container'
export const gridItemSel = '.react-grid-item'

export const itemDetailsSel = '[data-test="dashboarditem-footer"]'
export const itemMenuButton = '[data-test="dashboarditem-menu-button"]'

export const getDashboardItem = itemUid =>
cy.get(`[data-test="dashboarditem-${itemUid}"]`)

export const clickMenuButton = itemUid =>
getDashboardItem(itemUid)
.find('[data-test="dashboarditem-menu-button"]')
.click()
getDashboardItem(itemUid).find(itemMenuButton).click()
7 changes: 5 additions & 2 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-09T13:38:15.043Z\n"
"PO-Revision-Date: 2020-12-09T13:38:15.043Z\n"
"POT-Creation-Date: 2020-12-10T12:16:21.390Z\n"
"PO-Revision-Date: 2020-12-10T12:16:21.391Z\n"

msgid "Untitled dashboard"
msgstr ""
Expand Down Expand Up @@ -153,6 +153,9 @@ msgstr ""
msgid "Open in {{appName}} app"
msgstr ""

msgid "View fullscreen"
msgstr ""

msgid "Unable to load the plugin for this item"
msgstr ""

Expand Down
29 changes: 29 additions & 0 deletions src/components/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,35 @@ table.pivot * {
background-color: #48a999;
}

div:fullscreen,
div:-webkit-full-screen {
background-color: white;
}

div:-webkit-full-screen {
object-fit: contain;
position: fixed !important;
top: 0px !important;
right: 0px !important;
bottom: 0px !important;
left: 0px !important;
box-sizing: border-box !important;
min-width: 0px !important;
max-width: none !important;
min-height: 0px !important;
max-height: none !important;
width: 100% !important;
height: 100% !important;
transform: none !important;
margin: 0px !important;
}

div:fullscreen .dashboard-item-content,
div:-webkit-full-screen .dashboard-item-content {
height: 95vh !important;
width: 100vw !important;
}

@media print {
body {
width: 100% !important;
Expand Down
64 changes: 61 additions & 3 deletions src/components/Item/VisualizationItem/Item.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import { sGatherAnalyticalObjectStatisticsInDashboardViews } from '../../../reducers/settings'
import { acAddVisualization } from '../../../actions/visualizations'
import { acSetSelectedItemActiveType } from '../../../actions/selected'
import { pluginIsAvailable } from './Visualization/plugin'
import { getDataStatisticsName } from '../../../modules/itemTypes'
import { getVisualizationId, getVisualizationName } from '../../../modules/item'
import memoizeOne from '../../../modules/memoizeOne'
Expand All @@ -36,6 +37,7 @@ export class Item extends Component {
state = {
showFooter: false,
configLoaded: false,
isFullscreen: false,
}

constructor(props, context) {
Expand All @@ -45,6 +47,7 @@ export class Item extends Component {

this.contentRef = React.createRef()
this.headerRef = React.createRef()
this.itemDomElSelector = `.reactgriditem-${this.props.item.id}`

this.memoizedGetContentHeight = memoizeOne(
(calculatedHeight, measuredHeight, preferMeasured) =>
Expand Down Expand Up @@ -73,11 +76,59 @@ export class Item extends Component {
console.log(e)
}

this.setState({ configLoaded: true })

const el = document.querySelector(this.itemDomElSelector)
if (el?.requestFullscreen) {
el.onfullscreenchange = this.handleFullscreenChange
} else if (el?.webkitRequestFullscreen) {
el.onwebkitfullscreenchange = this.handleFullscreenChange
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we should handle the error case with onfullscreenerror

}

componentWillUnmount() {
const el = document.querySelector(this.itemDomElSelector)
if (el?.onfullscreenchange) {
el.removeEventListener(
'onfullscreenchange',
this.handleFullscreenChange
)
} else if (el?.onwebkitfullscreenchange) {
el.removeEventListener(
'onwebkitfullscreenchange',
this.handleFullscreenChange
)
}
}

isFullscreenSupported = () => {
const el = document.querySelector(this.itemDomElSelector)
return !!(el?.requestFullscreen || el?.webkitRequestFullscreen)
}

handleFullscreenChange = () => {
this.setState({
configLoaded: true,
isFullscreen:
!!document.fullscreenElement ||
!!document.webkitFullscreenElement,
})
}

onToggleFullscreen = () => {
if (!this.state.isFullscreen) {
const el = document.querySelector(this.itemDomElSelector)
if (el?.requestFullscreen) {
el.requestFullscreen()
} else if (el?.webkitRequestFullscreen) {
el.webkitRequestFullscreen()
}
} else {
document.exitFullscreen
? document.exitFullscreen()
: document.webkitExitFullscreen()
}
}

getUniqueKey = memoizeOne(() => uniqueId())

onToggleFooter = () => {
Expand All @@ -100,6 +151,10 @@ export class Item extends Component {
}

getAvailableHeight = () => {
if (this.state.isFullscreen) {
return '95vh'
}

const calculatedHeight =
this.props.item.originalHeight -
this.headerRef.current.clientHeight -
Expand All @@ -119,16 +174,19 @@ export class Item extends Component {
const { showFooter } = this.state
const activeType = this.getActiveType()

const actionButtons = (
const actionButtons = pluginIsAvailable(activeType || item.type) ? (
<ItemHeaderButtons
item={item}
visualization={this.props.visualization}
onSelectActiveType={this.setActiveType}
onToggleFooter={this.onToggleFooter}
onToggleFullscreen={this.onToggleFullscreen}
activeType={activeType}
activeFooter={showFooter}
isFullscreen={this.state.isFullscreen}
fullscreenSupported={this.isFullscreenSupported()}
/>
)
) : null

return (
<>
Expand Down
34 changes: 29 additions & 5 deletions src/components/Item/VisualizationItem/ItemHeaderButtons.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ import ChartIcon from '@material-ui/icons/InsertChart'
import MapIcon from '@material-ui/icons/Public'
import LaunchIcon from '@material-ui/icons/Launch'

import { ThreeDots, SpeechBubble } from './assets/icons'
import { pluginIsAvailable, getLink } from './Visualization/plugin'
import {
ThreeDots,
SpeechBubble,
Fullscreen,
ExitFullscreen,
} from './assets/icons'
import { getLink } from './Visualization/plugin'
import {
CHART,
MAP,
Expand All @@ -31,7 +36,6 @@ const iconFill = { fill: colors.grey600 }

const ItemHeaderButtons = (props, context) => {
const [menuIsOpen, setMenuIsOpen] = useState(null)

const { item, visualization, onSelectActiveType, activeType } = props

const isTrackerType = isTrackerDomainType(item.type)
Expand Down Expand Up @@ -60,6 +64,11 @@ const ItemHeaderButtons = (props, context) => {
}
}

const handleToggleFullscreenClick = () => {
props.onToggleFullscreen()
closeMenu()
}

const openMenu = () => setMenuIsOpen(true)
const closeMenu = () => setMenuIsOpen(false)

Expand Down Expand Up @@ -105,7 +114,11 @@ const ItemHeaderButtons = (props, context) => {

const buttonRef = createRef()

return pluginIsAvailable(activeType || item.type) ? (
return props.isFullscreen ? (
<Button small secondary onClick={props.onToggleFullscreen}>
<ExitFullscreen />
</Button>
) : (
<>
<div ref={buttonRef}>
<Button
Expand Down Expand Up @@ -146,20 +159,31 @@ const ItemHeaderButtons = (props, context) => {
label={interpretationMenuLabel}
onClick={handleInterpretationClick}
/>
{props.fullscreenSupported && (
<MenuItem
dense
icon={<Fullscreen />}
label={i18n.t('View fullscreen')}
onClick={handleToggleFullscreenClick}
/>
)}
</Menu>
</Popover>
)}
</>
) : null
)
}

ItemHeaderButtons.propTypes = {
activeFooter: PropTypes.bool,
activeType: PropTypes.string,
fullscreenSupported: PropTypes.bool,
isFullscreen: PropTypes.bool,
item: PropTypes.object,
visualization: PropTypes.object,
onSelectActiveType: PropTypes.func,
onToggleFooter: PropTypes.func,
onToggleFullscreen: PropTypes.func,
}

ItemHeaderButtons.contextTypes = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class Visualization extends React.Component {

Visualization.propTypes = {
activeType: PropTypes.string,
availableHeight: PropTypes.number,
availableHeight: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
item: PropTypes.object,
itemFilters: PropTypes.object,
visualization: PropTypes.object,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jest.mock('../Visualization/plugin', () => ({
pluginIsAvailable: () => true,
}))

it('renders correctly', () => {
it('renders correctly when not fullscreen', () => {
const buttons = shallow(
<ItemHeaderButtons
item={{
Expand All @@ -27,3 +27,24 @@ it('renders correctly', () => {
)
expect(toJson(buttons)).toMatchSnapshot()
})

it('renders correctly when fullscreen', () => {
const buttons = shallow(
<ItemHeaderButtons
item={{
type: 'CHART',
chart: { type: 'NOT_YOY', domainType: 'AGGREGATE' },
}}
visualization={{
type: 'SINGLE_VALUE',
}}
onSelectActiveType={Function.prototype}
activeFooter={false}
activeType={'CHART'}
d2={{}}
onToggleFooter={Function.prototype}
isFullscreen={true}
/>
)
expect(toJson(buttons)).toMatchSnapshot()
})
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ exports[`VisualizationItem/Item does not render Visualization if config not load
<ItemHeaderButtons
activeFooter={false}
activeType="CHART"
fullscreenSupported={false}
isFullscreen={false}
item={
Object {
"chart": Object {
Expand All @@ -18,6 +20,7 @@ exports[`VisualizationItem/Item does not render Visualization if config not load
}
onSelectActiveType={[Function]}
onToggleFooter={[Function]}
onToggleFullscreen={[Function]}
visualization={
Object {
"id": "vis id",
Expand Down
Loading