Skip to content

Commit

Permalink
feat: support new dashboard plugin architecture (#3447)
Browse files Browse the repository at this point in the history
Part of feature: DHIS2-17268

BREAKING CHANGE: this version is only compatible with Dashboard app >=101.0.0

Use generic components for plugins in app-platform and app-runtime.
  • Loading branch information
jenniferarnesen authored Feb 14, 2025
1 parent 9106cac commit 92bdd12
Show file tree
Hide file tree
Showing 7 changed files with 532 additions and 747 deletions.
4 changes: 2 additions & 2 deletions d2.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const config = {

minDHIS2Version: '2.40',

pluginType: 'DASHBOARD',

pwa: {
enabled: true,
caching: {
Expand All @@ -21,8 +23,6 @@ const config = {

coreApp: true,
dataStoreNamespace: 'DHIS2_MAPS_APP_CORE',

skipPluginLogic: true,
}

module.exports = config
9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"postinstall": "patch-package"
},
"devDependencies": {
"@dhis2/cli-app-scripts": "^11.7.5",
"@dhis2/cli-app-scripts": "^11.8.0",
"@dhis2/cli-style": "^10.7.5",
"@dhis2/cypress-commands": "^10.0.6",
"@dhis2/cypress-plugins": "^10.0.6",
Expand All @@ -42,13 +42,12 @@
"start-server-and-test": "^2.0.10"
},
"dependencies": {
"@dhis2/analytics": "^26.11.0",
"@dhis2/app-runtime": "^3.11.2",
"@dhis2/analytics": "^26.12.0",
"@dhis2/app-runtime": "^3.13.2",
"@dhis2/app-runtime-adapter-d2": "^1.1.0",
"@dhis2/app-service-datastore": "^1.0.0-beta.3",
"@dhis2/maps-gl": "^4.0.1",
"@dhis2/ui": "^9.13.0",
"@krakenjs/post-robot": "^11.0.0",
"@dhis2/ui": "^9.15.0",
"@testing-library/react-hooks": "^8.0.1",
"abortcontroller-polyfill": "^1.7.5",
"array-move": "^4.0.0",
Expand Down
124 changes: 9 additions & 115 deletions src/PluginWrapper.js
Original file line number Diff line number Diff line change
@@ -1,106 +1,12 @@
import { useCacheableSection, CacheableSection } from '@dhis2/app-runtime'
import { CenteredContent, CircularLoader, Layer } from '@dhis2/ui'
import postRobot from '@krakenjs/post-robot'
import { DashboardPluginWrapper } from '@dhis2/analytics'
import { debounce } from 'lodash/fp'
import PropTypes from 'prop-types'
import React, { useEffect, useLayoutEffect, useState } from 'react'
import React, { useLayoutEffect, useState } from 'react'
import { Plugin } from './components/plugin/Plugin.js'
import { getPWAInstallationStatus } from './util/getPWAInstallationStatus.js'
import './locales/index.js'

const LoadingMask = () => {
return (
<Layer>
<CenteredContent>
<CircularLoader />
</CenteredContent>
</Layer>
)
}

const CacheableSectionWrapper = ({
id,
children,
cacheNow,
isParentCached,
}) => {
const { startRecording, isCached, remove } = useCacheableSection(id)

useEffect(() => {
if (cacheNow) {
startRecording({ onError: console.error })
}

// NB: Adding `startRecording` to dependencies causes
// an infinite recording loop as-is (probably need to memoize it)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [cacheNow])

useEffect(() => {
const listener = postRobot.on(
'removeCachedData',
// todo: check domain too; differs based on deployment env though
{ window: window.parent },
() => remove()
)

return () => listener.cancel()
}, [remove])

useEffect(() => {
// Synchronize cache state on load or prop update
// -- a back-up to imperative `removeCachedData`
if (!isParentCached && isCached) {
remove()
}

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isParentCached])

return (
<CacheableSection id={id} loadingMask={LoadingMask}>
{children}
</CacheableSection>
)
}
CacheableSectionWrapper.propTypes = {
cacheNow: PropTypes.bool,
children: PropTypes.node,
id: PropTypes.string,
isParentCached: PropTypes.bool,
}

const sendInstallationStatus = (installationStatus) => {
postRobot.send(window.parent, 'installationStatus', { installationStatus })
}

const PluginWrapper = () => {
const [propsFromParent, setPropsFromParent] = useState()
const PluginWrapper = (props) => {
const [renderId, setRenderId] = useState(null)

const receivePropsFromParent = (event) => setPropsFromParent(event.data)

useEffect(() => {
postRobot
.send(window.parent, 'getProps')
.then(receivePropsFromParent)
.catch((err) => console.error(err))

// Get & send PWA installation status now, and also prepare to send
// future updates (installing/ready)
getPWAInstallationStatus({
onStateChange: sendInstallationStatus,
}).then(sendInstallationStatus)

// Allow parent to update props
const listener = postRobot.on(
'newProps',
{ window: window.parent /* Todo: check domain */ },
receivePropsFromParent
)

return () => listener.cancel()
}, [])

useLayoutEffect(() => {
const updateRenderId = debounce(300, () =>
setRenderId((renderId) =>
Expand All @@ -113,23 +19,11 @@ const PluginWrapper = () => {
return () => window.removeEventListener('resize', updateRenderId)
}, [])

return propsFromParent ? (
<div
style={{
display: 'flex',
height: '100%',
overflow: 'hidden',
}}
>
<CacheableSectionWrapper
id={propsFromParent.cacheId}
cacheNow={propsFromParent.recordOnNextLoad}
isParentCached={propsFromParent.isParentCached}
>
<Plugin id={renderId} {...propsFromParent} />
</CacheableSectionWrapper>
</div>
) : null
return (
<DashboardPluginWrapper {...props}>
{(props) => <Plugin id={renderId} {...props} />}
</DashboardPluginWrapper>
)
}

export default PluginWrapper
8 changes: 1 addition & 7 deletions src/components/map/MapLoadingMask.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
import { ComponentCover, CenteredContent, CircularLoader } from '@dhis2/ui'
import cx from 'classnames'
import React from 'react'
import styles from './styles/MapLoadingMask.module.css'

export const loadingMaskClass = 'dhis2-map-loading-mask'

const MapLoadingMask = () => (
<ComponentCover
translucent
className={cx(styles.cover, loadingMaskClass)}
dataTest="map-loading-mask"
>
<ComponentCover className={loadingMaskClass} dataTest="map-loading-mask">
<CenteredContent>
<CircularLoader />
</CenteredContent>
Expand Down
9 changes: 0 additions & 9 deletions src/components/map/styles/MapLoadingMask.module.css

This file was deleted.

9 changes: 8 additions & 1 deletion src/components/plugin/Map.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,14 @@ const Map = forwardRef((props, ref) => {
}

if (!mapIsLoaded) {
const layersToLoad = layers.current.filter((config) => !config.isLoaded)
const layersToLoad = layers.current.filter(
(config) => !config.isLoaded && !config.isLoading
)
layers.current = layers.current.map((layer) =>
layersToLoad.find((l) => l.id === layer.id)
? { ...layer, isLoading: true }
: layer
)
return (
<CenteredContent>
<CircularLoader />
Expand Down
Loading

0 comments on commit 92bdd12

Please sign in to comment.