Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: dhis2/app-runtime
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: efa8147852a66c737bc17942292c6e2410998866
Choose a base ref
..
head repository: dhis2/app-runtime
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: ccb5ec5e0638e54403edfc67910f67be55344f59
Choose a head ref
Showing with 1,211 additions and 568 deletions.
  1. +1 −2 .eslintrc.js
  2. +0 −4 .github/semantic.yml
  3. +32 −0 .github/workflows/dhis2-verify-commits.yml
  4. +4 −0 .hooks/commit-msg
  5. +4 −0 .hooks/pre-commit
  6. +0 −15 .huskyrc.js
  7. +21 −0 CHANGELOG.md
  8. +4 −9 docs/advanced/offline.md
  9. +1 −1 docs/hooks/useAlerts.md
  10. +81 −13 docs/hooks/useDataQuery.md
  11. +1 −1 examples/cra/src/components/Alerts.js
  12. +3 −3 examples/cra/src/components/IndicatorList.js
  13. +2 −0 examples/cra/src/components/SwitchableProvider.js
  14. +5 −5 examples/cra/src/serviceWorker.js
  15. +13 −13 examples/cra/yarn.lock
  16. +1 −1 examples/query-playground/src/components/Editor.js
  17. +2 −2 examples/query-playground/src/components/QueryEditor.js
  18. +3 −3 examples/query-playground/src/components/TabControls.js
  19. +1 −1 examples/query-playground/src/hooks/useExecuteQuery.js
  20. +15 −16 examples/query-playground/src/hooks/useTabs.js
  21. +13 −13 examples/query-playground/yarn.lock
  22. +4 −4 package.json
  23. +5 −5 runtime/package.json
  24. +1 −1 services/alerts/package.json
  25. +2 −3 services/alerts/src/AlertsManagerContext.ts
  26. +1 −1 services/alerts/src/setupRTL.ts
  27. +1 −1 services/config/package.json
  28. +1 −1 services/config/src/__tests__/integration.test.tsx
  29. +1 −1 services/config/src/setupRTL.ts
  30. +2 −2 services/data/package.json
  31. +7 −7 services/data/src/engine/DataEngine.ts
  32. +3 −3 services/data/src/engine/helpers/validate.ts
  33. +1 −1 services/data/src/engine/types/InvalidQueryError.ts
  34. +2 −2 services/data/src/links/RestAPILink/fetchData.test.ts
  35. +2 −2 services/data/src/links/RestAPILink/fetchData.ts
  36. +2 −2 services/data/src/links/RestAPILink/path.ts
  37. +12 −39 services/data/src/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.test.ts
  38. +0 −6 services/data/src/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.ts
  39. +40 −15 services/data/src/links/RestAPILink/queryToRequestOptions/requestContentType.test.ts
  40. +32 −12 services/data/src/links/RestAPILink/queryToRequestOptions/requestContentType.ts
  41. +29 −29 services/data/src/links/RestAPILink/queryToRequestOptions/textPlainMatchers.test.ts
  42. +4 −2 services/data/src/links/RestAPILink/queryToRequestOptions/textPlainMatchers.ts
  43. +25 −0 services/data/src/links/RestAPILink/queryToRequestOptions/xWwwFormUrlencodedMatchers.test.ts
  44. +8 −0 services/data/src/links/RestAPILink/queryToRequestOptions/xWwwFormUrlencodedMatchers.ts
  45. +10 −1 services/data/src/links/RestAPILink/queryToResourcePath.test.ts
  46. +2 −2 services/data/src/links/RestAPILink/queryToResourcePath.ts
  47. +3 −3 services/data/src/links/RestAPILink/validateQuery.ts
  48. +2 −0 services/data/src/react/components/DataProvider.tsx
  49. +1 −1 services/data/src/react/hooks/mergeAndCompareVariables.test.ts
  50. +8 −2 services/data/src/react/hooks/useDataMutation.ts
  51. +2 −2 services/data/src/react/hooks/useDataQuery.test.tsx
  52. +11 −14 services/data/src/react/hooks/useDataQuery.ts
  53. +3 −3 services/data/src/react/hooks/useQueryExecutor.ts
  54. +4 −1 services/data/src/react/hooks/useStaticInput.ts
  55. +1 −1 services/data/src/setupRTL.ts
  56. +2 −2 services/offline/package.json
  57. +14 −17 services/offline/src/__tests__/integration.test.tsx
  58. +1 −1 services/offline/src/lib/__tests__/clear-sensitive-caches.test.ts
  59. +2 −1 services/offline/src/lib/__tests__/offline-provider.test.tsx
  60. +1 −1 services/offline/src/lib/__tests__/online-status.test.tsx
  61. +6 −6 services/offline/src/lib/__tests__/use-cacheable-section.test.tsx
  62. +10 −8 services/offline/src/lib/cacheable-section-state.tsx
  63. +5 −12 services/offline/src/lib/cacheable-section.tsx
  64. +8 −8 services/offline/src/lib/clear-sensitive-caches.ts
  65. +5 −4 services/offline/src/lib/global-state-service.tsx
  66. +2 −3 services/offline/src/lib/offline-interface.tsx
  67. +1 −1 services/offline/src/utils/__tests__/render-counter.test.tsx
  68. +4 −2 services/offline/src/utils/render-counter.tsx
  69. +9 −3 services/offline/src/utils/test-mocks.ts
  70. +702 −228 yarn.lock
