-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/add js support #1
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
base: master
Are you sure you want to change the base?
Changes from all commits
4e0cff1
e53883b
90f9dbe
6a669df
73e1ece
328f4ed
8ca0837
f4df7ea
bed4fbc
126e05c
b1ecc71
06c0a3f
0131cf5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| import React, { createContext, useContext, useEffect } from 'react' | ||
| import { DE_EVENTS, FIELD_EVENTS } from './legacy-events.js' | ||
| import { | ||
| usePeriod, | ||
| useDataSetId, | ||
| useOrgUnitId, | ||
| usePeriodId, | ||
| } from '../index.js' | ||
| import {useCustomEvent, useEmitOnChange} from "./use-emit"; | ||
|
|
||
| const LegacyDhis2BridgeContext = createContext(undefined) | ||
|
|
||
| export function LegacyDhis2BridgeProvider({ children }) { | ||
| const value = useLegacyDhis2Bridge() | ||
|
|
||
| const [periodId] = usePeriodId() | ||
| const [dataSetId] = useDataSetId() | ||
| const [orgUnitId] = useOrgUnitId() | ||
|
|
||
| const selectedPeriod = usePeriod(periodId) | ||
|
|
||
| useEmitOnChange(selectedPeriod, { eventName: FIELD_EVENTS.period }) | ||
| useEmitOnChange(dataSetId, { eventName: FIELD_EVENTS.dataSet }) | ||
| useEmitOnChange(orgUnitId, { eventName: FIELD_EVENTS.orgUnit }) | ||
|
|
||
| useEffect(() => { | ||
| initializeDhis2Bridge() | ||
| }, []) | ||
|
|
||
| useEffect(() => { | ||
| if (typeof window === 'undefined' || !window.isLegacyDhis2Bridge) return | ||
| updateDhis2Bridge(orgUnitId, dataSetId, selectedPeriod) | ||
| }, [orgUnitId, dataSetId, selectedPeriod]) | ||
|
|
||
| return ( | ||
| <LegacyDhis2BridgeContext.Provider value={value}> | ||
| {children} | ||
| </LegacyDhis2BridgeContext.Provider> | ||
| ) | ||
| } | ||
|
|
||
| export function useLegacyDhis2BridgeContext() { | ||
| const ctx = useContext(LegacyDhis2BridgeContext) | ||
| if (!ctx) | ||
| throw new Error( | ||
| 'useLegacyDhis2BridgeContext must be used within a LegacyDhis2BridgeProvider' | ||
| ) | ||
| return ctx | ||
| } | ||
|
|
||
| function useLegacyDhis2Bridge() { | ||
| const emit = useCustomEvent({ | ||
| target: typeof window !== 'undefined' ? window : null, | ||
| }); | ||
|
|
||
| return { | ||
| dhis2: typeof window !== 'undefined' ? window.dhis2 : undefined, | ||
| emit, | ||
| } | ||
| } | ||
|
|
||
| export function initializeDhis2Bridge() { | ||
| if (typeof window === 'undefined') return | ||
|
|
||
| // Only skip if bridge already initialized it | ||
| if (window.isLegacyDhis2Bridge && window.dhis2) return | ||
|
|
||
| window.dhis2 = { | ||
| util: { | ||
| on(type, handler) { | ||
| window.removeEventListener(type, handler) | ||
| window.addEventListener(type, handler) | ||
| }, | ||
| off(type, handler) { | ||
| window.removeEventListener(type, handler) | ||
| }, | ||
| }, | ||
| de: { | ||
| event: { ...DE_EVENTS }, | ||
| currentOrganisationUnitId: null, | ||
| currentDataSetId: null, | ||
| getSelectedPeriod() { | ||
| return null | ||
| }, | ||
| }, | ||
| } | ||
| window.isLegacyDhis2Bridge = true | ||
| console.info('Legacy DHIS2 bridge initialized') | ||
| } | ||
|
|
||
| function updateDhis2Bridge(orgUnitId, dataSetId, selectedPeriod) { | ||
| if (typeof window === 'undefined' || !window.isLegacyDhis2Bridge) return | ||
|
|
||
| if (window.dhis2?.de) { | ||
| window.dhis2.de.currentOrganisationUnitId = orgUnitId | ||
| window.dhis2.de.currentDataSetId = dataSetId | ||
| window.dhis2.de.getSelectedPeriod = () => selectedPeriod | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,18 @@ | ||||||
| //copied from window.dhis2.de.event in DHIS2 2.41 | ||||||
| export const DE_EVENTS = { | ||||||
| completed: 'dhis2.de.event.completed', | ||||||
| dataValueSaved: 'dhis2.de.event.dataValueSaved', | ||||||
| dataValuesLoaded: 'dhis2.de.event.dataValuesLoaded', | ||||||
| formLoaded: 'dhis2.de.event.formLoaded', | ||||||
| formReady: 'dhis2.de.event.formReady', | ||||||
| uncompleted: 'dhis2.de.event.uncompleted', | ||||||
| validationError: 'dhis2.de.event.validationError', | ||||||
| validationSucces: 'dhis2.de.event.validationSuccess', | ||||||
|
||||||
| validationSucces: 'dhis2.de.event.validationSuccess', | |
| validationSuccess: 'dhis2.de.event.validationSuccess', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this was copied from legacy bridge so I didn't want to apply the correction
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import { useCallback, useEffect, useRef } from 'react'; | ||
|
|
||
| export function useCustomEvent(opts) { | ||
| const { target = typeof window !== 'undefined' ? window : null } = | ||
| opts || {} | ||
|
|
||
| return useCallback( | ||
| (type, detail) => { | ||
| if (!target) return false | ||
| const evt = new CustomEvent(type, { | ||
| detail | ||
| }) | ||
| return target.dispatchEvent(evt) | ||
| }, | ||
| [target] | ||
| ) | ||
| } | ||
|
|
||
| export function useEmitOnSet(setFn, { eventName, target, mapDetail }) { | ||
| const emit = useCustomEvent({ target }); | ||
|
|
||
| return useCallback((value) => { | ||
| setFn(value); | ||
| const detail = typeof mapDetail === "function" ? mapDetail(value) : value; | ||
| emit(eventName, detail); | ||
| }, [setFn, emit, eventName, mapDetail]); | ||
| } | ||
|
|
||
| export function useEmitOnChange(value, { eventName, target, mapDetail, fireOnMount = false }) { | ||
| const emit = useCustomEvent({ target }); | ||
| const isFirstRender = useRef(true); | ||
|
|
||
| useEffect(() => { | ||
| if (isFirstRender.current) { | ||
| isFirstRender.current = false; | ||
| if (!fireOnMount) { | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| const detail = typeof mapDetail === "function" ? mapDetail(value) : value; | ||
| emit(eventName, detail); | ||
| }, [value, emit, eventName, mapDetail, fireOnMount]); | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,46 @@ | ||||||
| import { useEffect } from 'react'; | ||||||
| import { DE_EVENTS } from './legacy-events'; | ||||||
| import { useCustomEvent } from './use-emit'; | ||||||
|
|
||||||
| export function useRunInlineScripts( | ||||||
| { containerRef, dataSetId }, deps | ||||||
| ) { | ||||||
| const emit = useCustomEvent(); | ||||||
|
|
||||||
| useEffect(() => { | ||||||
| const container = containerRef.current; | ||||||
| if (!container) return; | ||||||
|
|
||||||
| let cancelled = false; | ||||||
| (async () => { | ||||||
| await runInlineScripts(container); | ||||||
| if (!cancelled) emit(DE_EVENTS.formLoaded, dataSetId); | ||||||
| })(); | ||||||
|
|
||||||
| return () => { cancelled = true; }; | ||||||
| }, deps); | ||||||
| } | ||||||
|
|
||||||
| function runInlineScripts(container) { | ||||||
| const scripts = [...container.querySelectorAll('script:not([src])')]; | ||||||
|
|
||||||
| return scripts.reduce((chain, script) => { | ||||||
| return chain.then(() => new Promise((resolve) => { | ||||||
| const injScript = document.createElement('script'); | ||||||
| copyAttrs(script, injScript); | ||||||
|
|
||||||
| injScript.text = script.text || script.innerHTML || ''; | ||||||
|
|
||||||
| if (script.parentNode) script.parentNode.replaceChild(injScript, script); | ||||||
|
|
||||||
| Promise.resolve().then(resolve) | ||||||
|
||||||
| Promise.resolve().then(resolve) | |
| resolve(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Script runs synchronously her injScript.text = ...
Calling Promise.resolve().then(resolve) just ensures the scripts run one after another
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The comment says scripts 'pass through', but returning 'undefined' means the default behavior is used. Consider clarifying the comment to say 'Allow scripts to render normally so they can be manually executed in CustomForm' to better reflect what actually happens.