3 changes: 1 addition & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -2,9 +2,8 @@ const { config } = require('@dhis2/cli-style')

module.exports = {
extends: [config.eslintReact],
plugins: ['react-hooks'],
rules: {
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'error',
'import/extensions': 'off',
},
}
4 changes: 0 additions & 4 deletions .github/semantic.yml

This file was deleted.

32 changes: 32 additions & 0 deletions .github/workflows/dhis2-verify-commits.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: 'dhis2: verify (commits)'

on:
pull_request:
types: ['opened', 'edited', 'reopened', 'synchronize']

jobs:
lint-pr-title:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: c-hive/gha-yarn-cache@v1
- run: yarn install --frozen-lockfile
- id: commitlint
run: echo ::set-output name=config_path::$(node -e "process.stdout.write(require('@dhis2/cli-style').config.commitlint)")
- uses: JulienKode/pull-request-name-linter-action@v0.5.0
with:
configuration-path: ${{ steps.commitlint.outputs.config_path }}

lint-commits:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: c-hive/gha-yarn-cache@v1
- run: yarn install --frozen-lockfile
- id: commitlint
run: echo ::set-output name=config_path::$(node -e "process.stdout.write(require('@dhis2/cli-style').config.commitlint)")
- uses: wagoid/commitlint-github-action@v4
with:
configFile: ${{ steps.commitlint.outputs.config_path }}
4 changes: 4 additions & 0 deletions .hooks/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn d2-style check commit "$1"
4 changes: 4 additions & 0 deletions .hooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn d2-style check --staged
15 changes: 0 additions & 15 deletions .huskyrc.js

This file was deleted.

21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
## [3.4.4](https://github.com/dhis2/app-runtime/compare/v3.4.3...v3.4.4) (2022-06-08)


### Bug Fixes

* **data-service:** application/x-www-form-urlencoded for svg conversion ([5e2818c](https://github.com/dhis2/app-runtime/commit/5e2818c8d63f9ede61d65ca431f0604201d31531))

## [3.4.3](https://github.com/dhis2/app-runtime/compare/v3.4.2...v3.4.3) (2022-04-06)


### Bug Fixes

* use unversioned api endpoint for tracker sub-resources ([#1158](https://github.com/dhis2/app-runtime/issues/1158)) ([1af1ca7](https://github.com/dhis2/app-runtime/commit/1af1ca7024eb0b1b7172cfa82a573cd3d5684aa9))

## [3.4.2](https://github.com/dhis2/app-runtime/compare/v3.4.1...v3.4.2) (2022-04-05)


### Bug Fixes

* ensure refetch function has stable identity ([9fc3cb4](https://github.com/dhis2/app-runtime/commit/9fc3cb4537d07f89d4501a08fb513f3f491971ac))

## [3.4.1](https://github.com/dhis2/app-runtime/compare/v3.4.0...v3.4.1) (2022-03-22)


13 changes: 4 additions & 9 deletions docs/advanced/offline.md
Original file line number Diff line number Diff line change
@@ -71,13 +71,8 @@ export function CacheableSectionWrapper({ id }) {
import { useCacheableSections } from '@dhis2/app-runtime'

function DemoComponent() {
const {
startRecording,
remove,
lastUpdated,
isCached,
recordingState,
} = useCacheableSection(id)
const { startRecording, remove, lastUpdated, isCached, recordingState } =
useCacheableSection(id)
}
```

@@ -115,11 +110,11 @@ function StartRecordingButton({ id }) {
startRecording({
onStarted: () => console.log('Recording started'),
onCompleted: () => console.log('Recording completed'),
onError: err => console.error(err),
onError: (err) => console.error(err),
recordingTimeoutDelay: 1000, // the default
})
.then(() => console.log('startRecording signal sent successfully'))
.catch(err =>
.catch((err) =>
console.error(`Error when starting recording: ${err}`)
)
}
2 changes: 1 addition & 1 deletion docs/hooks/useAlerts.md
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ const Alerter = () => {
const Alerts = () => {
const alerts = useAlerts()

return alerts.map(alert => (
return alerts.map((alert) => (
<div key={alert.id}>
{alert.message}
<button onClick={alert.remove}>hide</button>
94 changes: 81 additions & 13 deletions docs/hooks/useDataQuery.md
Original file line number Diff line number Diff line change
@@ -13,14 +13,14 @@ const { loading, error, data, refetch } = useDataQuery(query, options)

## Input

| Name | Type | Required | Description |
| :--------------------: | :--------------------: | :----------: | -------------------------------------------------------------------------------------------------------------------------- |
| **query** | [_Query_](types/Query) | **required** | The Query definition describing the requested data |
| **options** | _Object_ | | An optional set of query options |
| **options.variables** | _Object_ | | Variables to be passed to the dynamic portions of the query |
| **options.onComplete** | _Function_ | | Callback function to be called on successfull completion of the query. Called with the response data as the only argument. |
| **options.onError** | _Function_ | | Callback function to be called on failure of the query. Called with the error instance as the only argument. |
| **options.lazy** | _boolean_ | | If true, wait until `refetch` is called before fetching data.<br/>_**Default**: `false`_ |
| Name | Type | Required | Description |
| :--------------------: | :--------------------: | :----------: | --------------------------------------------------------------------------------------------------------------------------- |
| **query** | [_Query_](types/Query) | **required** | The Query definition describing the requested data |
| **options** | _Object_ | | An optional set of query options |
| **options.variables** | _Object_ | | Variables to be passed to the dynamic portions of the query (can also be passed via the refetch function, see Output below) |
| **options.onComplete** | _Function_ | | Callback function to be called on successfull completion of the query. Called with the response data as the only argument. |
| **options.onError** | _Function_ | | Callback function to be called on failure of the query. Called with the error instance as the only argument. |
| **options.lazy** | _boolean_ | | If true, wait until `refetch` is called before fetching data.<br/>_**Default**: `false`_ |

## Output

@@ -30,21 +30,27 @@ const { loading, error, data, refetch } = useDataQuery(query, options)
| **loading** | _boolean_ | **true** if the data is not yet available and no error has yet been encountered |
| **error** | _Error_<br/>or<br/>_undefined_ | **undefined** if no error has occurred, otherwise the Error which was thrown |
| **data** | _QueryResult_<br/>or<br/>_undefined_ | **undefined** if the data is loading or an error has occurred, otherwise a map from the name of each **QueryDefinition** defined in the **Query** to the resulting data for that query |
| **refetch** | _Function_ | This function can be called to refetch the data. Any in-flight HTTP requests will automatically be aborted. |
| **refetch** | _Function_ | This function can be called to refetch the data and can accept variables (see Examples below). Any in-flight HTTP requests will automatically be aborted. |
| **engine** | [_Data Engine_](advanced/DataEngine) | A reference to the DataEngine instance |

## Example
## Examples

### Static query

This is a minimal example showing how to fetch the first page of indicators with a descending order.

```jsx
import React from 'react'
import { useDataQuery } from '@dhis2/app-runtime'
import { CircularLoader } from '@dhis2/ui'

const PAGE_SIZE = 10
const query = {
indicators: {
resource: 'indicators.json',
params: {
order: 'shortName:desc',
pageSize: 10,
pageSize: PAGE_SIZE,
},
},
}
@@ -53,16 +59,78 @@ export const IndicatorList = () => {
return (
<div>
<h3>Indicators (first 10)</h3>
{loading && <span>...</span>}
{loading && <CircularLoader />}
{error && <span>{`ERROR: ${error.message}`}</span>}
{data && (
<pre>
{data.indicators.indicators
.map(ind => ind.displayName)
.map((ind) => ind.displayName)
.join('\n')}
</pre>
)}
</div>
)
}
```

### Dynamic Query

This example is similar to the previous one but builds on top of it by showing how to fetch new pages of data using dynamic variables. A similar approach can be used implement dynamic filtering, ordering, etc.

```jsx
import React from 'react'
import { useDataQuery } from '@dhis2/app-runtime'
import { Pagination, CircularLoader } from '@dhis2/ui'

const PAGE_SIZE = 10
const query = {
// "page" variable below can be dinamically passed via refetch (see "handlePageChange" below)
indicators: {
resource: 'indicators.json',
params: ({ page }) => ({
order: 'shortName:desc',
pageSize: PAGE_SIZE,
page,
}),
},
}

export const IndicatorList = () => {
const { loading, error, data, refetch } = useDataQuery(query)

const pager = data?.indicators?.pager
const hasNextPage = pager?.nextPage

const handlePageChange = (nextPage) => {
// "page" variable in query is passed via refetch below
refetch({ page: nextPage })
}

return (
<div>
<h3>Indicators (paginated)</h3>
{loading && <CircularLoader />}
{error && <span>{`ERROR: ${error.message}`}</span>}
{data && (
<pre>
{data.indicators.indicators
.map((ind) => ind.displayName)
.join('\n')}
</pre>
)}

{pager && (
<Pagination
page={pager.page}
pageCount={pager.pageCount}
pageSize={PAGE_SIZE}
total={pager.total}
isLastPage={hasNextPage}
onPageChange={handlePageChange}
hidePageSizeSelect={true}
/>
)}
</div>
)
}
```
2 changes: 1 addition & 1 deletion examples/cra/src/components/Alerts.js
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ import React from 'react'
export const Alerts = () => {
const alerts = useAlerts()

return alerts.map(alert => (
return alerts.map((alert) => (
<div key={alert.id}>
{alert.message}
<button onClick={alert.remove}>hide</button>
6 changes: 3 additions & 3 deletions examples/cra/src/components/IndicatorList.js
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ const query = {

export const IndicatorList = () => {
const { loading, error, data, refetch } = useDataQuery(query)
const { show } = useAlert(id => `Created indicator ${id}`)
const { show } = useAlert((id) => `Created indicator ${id}`)
return (
<div>
<h3>Indicators</h3>
@@ -25,7 +25,7 @@ export const IndicatorList = () => {
{data && (
<>
<pre>
{data.indicators.indicators.map(ind => (
{data.indicators.indicators.map((ind) => (
<Indicator
key={ind.id}
indicator={ind}
@@ -46,7 +46,7 @@ export const IndicatorList = () => {
&lt;- Previous
</button>
<AddButton
onCreate={result => {
onCreate={(result) => {
show(result.response.uid)
refetch()
}}
2 changes: 2 additions & 0 deletions examples/cra/src/components/SwitchableProvider.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* eslint-disable react/prop-types */

import { Provider, DataProvider } from '@dhis2/app-runtime'
import React from 'react'

10 changes: 5 additions & 5 deletions examples/cra/src/serviceWorker.js
Original file line number Diff line number Diff line change
@@ -57,7 +57,7 @@ export function register(config) {
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
.then((registration) => {
registration.onupdatefound = () => {
const installingWorker = registration.installing
if (installingWorker == null) {
@@ -93,15 +93,15 @@ function registerValidSW(swUrl, config) {
}
}
})
.catch(error => {
.catch((error) => {
console.error('Error during service worker registration:', error)
})
}

function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
.then((response) => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type')
if (
@@ -110,7 +110,7 @@ function checkValidServiceWorker(swUrl, config) {
contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
navigator.serviceWorker.ready.then((registration) => {
registration.unregister().then(() => {
window.location.reload()
})
@@ -129,7 +129,7 @@ function checkValidServiceWorker(swUrl, config) {

export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
navigator.serviceWorker.ready.then((registration) => {
registration.unregister()
})
}
Loading