From b282ae4992c1cd158a3879f848c30e2a951c19b6 Mon Sep 17 00:00:00 2001 From: Edoardo Sabadelli Date: Tue, 19 Dec 2023 11:11:09 +0100 Subject: [PATCH 1/6] feat: support outliers table vis type and layout (DHIS2-16344) --- i18n/en.pot | 10 +- package.json | 7 +- src/components/VisTypeIcon.js | 17 +- src/index.js | 3 + .../getAdaptedUiLayoutByType.spec.js | 28 + src/modules/axis.js | 5 + src/modules/getAdaptedUiLayoutByType.js | 26 + src/modules/layoutTypes.js | 1 + src/modules/layoutUiRules/rules.js | 14 + src/modules/visTypeToLayoutType.js | 3 + src/modules/visTypes.js | 9 +- yarn.lock | 858 +++++++++--------- 12 files changed, 557 insertions(+), 424 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index bfd246658..4bf30c374 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -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: 2023-09-27T14:15:13.876Z\n" -"PO-Revision-Date: 2023-09-27T14:15:13.876Z\n" +"POT-Creation-Date: 2024-01-17T09:19:49.801Z\n" +"PO-Revision-Date: 2024-01-17T09:19:49.802Z\n" msgid "view only" msgstr "view only" @@ -150,6 +150,9 @@ msgstr "Math operators" msgid "Data Type" msgstr "Data Type" +msgid "Only {{dataType}} can be used in {{visType}}" +msgstr "Only {{dataType}} can be used in {{visType}}" + msgid "All types" msgstr "All types" @@ -1164,6 +1167,9 @@ msgstr "Scatter" msgid "Single value" msgstr "Single value" +msgid "Outlier table" +msgstr "Outlier table" + msgid "All charts" msgstr "All charts" diff --git a/package.json b/package.json index 1062504ed..d10b61b5b 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "@dhis2/cli-app-scripts": "^9.0.1", "@dhis2/cli-style": "^10.4.1", "@dhis2/d2-i18n": "^1.1.0", - "@dhis2/ui": "^8.4.11", + "@dhis2/ui": "^9.2.0", "@sambego/storybook-state": "^2.0.1", "@storybook/addons": "^6.5.16", "@storybook/preset-create-react-app": "^3.1.7", @@ -52,7 +52,7 @@ "peerDependencies": { "@dhis2/app-runtime": "^3", "@dhis2/d2-i18n": "^1.1", - "@dhis2/ui": "^8.2.5", + "@dhis2/ui": "^9.2.0", "prop-types": "^15", "react": "^16.3", "react-dom": "^16.3", @@ -75,6 +75,9 @@ "react-beautiful-dnd": "^10.1.1", "resize-observer-polyfill": "^1.5.1" }, + "resolutions": { + "@dhis2/ui": "^9.2.0" + }, "files": [ "build" ] diff --git a/src/components/VisTypeIcon.js b/src/components/VisTypeIcon.js index 3949a29f3..b9475ba78 100644 --- a/src/components/VisTypeIcon.js +++ b/src/components/VisTypeIcon.js @@ -1,5 +1,4 @@ import { - IconTable16, IconVisualizationArea16, IconVisualizationAreaStacked16, IconVisualizationBar16, @@ -11,11 +10,12 @@ import { IconVisualizationLine16, IconVisualizationLinelist16, IconVisualizationLineMulti16, + IconVisualizationOutlierTable16, IconVisualizationPie16, + IconVisualizationPivotTable16, IconVisualizationRadar16, IconVisualizationScatter16, IconVisualizationSingleValue16, - IconTable24, IconVisualizationArea24, IconVisualizationAreaStacked24, IconVisualizationBar24, @@ -27,7 +27,9 @@ import { IconVisualizationGauge24, IconVisualizationLine24, IconVisualizationLineMulti24, + IconVisualizationOutlierTable24, IconVisualizationPie24, + IconVisualizationPivotTable24, IconVisualizationRadar24, IconVisualizationScatter24, IconVisualizationSingleValue24, @@ -51,6 +53,7 @@ import { VIS_TYPE_YEAR_OVER_YEAR_COLUMN, VIS_TYPE_SINGLE_VALUE, VIS_TYPE_SCATTER, + VIS_TYPE_OUTLIER_TABLE, } from '../modules/visTypes.js' export const VisTypeIcon = ({ type, useSmall = false, ...props }) => { @@ -64,7 +67,9 @@ export const VisTypeIcon = ({ type, useSmall = false, ...props }) => { break } case VIS_TYPE_PIVOT_TABLE: { - VisIcon = useSmall ? IconTable16 : IconTable24 + VisIcon = useSmall + ? IconVisualizationPivotTable16 + : IconVisualizationPivotTable24 break } case VIS_TYPE_BAR: { @@ -141,6 +146,12 @@ export const VisTypeIcon = ({ type, useSmall = false, ...props }) => { : IconVisualizationScatter24 break } + case VIS_TYPE_OUTLIER_TABLE: { + VisIcon = useSmall + ? IconVisualizationOutlierTable16 + : IconVisualizationOutlierTable24 + break + } case VIS_TYPE_COLUMN: default: { VisIcon = useSmall diff --git a/src/index.js b/src/index.js index 8db29cbb2..07e1f9cbe 100644 --- a/src/index.js +++ b/src/index.js @@ -203,6 +203,7 @@ export { VIS_TYPE_PIVOT_TABLE, VIS_TYPE_SCATTER, VIS_TYPE_LINE_LIST, + VIS_TYPE_OUTLIER_TABLE, visTypeDisplayNames, visTypeIcons, getDisplayNameByVisType, @@ -212,6 +213,7 @@ export { isYearOverYear, isDualAxisType, isSingleValue, + isOutlierTable, isTwoCategoryChartType, isLegendSetType, isColumnBasedType, @@ -228,6 +230,7 @@ export { LAYOUT_TYPE_PIVOT_TABLE, LAYOUT_TYPE_SCATTER, LAYOUT_TYPE_LINE_LIST, + LAYOUT_TYPE_OUTLIER_TABLE, } from './modules/layoutTypes.js' // Modules: layoutUiRules diff --git a/src/modules/__tests__/getAdaptedUiLayoutByType.spec.js b/src/modules/__tests__/getAdaptedUiLayoutByType.spec.js index 1a5534f79..1de6c2e5b 100644 --- a/src/modules/__tests__/getAdaptedUiLayoutByType.spec.js +++ b/src/modules/__tests__/getAdaptedUiLayoutByType.spec.js @@ -15,6 +15,7 @@ import { VIS_TYPE_BAR, VIS_TYPE_PIE, VIS_TYPE_SINGLE_VALUE, + VIS_TYPE_OUTLIER_TABLE, } from '../visTypes.js' const someId = 'someId' @@ -172,6 +173,33 @@ describe('getAdaptedUiLayoutByType', () => { expect(actualState).toEqual(expectedState) }) + it('outlier table: moves all dimensions to columns with dimension strings', () => { + const initialState = { + [AXIS_ID_COLUMNS]: [DIMENSION_ID_DATA, someId], + [AXIS_ID_ROWS]: [DIMENSION_ID_PERIOD, otherId], + [AXIS_ID_FILTERS]: [DIMENSION_ID_ORGUNIT], + } + + const actualState = getAdaptedUiLayoutByType( + initialState, + VIS_TYPE_OUTLIER_TABLE + ) + + const expectedState = { + [AXIS_ID_COLUMNS]: [ + DIMENSION_ID_DATA, + DIMENSION_ID_PERIOD, + DIMENSION_ID_ORGUNIT, + someId, + otherId, + ], + [AXIS_ID_ROWS]: [], + [AXIS_ID_FILTERS]: [], + } + + expect(actualState).toEqual(expectedState) + }) + it('pivot -> sv with dimension strings', () => { const initialLayout = { [AXIS_ID_COLUMNS]: [DIMENSION_ID_DATA, DIMENSION_ID_PERIOD], diff --git a/src/modules/axis.js b/src/modules/axis.js index 6dfe47cfe..0b68cd3f1 100644 --- a/src/modules/axis.js +++ b/src/modules/axis.js @@ -11,6 +11,7 @@ import { LAYOUT_TYPE_PIVOT_TABLE, LAYOUT_TYPE_SCATTER, LAYOUT_TYPE_LINE_LIST, + LAYOUT_TYPE_OUTLIER_TABLE, } from './layoutTypes.js' const getAxisNamesByLayoutType = (layoutType) => { @@ -40,6 +41,10 @@ const getAxisNamesByLayoutType = (layoutType) => { [AXIS_ID_ROWS]: i18n.t('Points'), [AXIS_ID_FILTERS]: i18n.t('Filter'), } + case LAYOUT_TYPE_OUTLIER_TABLE: + return { + [AXIS_ID_COLUMNS]: i18n.t('Columns'), + } } } diff --git a/src/modules/getAdaptedUiLayoutByType.js b/src/modules/getAdaptedUiLayoutByType.js index 42ef2e546..c57d8eac7 100644 --- a/src/modules/getAdaptedUiLayoutByType.js +++ b/src/modules/getAdaptedUiLayoutByType.js @@ -17,6 +17,7 @@ import { VIS_TYPE_SINGLE_VALUE, VIS_TYPE_PIVOT_TABLE, VIS_TYPE_SCATTER, + VIS_TYPE_OUTLIER_TABLE, isTwoCategoryChartType, } from './visTypes.js' @@ -40,6 +41,8 @@ export const getAdaptedUiLayoutByType = (layout, type) => { return layout case VIS_TYPE_SCATTER: return getScatterLayout(layout) + case VIS_TYPE_OUTLIER_TABLE: + return getOutlierTableLayout(layout) default: return getDefaultLayout(layout) } @@ -140,6 +143,29 @@ const getSingleValueLayout = (layout) => { } } +// Transform from ui.layout to outlier table layout format +const getOutlierTableLayout = (layout) => ({ + [AXIS_ID_COLUMNS]: [ + DIMENSION_ID_DATA, + DIMENSION_ID_PERIOD, + DIMENSION_ID_ORGUNIT, + ...[ + ...layout[AXIS_ID_COLUMNS], + ...layout[AXIS_ID_ROWS], + ...layout[AXIS_ID_FILTERS], + ].filter( + (dim) => + ![ + DIMENSION_ID_DATA, + DIMENSION_ID_ORGUNIT, + DIMENSION_ID_PERIOD, + ].includes(getDimensionId(dim)) + ), + ], + [AXIS_ID_ROWS]: [], + [AXIS_ID_FILTERS]: [], +}) + /** * * @param {string|object} dimension diff --git a/src/modules/layoutTypes.js b/src/modules/layoutTypes.js index fc2de5664..3ba97137d 100644 --- a/src/modules/layoutTypes.js +++ b/src/modules/layoutTypes.js @@ -5,3 +5,4 @@ export const LAYOUT_TYPE_YEAR_OVER_YEAR = 'LAYOUT_TYPE_YEAR_OVER_YEAR' export const LAYOUT_TYPE_PIVOT_TABLE = 'LAYOUT_TYPE_PIVOT_TABLE' export const LAYOUT_TYPE_SCATTER = 'LAYOUT_TYPE_SCATTER' export const LAYOUT_TYPE_LINE_LIST = 'LAYOUT_TYPE_LINE_LIST' +export const LAYOUT_TYPE_OUTLIER_TABLE = 'LAYOUT_TYPE_OUTLIER_TABLE' diff --git a/src/modules/layoutUiRules/rules.js b/src/modules/layoutUiRules/rules.js index 8add0bcb2..40f9f7537 100644 --- a/src/modules/layoutUiRules/rules.js +++ b/src/modules/layoutUiRules/rules.js @@ -25,6 +25,7 @@ import { VIS_TYPE_PIVOT_TABLE, VIS_TYPE_SCATTER, VIS_TYPE_LINE_LIST, + VIS_TYPE_OUTLIER_TABLE, } from '../visTypes.js' const RULE_PROP_AVAILABLE_AXES = 'availableAxes', @@ -129,6 +130,18 @@ const lineListRules = { [RULE_PROP_AVAILABLE_AXES]: [AXIS_ID_COLUMNS, AXIS_ID_FILTERS], } +const outlierTableRules = { + [RULE_PROP_AVAILABLE_AXES]: [AXIS_ID_COLUMNS], + [RULE_PROP_MIN_DIMS_PER_AXIS]: { + [AXIS_ID_COLUMNS]: 3, + }, + [RULE_PROP_LOCKED_DIMS]: { + [DIMENSION_ID_DATA]: AXIS_ID_COLUMNS, + [DIMENSION_ID_PERIOD]: AXIS_ID_COLUMNS, + [DIMENSION_ID_ORGUNIT]: AXIS_ID_COLUMNS, + }, +} + const visTypeToRules = { [VIS_TYPE_COLUMN]: defaultRules, [VIS_TYPE_STACKED_COLUMN]: defaultRules, @@ -146,6 +159,7 @@ const visTypeToRules = { [VIS_TYPE_PIVOT_TABLE]: pivotTableRules, [VIS_TYPE_SCATTER]: scatterRules, [VIS_TYPE_LINE_LIST]: lineListRules, + [VIS_TYPE_OUTLIER_TABLE]: outlierTableRules, } const getRulesByVisType = (visType) => { diff --git a/src/modules/visTypeToLayoutType.js b/src/modules/visTypeToLayoutType.js index 0b1d21af1..cd1d94b69 100644 --- a/src/modules/visTypeToLayoutType.js +++ b/src/modules/visTypeToLayoutType.js @@ -5,6 +5,7 @@ import { LAYOUT_TYPE_PIVOT_TABLE, LAYOUT_TYPE_SCATTER, LAYOUT_TYPE_LINE_LIST, + LAYOUT_TYPE_OUTLIER_TABLE, } from './layoutTypes.js' import { VIS_TYPE_COLUMN, @@ -23,6 +24,7 @@ import { VIS_TYPE_PIVOT_TABLE, VIS_TYPE_SCATTER, VIS_TYPE_LINE_LIST, + VIS_TYPE_OUTLIER_TABLE, } from './visTypes.js' const visTypeToLayoutType = { @@ -42,6 +44,7 @@ const visTypeToLayoutType = { [VIS_TYPE_PIVOT_TABLE]: LAYOUT_TYPE_PIVOT_TABLE, [VIS_TYPE_SCATTER]: LAYOUT_TYPE_SCATTER, [VIS_TYPE_LINE_LIST]: LAYOUT_TYPE_LINE_LIST, + [VIS_TYPE_OUTLIER_TABLE]: LAYOUT_TYPE_OUTLIER_TABLE, } export const getLayoutTypeByVisType = (visType) => visTypeToLayoutType[visType] diff --git a/src/modules/visTypes.js b/src/modules/visTypes.js index 8150b43f2..2040dad5e 100644 --- a/src/modules/visTypes.js +++ b/src/modules/visTypes.js @@ -1,5 +1,4 @@ import { - IconTable24, IconVisualizationArea24, IconVisualizationAreaStacked24, IconVisualizationBar24, @@ -11,7 +10,9 @@ import { IconVisualizationLine24, IconVisualizationLinelist24, IconVisualizationLineMulti24, + IconVisualizationOutlierTable24, IconVisualizationPie24, + IconVisualizationPivotTable24, IconVisualizationRadar24, IconVisualizationScatter24, IconVisualizationSingleValue24, @@ -36,6 +37,7 @@ export const VIS_TYPE_SINGLE_VALUE = 'SINGLE_VALUE' export const VIS_TYPE_BUBBLE = 'BUBBLE' export const VIS_TYPE_GROUP_ALL = 'ALL' export const VIS_TYPE_GROUP_CHARTS = 'CHARTS' +export const VIS_TYPE_OUTLIER_TABLE = 'OUTLIER_TABLE' export const visTypeDisplayNames = { [VIS_TYPE_PIVOT_TABLE]: i18n.t('Pivot table'), @@ -54,12 +56,13 @@ export const visTypeDisplayNames = { [VIS_TYPE_RADAR]: i18n.t('Radar'), [VIS_TYPE_SCATTER]: i18n.t('Scatter'), [VIS_TYPE_SINGLE_VALUE]: i18n.t('Single value'), + [VIS_TYPE_OUTLIER_TABLE]: i18n.t('Outlier table'), [VIS_TYPE_GROUP_ALL]: i18n.t('All types'), [VIS_TYPE_GROUP_CHARTS]: i18n.t('All charts'), } export const visTypeIcons = { - [VIS_TYPE_PIVOT_TABLE]: IconTable24, + [VIS_TYPE_PIVOT_TABLE]: IconVisualizationPivotTable24, [VIS_TYPE_AREA]: IconVisualizationArea24, [VIS_TYPE_STACKED_AREA]: IconVisualizationAreaStacked24, [VIS_TYPE_BAR]: IconVisualizationBar24, @@ -75,6 +78,7 @@ export const visTypeIcons = { [VIS_TYPE_RADAR]: IconVisualizationRadar24, [VIS_TYPE_SCATTER]: IconVisualizationScatter24, [VIS_TYPE_SINGLE_VALUE]: IconVisualizationSingleValue24, + [VIS_TYPE_OUTLIER_TABLE]: IconVisualizationOutlierTable24, } export const getDisplayNameByVisType = (visType) => { @@ -143,6 +147,7 @@ export const isYearOverYear = (type) => yearOverYearTypes.includes(type) export const isDualAxisType = (type) => dualAxisTypes.includes(type) export const isMultiType = (type) => multiTypeTypes.includes(type) export const isSingleValue = (type) => type === VIS_TYPE_SINGLE_VALUE +export const isOutlierTable = (type) => type === VIS_TYPE_OUTLIER_TABLE export const isTwoCategoryChartType = (type) => twoCategoryChartTypes.includes(type) export const isVerticalType = (type) => verticalTypes.includes(type) diff --git a/yarn.lock b/yarn.lock index 08374ba9f..0488c6214 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1722,564 +1722,583 @@ resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18" integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg== -"@dhis2-ui/alert@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/alert/-/alert-8.4.11.tgz#fce9d0ee88cf06b3c52a6c080686338dfe7aeed8" - integrity sha512-xHEUuwtw0ODQ9khrye98kTAM+R4AlKOxeM9IZ620NBvjVwZqEI3LniVK9Z+YxqGye0NjCgPrlS2icBT5Nlr03Q== +"@dhis2-ui/alert@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/alert/-/alert-9.2.0.tgz#3b30e90a88a96c617219c60ad8bc16a5969e85d1" + integrity sha512-9uh6vIKlj9vm9wOr/7UvSvxrEgl0p99+FdfFtvCeiOywR2+CCPvd9fRsL/vWKcCraopOriCmTm5PzBUWPuatvg== dependencies: - "@dhis2-ui/portal" "8.4.11" + "@dhis2-ui/portal" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" - "@dhis2/ui-icons" "8.4.11" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-icons" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/box@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/box/-/box-8.4.11.tgz#39e2ff734ae7f7fab5a25f67d21e37bbe643e515" - integrity sha512-Dc4Dj8goHLTZY58OTneXdqrrFXIIQY5NKOe5iBQ0DVQi5bsU4KXS2fxZaWWXTcdIqXEDS/2Oqxfh9t5oV2GbRQ== +"@dhis2-ui/box@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/box/-/box-9.2.0.tgz#0f8fd79059d724c075180ea442c860ebff88c802" + integrity sha512-ERNEd8lDAQIGDmTYknWClPGbmWuOpFAnE8XurB6wrkydn0M2+wsIa00q2on6RgY2YaAjVtYZEXr9CfjBtwh04w== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/button@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/button/-/button-8.4.11.tgz#edf83cc8dc0b33523dd43f5812135a0353ab77e7" - integrity sha512-A+w9tnaaEOl22ydyB/Phr9Rvhe85S5y+2E2jSkhOZ54r4iyAn1y1qMeq070JztrGSWb968N6zwVKERkZivdkxg== +"@dhis2-ui/button@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/button/-/button-9.2.0.tgz#8d465f471ca629e1cb0ded01fc68143eb4960c1c" + integrity sha512-YBdIpaOqEgpI0JhHm8XBlpFGDF7O6eiFQ4Noxu131KEyDOf7a+AkIyECVZw6Jsj3n2zrbyzBDw6IndSFSlBqRQ== dependencies: - "@dhis2-ui/layer" "8.4.11" - "@dhis2-ui/loader" "8.4.11" - "@dhis2-ui/popper" "8.4.11" + "@dhis2-ui/layer" "9.2.0" + "@dhis2-ui/loader" "9.2.0" + "@dhis2-ui/popper" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" - "@dhis2/ui-icons" "8.4.11" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-icons" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/card@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/card/-/card-8.4.11.tgz#ef46cd4bb8ca66cc054d8d90091fd98e58c6c3d3" - integrity sha512-TIkVY3R8qj+Xnj6jXTphxTekQyOhmUVIVeqHDV4CEv1Wa3j6B2x+PPEMVgvrGCvms7gkEhILg6VQQD2516WFhg== - dependencies: +"@dhis2-ui/calendar@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/calendar/-/calendar-9.2.0.tgz#c3fbcdc622a13dd112de163719cfdcd0f960cfe2" + integrity sha512-6xxkCx66tjfGffGoPnDB6pe9Pvx+UybAGCqof7/0xfHP2vnfdss716imM02HfJRZwp5dNUrXqBO9uuQ1qkp/HA== + dependencies: + "@dhis2-ui/button" "9.2.0" + "@dhis2-ui/card" "9.2.0" + "@dhis2-ui/input" "9.2.0" + "@dhis2-ui/layer" "9.2.0" + "@dhis2-ui/popper" "9.2.0" + "@dhis2/multi-calendar-dates" "1.0.2" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-icons" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/center@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/center/-/center-8.4.11.tgz#4254fa9cc9e638f4b7f0445054c4ec832eb1653c" - integrity sha512-zzFtdtzfP8FUw63Lzd6FDs0fNiZ8UR8rXa/3z7dqHT28qKLWWPHnDGIqv9UFmvF88+JnHveoBUBO2yWRY0kKvw== +"@dhis2-ui/card@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/card/-/card-9.2.0.tgz#1a2d8f2f9becfd9fd43e7e315fda511418698fd1" + integrity sha512-++eLieBc4WsNXD1iQEQaqijnQ7hpKu0HH9cjkwTa46jjI9SoJGdYInvCzZPafB1XuCZ42f08kTcdND2qZWbssQ== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/checkbox@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/checkbox/-/checkbox-8.4.11.tgz#536cddf3de2c744fa786fb635dc4dd9fe93e8a42" - integrity sha512-I8WniCQzwmGOEFLi8G2/bVUpCDBhJvGMn5DEewoY+qseBQGvhkA6Lz2pcPdKYImnlv1i8cIvMsRfa8Wg2iJyhQ== +"@dhis2-ui/center@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/center/-/center-9.2.0.tgz#f7fbcbd00696d718286f269a64a5bad327160449" + integrity sha512-IBwLld/SQEtT5acjBr3nJnpkXNzypxm9f+QfdGYRq2ZSQmkIv9IUjIvlC25CU72Ksq+N0nWvA8AYp2Qx/l04fg== dependencies: - "@dhis2-ui/field" "8.4.11" - "@dhis2-ui/required" "8.4.11" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/chip@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/chip/-/chip-8.4.11.tgz#9b6908a0c8e0a6656e467b689d85ec322d63bb8c" - integrity sha512-9m4+9s85iBp81fvy8chv2wep8jjJtjfPMZ/CkrK7M+LRTjS8pDpO+Lp9BkMvDTaek9pqyucEdiuFWLY7UzVPuw== +"@dhis2-ui/checkbox@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/checkbox/-/checkbox-9.2.0.tgz#7f27c645bb9d31df4898c9c96cfdabf9ddd01ab3" + integrity sha512-EIGbkdnCLOyW3s7Mh31LdPgghHPaTAt0MJe5ieF/1NycUCfx99NGoR7wXlAlI6wNvjkuTYnYBknERGHUEzY9rA== dependencies: + "@dhis2-ui/field" "9.2.0" + "@dhis2-ui/required" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/cover@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/cover/-/cover-8.4.11.tgz#a69d2dd8a03adcbbc8d459d62ce6d4cd14024c03" - integrity sha512-FlLcsC0vbHr6psbDFeSjH3X/34qOTpk+8mHGZtwkKDzPtZafk2+vra0vSQRwcjM1bzG4jlSJBJWI2kuu8l76Zg== +"@dhis2-ui/chip@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/chip/-/chip-9.2.0.tgz#45b5e55e6d11318bb6917f1bcdee9bf9b67bd620" + integrity sha512-PesbKz0MrXegDAFcGHq34Ast0kM/mJrmExiLua262egYYaRiQXDYeaNuS2TT+qPsPsDva2Pp7kqjdrFRDL2Few== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/css@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/css/-/css-8.4.11.tgz#bac0cbfd3827d767bb558a7daf4efcf1a2d98c51" - integrity sha512-cxUa+i/Zb370KMHAbdDISuRyEzYq572aS+SxfTBVdosNAxRepFs3Dq892g6paAPLqn/P/cV6O2Rbpronkui8+A== +"@dhis2-ui/cover@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/cover/-/cover-9.2.0.tgz#0288dad0ba9caf8c0b77c86868fc4bae3388f6c7" + integrity sha512-ZxcOOuyy/dB4pgjdVzBlgwtVue0HEct2XOiuIKM66F5G0DPBqz6UtLDD+8VRofQwTKcTYQh9kPZPmx/9q65Zcg== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/divider@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/divider/-/divider-8.4.11.tgz#8396eedaa65b3384ceb3f56963a22ad6f04e3514" - integrity sha512-VZqsLUV7MflznEKo8xzEXMX9c6r7OQICKB82iOYGgH7qfKLXUdRSbNYxlrXktSnLDshG/SoF2FbDjGBDQdFYuw== +"@dhis2-ui/css@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/css/-/css-9.2.0.tgz#ad881b90371c7506690ee2a2b2151c09f793e368" + integrity sha512-dBe9S75+Nj1c9SNRkxU0VWTVwFZJ+vLFKxD1UYaPdbJ2DHD6AD4UJ1YtXgvBvgiJ0wbT1vesh3tKfEUXbnTmkw== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/field@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/field/-/field-8.4.11.tgz#c48919e173a43f8ec80cb38e54a10c90dd106fad" - integrity sha512-nUvbU64F9V3GC2aDn/zhNPufHH+oV/fEGtYUvKK7bAhvnppstBAOz76w/p+zuZiacLC8T86asPNN8JUx8mFXUw== +"@dhis2-ui/divider@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/divider/-/divider-9.2.0.tgz#2f9ea2f3a1f89853963bcffdeef27e5ebc390021" + integrity sha512-z87v4XKIO1IXhWmHYhCQgR7MTiuU+zLMg9Py2OIDxMchVXrdyGSeKCpL9UgOzl/jtHxwoMdnVDzy8sLLzITgsw== dependencies: - "@dhis2-ui/box" "8.4.11" - "@dhis2-ui/help" "8.4.11" - "@dhis2-ui/label" "8.4.11" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/file-input@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/file-input/-/file-input-8.4.11.tgz#10b691ceb0624c2125d6b12c460200594f198358" - integrity sha512-IcD/hLGFTs+Fgqvc4WI7JAhhwybQ38Zbw7wMygI+SX1TVmqIoB961ReLTf53YR768RGT7M1GRIqNJ/2udRGFFw== +"@dhis2-ui/field@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/field/-/field-9.2.0.tgz#d16b443ff168e352b6f5168d2179975377902dd1" + integrity sha512-pZr1LkzOm4TOPdPAen9e3RHQE8Y1uCbdFhP1oc5vg44etmudX7stJuEXP6/DrPm6sHCJRojf90laqPai1vfrHg== dependencies: - "@dhis2-ui/button" "8.4.11" - "@dhis2-ui/field" "8.4.11" - "@dhis2-ui/label" "8.4.11" - "@dhis2-ui/loader" "8.4.11" - "@dhis2-ui/status-icon" "8.4.11" + "@dhis2-ui/box" "9.2.0" + "@dhis2-ui/help" "9.2.0" + "@dhis2-ui/label" "9.2.0" + "@dhis2/prop-types" "^3.1.2" + "@dhis2/ui-constants" "9.2.0" + classnames "^2.3.1" + prop-types "^15.7.2" + +"@dhis2-ui/file-input@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/file-input/-/file-input-9.2.0.tgz#d48e9d290aea798640b251d915b9a8f51abcf363" + integrity sha512-x+lKilo6aQN320fIAPZMAq13PxKuTb/jhZb7m7pMS0HKsPqfT5rWm1VfmBlN/02BkKsmhsvo/tTe9vxpAic71A== + dependencies: + "@dhis2-ui/button" "9.2.0" + "@dhis2-ui/field" "9.2.0" + "@dhis2-ui/label" "9.2.0" + "@dhis2-ui/loader" "9.2.0" + "@dhis2-ui/status-icon" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" - "@dhis2/ui-icons" "8.4.11" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-icons" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/header-bar@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/header-bar/-/header-bar-8.4.11.tgz#f2f0c95d9bcb3286e159e5977dd6f4d0f76995cd" - integrity sha512-FN/fVSHPE4lKYSIbUYvqYXK2bP+HmzhDJZVGPgNMHNnf+2tDLbCzF0X+66G9cXqJPZQB2P+9o3tL7/pBRVdSag== - dependencies: - "@dhis2-ui/box" "8.4.11" - "@dhis2-ui/card" "8.4.11" - "@dhis2-ui/center" "8.4.11" - "@dhis2-ui/divider" "8.4.11" - "@dhis2-ui/input" "8.4.11" - "@dhis2-ui/layer" "8.4.11" - "@dhis2-ui/loader" "8.4.11" - "@dhis2-ui/logo" "8.4.11" - "@dhis2-ui/menu" "8.4.11" - "@dhis2-ui/user-avatar" "8.4.11" +"@dhis2-ui/header-bar@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/header-bar/-/header-bar-9.2.0.tgz#83efe93821318decca3bd1a28d30fd3089435b6d" + integrity sha512-7iNKOllQNeYfa9nJjXVqiohcuKEk+RMhuu/7BY8g2oPmwbvS0uc49KiL53cFfAWBST/v5fU1XOpWS/N7PmJr2g== + dependencies: + "@dhis2-ui/box" "9.2.0" + "@dhis2-ui/button" "9.2.0" + "@dhis2-ui/card" "9.2.0" + "@dhis2-ui/center" "9.2.0" + "@dhis2-ui/divider" "9.2.0" + "@dhis2-ui/input" "9.2.0" + "@dhis2-ui/layer" "9.2.0" + "@dhis2-ui/loader" "9.2.0" + "@dhis2-ui/logo" "9.2.0" + "@dhis2-ui/menu" "9.2.0" + "@dhis2-ui/modal" "9.2.0" + "@dhis2-ui/user-avatar" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" - "@dhis2/ui-icons" "8.4.11" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-icons" "9.2.0" classnames "^2.3.1" moment "^2.29.1" prop-types "^15.7.2" -"@dhis2-ui/help@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/help/-/help-8.4.11.tgz#e557b6c58381f97a86c8bdfb4692c09a657c1349" - integrity sha512-MmrT5YfjKXtinPsw1xF+XoSe/FWO8uuFpuivFcU3EtSvcOBrRUizpQoiuo3Qb7KtLzqtr/585n12hKlcquXVqw== +"@dhis2-ui/help@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/help/-/help-9.2.0.tgz#76556c0d581fb0f7611fe5c5bb0d9adf35ad0c8e" + integrity sha512-qD3oNEwEb+pT7jsD4ciHtu16KrxMySPWoqco5nJwoGbcZFLw/caEfkBo2IroImD0MxtI0mKt5emD7V2yXRWm7A== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/input@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/input/-/input-8.4.11.tgz#2e4e1a08ff0ab4ebef821c94e805ef9fcff569e3" - integrity sha512-2fQcuRo8u8bLd7YnTnG3KWHWOaw0o03DK1szUslZaPqlCdwvAE1C7d0um0RaoluDpA1vk6sEY6gSz0TGPN8LDA== - dependencies: - "@dhis2-ui/box" "8.4.11" - "@dhis2-ui/field" "8.4.11" - "@dhis2-ui/input" "8.4.11" - "@dhis2-ui/loader" "8.4.11" - "@dhis2-ui/status-icon" "8.4.11" +"@dhis2-ui/input@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/input/-/input-9.2.0.tgz#bdf7c72e11b818ed86e1e6335cd373ae037f4ef3" + integrity sha512-0bzF/8pZSMqe5ZN2v0t0/rMTvKWd9kl5MDOy9fRXpX6yoFgfH+j+iIU06eVyqJl3DMqCdInfapJvpJR7MHvd+g== + dependencies: + "@dhis2-ui/box" "9.2.0" + "@dhis2-ui/field" "9.2.0" + "@dhis2-ui/input" "9.2.0" + "@dhis2-ui/loader" "9.2.0" + "@dhis2-ui/status-icon" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" - "@dhis2/ui-icons" "8.4.11" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-icons" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/intersection-detector@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/intersection-detector/-/intersection-detector-8.4.11.tgz#63fad64b86d13a537d61e77bb31b83d48c9831d4" - integrity sha512-VMIfmRjxMsO2LnqJkaVEUA5JGPlIwH79e/W54SYTFCTVeQyeI7l4zIaePDGoIBmP/rSZBdoBmbL4hXRSa2mxCw== +"@dhis2-ui/intersection-detector@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/intersection-detector/-/intersection-detector-9.2.0.tgz#31f19bfb8645ebda7d7af6f6e641b40fa1e57888" + integrity sha512-erBoDMhOPmua8eP8bKJNl4WIUUm+Fw3Jj9M0OxC+xia3/Fi8scLUyk0Yek1Y0lGdb4YHJEXqx475dLNjpmiLRQ== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/label@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/label/-/label-8.4.11.tgz#eb15961534f279711e8298210518628c8d90c7eb" - integrity sha512-CMnGk9qbQoRHrw+PopebeDyQGGX/Kw/1tTb58xF8klI6gKtrlu9ggMHOORhFe6It0oENZPsa6wbdpELXnyeygA== +"@dhis2-ui/label@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/label/-/label-9.2.0.tgz#81ec2de2ea1b9bc7cf25eb8bb036d451d5b61748" + integrity sha512-k9Q4XyIbaNRvCn1+rLcEb/iDi479S3fOEJ2MCT6wsGxr1+Hys7yVw7Ggq0OQ9SLCEwElNQcvj+vWB4dZltXl5Q== dependencies: - "@dhis2-ui/required" "8.4.11" + "@dhis2-ui/required" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/layer@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/layer/-/layer-8.4.11.tgz#d3fcfb2cd520775b2e6380a3d9dc75c7c3ea2e57" - integrity sha512-oPE9ESxNXxVjOZHKJR8wNDK1J7OiBhNQUiWrzt4KIwVS15PUTDN+GYvSRZvPneKiXsKa9jKQx2pSUYjYaSrXyw== +"@dhis2-ui/layer@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/layer/-/layer-9.2.0.tgz#7afcb2ef8571eb9c839fc522eb76ead70e395dbc" + integrity sha512-95aFgQYxgJ7GmWY6AOSoAH4BH7wkIyUAioAIRUDwp0mmSJhJxG6P9b1PFqw4koX1zV4/RLoz+NiQ7Twv+03CLQ== dependencies: - "@dhis2-ui/portal" "8.4.11" + "@dhis2-ui/portal" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/legend@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/legend/-/legend-8.4.11.tgz#c09e521cf46a0dfa535a5ee8fe1733877fc0821f" - integrity sha512-wiJUj9sBQdZvIrEI0dcadFmPLioINP4ulTNZklMgeCTiu2BfEQ6tSaXJm/Bf8jdhV5LFJbmc46cB20KIvXGdFw== +"@dhis2-ui/legend@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/legend/-/legend-9.2.0.tgz#a051822fd67c1442b6986a86097aca94f3960dd2" + integrity sha512-IfJhu48eu//O9AlHt7IUdsv5E92XG7v/95laFfyQOaGhf1C4BQf11s6yXc3nTFoil/p55oAsZnWp5e7UXcgrhg== dependencies: - "@dhis2-ui/required" "8.4.11" + "@dhis2-ui/required" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/loader@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/loader/-/loader-8.4.11.tgz#ba937e196606eed30486d5f1e14130385348b998" - integrity sha512-z96kT2lPSKqkB6RYjiEY/nZlAi19toWGu74JUU0GP7iiuE6gD13Z0dBnjxK/ENY0U8PCDd0sYY1dw7iVEaqt3A== +"@dhis2-ui/loader@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/loader/-/loader-9.2.0.tgz#f45fdb19a37182351932cd3e11980d43b4895d40" + integrity sha512-M8tuLE5kgm7oHmIN6par8GfRDpmt+DXFU3cCSZdiYIUM6SQSD8G5LmA8AaIHR5h7l430d93vcw7RQL3eeE/svw== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/logo@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/logo/-/logo-8.4.11.tgz#9a222a6febe8257d46a141ca4c554d9276a7796f" - integrity sha512-oPsSm8kkSoP8QwHgqgwPz++8+1d6LFgH18E42YL82f97EG55D+qSGmoXmCm70v3r8A+TEKESbCvaO6diux5x1w== +"@dhis2-ui/logo@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/logo/-/logo-9.2.0.tgz#0452aadd8e92afc1c558869a08c8e5d1f14ff55f" + integrity sha512-OEFrSpDijeCIhLjJ8vipDGRdihTgj8+iLcPLDrOeRAurlgJZJZiicxBKSuY6uc6p1/9QPccqX2huJjM1uNqo2g== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/menu@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/menu/-/menu-8.4.11.tgz#18fa23006a18677fd376d9500bdd6c8448cedf9a" - integrity sha512-oQX+O806FH5dEuOuVISuEj8C2muIgfdoxK2UEgStNatzA8incxsD6xaH6SYuhbIsK3dAOKAmGIRGyK6e4FAoqA== - dependencies: - "@dhis2-ui/card" "8.4.11" - "@dhis2-ui/divider" "8.4.11" - "@dhis2-ui/layer" "8.4.11" - "@dhis2-ui/popper" "8.4.11" - "@dhis2-ui/portal" "8.4.11" +"@dhis2-ui/menu@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/menu/-/menu-9.2.0.tgz#996729545b29def9366a099005a17c781bdca8f6" + integrity sha512-8k15qmoqBWKo1Afj+QXFnDAIoDyAqosvq3j8M/+xM+cSn0H+6e3Q7UOp0ByVQlumoY5DyrT9Z7NojULIjFUifQ== + dependencies: + "@dhis2-ui/card" "9.2.0" + "@dhis2-ui/divider" "9.2.0" + "@dhis2-ui/layer" "9.2.0" + "@dhis2-ui/popper" "9.2.0" + "@dhis2-ui/portal" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" - "@dhis2/ui-icons" "8.4.11" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-icons" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/modal@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/modal/-/modal-8.4.11.tgz#14db36733a8d7b27dc24d45823bc26e25bc81d41" - integrity sha512-f47QFlU4T/PjjwHFBYZM/+kbe/MHdr+QLhG4j51X6N/h9QnTOkMERbh62BY9LUkIi6fKpcs5mWv2/RSW5yWkwQ== +"@dhis2-ui/modal@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/modal/-/modal-9.2.0.tgz#7815efe62be4f482b2e2224ffe92981068b1bae9" + integrity sha512-gp+gCTmtoiLVAhigo1i6msE598qIGnkW6To+dTkUecvxyvni+DZTAulTmL62UtTzzjPjYO0yOqNTWQztbpj1KQ== dependencies: - "@dhis2-ui/card" "8.4.11" - "@dhis2-ui/center" "8.4.11" - "@dhis2-ui/layer" "8.4.11" - "@dhis2-ui/portal" "8.4.11" + "@dhis2-ui/card" "9.2.0" + "@dhis2-ui/center" "9.2.0" + "@dhis2-ui/layer" "9.2.0" + "@dhis2-ui/portal" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" - "@dhis2/ui-icons" "8.4.11" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-icons" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/node@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/node/-/node-8.4.11.tgz#3eb4999e43b29fe522d8a8693102f4f2fa50e789" - integrity sha512-v7QWu8fB08Bb+dMyydFYHnzaxTwExgY5Eof8uxlxCvB8z00+tTe/C9J06yb0mf2YT7KX9nFD8Y6H3qs6XV8lzw== +"@dhis2-ui/node@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/node/-/node-9.2.0.tgz#3560b3e7394d275c1bce3e800b2654cddcd2adb2" + integrity sha512-xx7P/6V7vq3JLXUUATKGGUCORHqQL74fsGYUd9a0izyUyq4h3pEHL9ZT6Cel+A0d5ODYn/j/Q6fHICZzg55FBQ== dependencies: - "@dhis2-ui/loader" "8.4.11" + "@dhis2-ui/loader" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/notice-box@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/notice-box/-/notice-box-8.4.11.tgz#ae271cf7c127ffb0281c4d87e9a1155938a8ceb8" - integrity sha512-qzL2a30GPsIjOKaAM43+G6yIGPpUqCEoZztcAUoRQ/uYmIJMeGaNACmIbvBjpK6EHDWFIeA02+1dsfEcCFLREA== +"@dhis2-ui/notice-box@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/notice-box/-/notice-box-9.2.0.tgz#8c1fb4a2a780fea0fde4b8f9eaf8f73957187c99" + integrity sha512-DY3WYXj1hsOsiBHGaNrOeZ8h7SPaXox6iMCTzL/jLvnfmTrH7wy6SHRLQYWg0BMrDflhMJu9qhn0jtzhEXZNMQ== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" - "@dhis2/ui-icons" "8.4.11" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-icons" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/organisation-unit-tree@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/organisation-unit-tree/-/organisation-unit-tree-8.4.11.tgz#49433734bfb17292c986d3199a6ef0940c1f0239" - integrity sha512-LJdiaRhIdnY3gpaiyRyysAc++JlemDFSmAn09ftrnB9UXM3MunbU9rfyCOVBW70wnDmAYAlm4/ZVeUP+Ny5pTQ== +"@dhis2-ui/organisation-unit-tree@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/organisation-unit-tree/-/organisation-unit-tree-9.2.0.tgz#ce33d91361145d6574ddbef3866b56be06cee6cb" + integrity sha512-PHm908gNGPhq5D655BI4lrB+hMqfISKASjoFCWxG2f9FU64/pvQ+snZQQwQFMAJYMd6FKw4GOP1isKz0jTGNuA== dependencies: - "@dhis2-ui/checkbox" "8.4.11" - "@dhis2-ui/loader" "8.4.11" - "@dhis2-ui/node" "8.4.11" + "@dhis2-ui/checkbox" "9.2.0" + "@dhis2-ui/loader" "9.2.0" + "@dhis2-ui/node" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/pagination@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/pagination/-/pagination-8.4.11.tgz#ce551b27cb857db6470767072d4ea54a472c42db" - integrity sha512-+CU/kAE2tp0anIz7HT9suWjGmjagDlt6Ts1vjhEoo8hLAkz26/b0k2QYiR7/ickJdTWW8nmQrS32YOZVN8t6aA== +"@dhis2-ui/pagination@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/pagination/-/pagination-9.2.0.tgz#f38d1ada2bc9f9795dae8f86c52d54f29889dd17" + integrity sha512-jXJNQ8JOPweeMFCsPXgAb8dAx8J/rNTnExL8WA6rfRDWujOojLp8Gu2MrH5jlHRpCBWIl+aJO1I/ZKHekQOeDg== dependencies: - "@dhis2-ui/button" "8.4.11" - "@dhis2-ui/select" "8.4.11" + "@dhis2-ui/button" "9.2.0" + "@dhis2-ui/select" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" - "@dhis2/ui-icons" "8.4.11" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-icons" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/popover@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/popover/-/popover-8.4.11.tgz#a8b055a28def6bca29b897217bf70b13b463a521" - integrity sha512-nmVOCjHGsf/heoj1Nb4552jGdh1WFBdJ5HDGHXS3y+ONh6ltWen8ezj4LuqY9asKi/ZRBUlta2LIPAJUe+WkBQ== +"@dhis2-ui/popover@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/popover/-/popover-9.2.0.tgz#3eccd6abde1da72089aa5feda5669ce4d1aa6f7e" + integrity sha512-7g8AHPrzUuMuv2MXpX5HdwiyO+peSGoq7hg6rHN/VDasIUvGS7vbaV4Xbxfd32fNmpHceBV7gMga31hRNaKtgw== dependencies: - "@dhis2-ui/layer" "8.4.11" - "@dhis2-ui/popper" "8.4.11" + "@dhis2-ui/layer" "9.2.0" + "@dhis2-ui/popper" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/popper@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/popper/-/popper-8.4.11.tgz#d3a6007a1a018309cb882051c36cd1c38cae8951" - integrity sha512-8+7a/Svo2aTJ3pl/ia6i4cR9bOP1ZJOczPOakaacPB/c5NTOt8t6Nh0ADHC4qfaI3UfPnXtb6VceNmqVglwJiA== +"@dhis2-ui/popper@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/popper/-/popper-9.2.0.tgz#6d17ca49d7f0496289e11315d414095d93d179b5" + integrity sha512-6xWNvUQaDu8VE4rCa+uYOheb/4BD/52Cs23w2yt4lLAVrym4kV+0cnpHtlEG1ZuuVrK/yHelMjrmYxn6yJE41Q== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" "@popperjs/core" "^2.10.1" classnames "^2.3.1" prop-types "^15.7.2" react-popper "^2.2.5" resize-observer-polyfill "^1.5.1" -"@dhis2-ui/portal@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/portal/-/portal-8.4.11.tgz#840e567e01c3a36c61f66f0a4dd4c020c865a618" - integrity sha512-S7yWmnEpZ3CtJS666Yib2oTsZ7qi+GQlznG8CJpgGn+06oWGy3bQ64cf7ssCl+yrGFLbHyZ192gsEKYiVhT46Q== +"@dhis2-ui/portal@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/portal/-/portal-9.2.0.tgz#12435f3a8886de685fc9ee4540639810eabf0191" + integrity sha512-a5DqmTG+pn4y1aXZWr8wEGlK4xRqdvnOWJVqO/6aebEn4ZtcPJT6yyXouviAM2yoW7+fDj6TZhKrZo3vgMLHlA== dependencies: classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/radio@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/radio/-/radio-8.4.11.tgz#caf316ffa6818df8c7a7a260e234638db956394d" - integrity sha512-LQw/lZtG0rdDE/eQiTBlxPUzRY3/zAxFEEE+oOQGdWZrRi0THipzCeTsfS4/+OPtkCKHR4a/jo3eozRyYodH9Q== +"@dhis2-ui/radio@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/radio/-/radio-9.2.0.tgz#9223ada90fe2d0eae375e85e0d99e6eb81d5269a" + integrity sha512-rjcz05spFlvRL8fnkO/7/ckznY4agQLl5P7UKSFL3Kz0KxvnocmounTX1BDsm6iQhKKt2HqJCYShpzPhTXavSA== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/required@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/required/-/required-8.4.11.tgz#764cdedf38ca17cccfeabfdf3c80a686d6d75e16" - integrity sha512-VdZNQhJn5MFG7B8sPvN2qtbow1SOW6X8yqoxJUFQDxlaN9i/RFZPYgGLD+zxIzAJAV8C3+KpxS7mwuhANCAF9Q== +"@dhis2-ui/required@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/required/-/required-9.2.0.tgz#8d76c914c3eb483958284c8ddf52ef7095cc2f42" + integrity sha512-cNoQct7gVbrAdzGDDFLnfBktmwmxAhD48oEZ6z4TE7IrPi2N+idqOiQCTEVKEWmuY7VGv9TV0I/1OLX6oiuSvA== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/segmented-control@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/segmented-control/-/segmented-control-8.4.11.tgz#aad7b36f179b89057922c53466834a15be7a29fd" - integrity sha512-bp0y04xSWgcJB3hYTxayFHlzCVgzOKvZ2TGBDfdsAcGioC5sZhJHeFCN7sHgqWqTW3o47WE2nDGvfxK2ab6BNQ== +"@dhis2-ui/segmented-control@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/segmented-control/-/segmented-control-9.2.0.tgz#2b230eea17e35194072544ebbfc68e2c896fb951" + integrity sha512-x4eDqGu4JFfFPQk06mg8YdUCjYeYyXCLmZ0gbGj1Zx03gxpMwb4/nVyGoAxZg31/IVkhBOeAwXlvt+ckNXovgw== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/select@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/select/-/select-8.4.11.tgz#f3796c4763085b7236e3b1f30cb678307ae4c1fd" - integrity sha512-tHn7QIFdgpudtG1QV+YXirt6YXrocv8/VTEGSL6WI6l+yyAu9In6v2bFmaS7W0sdW+nzIJ+TD7UsEs2nPlMYuA== - dependencies: - "@dhis2-ui/box" "8.4.11" - "@dhis2-ui/button" "8.4.11" - "@dhis2-ui/card" "8.4.11" - "@dhis2-ui/checkbox" "8.4.11" - "@dhis2-ui/chip" "8.4.11" - "@dhis2-ui/field" "8.4.11" - "@dhis2-ui/input" "8.4.11" - "@dhis2-ui/layer" "8.4.11" - "@dhis2-ui/loader" "8.4.11" - "@dhis2-ui/popper" "8.4.11" - "@dhis2-ui/status-icon" "8.4.11" +"@dhis2-ui/select@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/select/-/select-9.2.0.tgz#e854ecf6fd2580f9fb904301d1985923c1ade9cf" + integrity sha512-Hv0mRbpJNHJUa2lVivrWBt67iJ+4xTb7KxZkThISkmpQpzbWmhmJWlDbe0L+PdqOsaB2A70/N9HH4dhQyVP8vA== + dependencies: + "@dhis2-ui/box" "9.2.0" + "@dhis2-ui/button" "9.2.0" + "@dhis2-ui/card" "9.2.0" + "@dhis2-ui/checkbox" "9.2.0" + "@dhis2-ui/chip" "9.2.0" + "@dhis2-ui/field" "9.2.0" + "@dhis2-ui/input" "9.2.0" + "@dhis2-ui/layer" "9.2.0" + "@dhis2-ui/loader" "9.2.0" + "@dhis2-ui/popper" "9.2.0" + "@dhis2-ui/status-icon" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" - "@dhis2/ui-icons" "8.4.11" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-icons" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/selector-bar@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/selector-bar/-/selector-bar-8.4.11.tgz#4e6fd2e7b022103dd4c42165698eeb32e97f6faa" - integrity sha512-dE05308qTK8ZeXwtnviJLOGxWY5o/uiKeMMrqPK4rxqfwQwJKTpU/7KCaLQQnFOax03UJ4YOheXrBCaTzYCqtw== - dependencies: - "@dhis2-ui/button" "8.4.11" - "@dhis2-ui/card" "8.4.11" - "@dhis2-ui/layer" "8.4.11" - "@dhis2-ui/popper" "8.4.11" - "@dhis2/ui-constants" "8.4.11" - "@dhis2/ui-icons" "8.4.11" +"@dhis2-ui/selector-bar@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/selector-bar/-/selector-bar-9.2.0.tgz#23790f850bee1142e7bdffd711a2dea27dc78112" + integrity sha512-HDb/XKuUdD82vDh5mgz+OWIwpKjyiZEqek74y8g2r/AsMIl5Fe2rOyncOUPpGctxYeVp9u3gAlkFbIHd/qpv/w== + dependencies: + "@dhis2-ui/button" "9.2.0" + "@dhis2-ui/card" "9.2.0" + "@dhis2-ui/layer" "9.2.0" + "@dhis2-ui/popper" "9.2.0" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-icons" "9.2.0" "@testing-library/react" "^12.1.2" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/sharing-dialog@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/sharing-dialog/-/sharing-dialog-8.4.11.tgz#5652f7dd5fed8531fb6baa42a2614dca25f6caf8" - integrity sha512-wawhmZZlQ08815hMk1QqZtKiHWZzoEbXV6y2MB8lY8uRoYI8hCQPKrZkNwtkeJce40ZehkjW/uMqqhwXeLN90A== - dependencies: - "@dhis2-ui/box" "8.4.11" - "@dhis2-ui/button" "8.4.11" - "@dhis2-ui/card" "8.4.11" - "@dhis2-ui/divider" "8.4.11" - "@dhis2-ui/input" "8.4.11" - "@dhis2-ui/layer" "8.4.11" - "@dhis2-ui/menu" "8.4.11" - "@dhis2-ui/modal" "8.4.11" - "@dhis2-ui/notice-box" "8.4.11" - "@dhis2-ui/popper" "8.4.11" - "@dhis2-ui/select" "8.4.11" - "@dhis2-ui/tab" "8.4.11" - "@dhis2-ui/tooltip" "8.4.11" - "@dhis2-ui/user-avatar" "8.4.11" +"@dhis2-ui/sharing-dialog@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/sharing-dialog/-/sharing-dialog-9.2.0.tgz#8818e5119c2113b3a54add3bd56dfe55ad2fc52a" + integrity sha512-h7chsY8XM2kw06r52pGRjS8ZknuwiEkRxuBTXD77G1Loni5TMm6xr0OFr9nSQHSsbcbxydbo2WPRPAu6WjVyfg== + dependencies: + "@dhis2-ui/box" "9.2.0" + "@dhis2-ui/button" "9.2.0" + "@dhis2-ui/card" "9.2.0" + "@dhis2-ui/divider" "9.2.0" + "@dhis2-ui/input" "9.2.0" + "@dhis2-ui/layer" "9.2.0" + "@dhis2-ui/menu" "9.2.0" + "@dhis2-ui/modal" "9.2.0" + "@dhis2-ui/notice-box" "9.2.0" + "@dhis2-ui/popper" "9.2.0" + "@dhis2-ui/select" "9.2.0" + "@dhis2-ui/tab" "9.2.0" + "@dhis2-ui/tooltip" "9.2.0" + "@dhis2-ui/user-avatar" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" - "@dhis2/ui-icons" "8.4.11" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-icons" "9.2.0" "@react-hook/size" "^2.1.2" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/status-icon@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/status-icon/-/status-icon-8.4.11.tgz#99f84536260d415d9859aacc66c09b464b528671" - integrity sha512-OfyHie6mWklmc2mrDIOYDJDIzozQiqCU4xnBh59JtT/TaF6ppLEf9o6qbJbYuHnzNI5BqEZuQ6BMNjTX2TtMNQ== +"@dhis2-ui/status-icon@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/status-icon/-/status-icon-9.2.0.tgz#5686721ef7dc1b1cd1cca315b11136e0809e568f" + integrity sha512-oILHs38xICDU27C7CXgFAEttU4QJzg0wImKlX4XVJ5z1aGeq6qOW0RUVibF6JtyAppEi9XsYZ+AB1KLEGnDFJw== dependencies: - "@dhis2-ui/loader" "8.4.11" + "@dhis2-ui/loader" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" - "@dhis2/ui-icons" "8.4.11" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-icons" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/switch@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/switch/-/switch-8.4.11.tgz#9bb76ab78de4ba57b15b25e5d2b4cd0eeb6b2ed9" - integrity sha512-ziztjkX8ZGSlKubt87zcLQt9P85JsuBSxMZgkWn+iHgQ8njsYb2MBjgs/l/sPcR8p+v+1YuZPtXV7HisJB63FQ== +"@dhis2-ui/switch@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/switch/-/switch-9.2.0.tgz#1fc9ed88546906da11aa19348b8ba8ff2e7c8414" + integrity sha512-IwVUiqxMKZmx7VtEJkyHqGzy4tvRNh4qbhmrFodaH6d9YrbycaFNECbphrT1OywrUBuci0Q2LovZhYKInkeIFg== dependencies: - "@dhis2-ui/field" "8.4.11" - "@dhis2-ui/required" "8.4.11" + "@dhis2-ui/field" "9.2.0" + "@dhis2-ui/required" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/tab@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/tab/-/tab-8.4.11.tgz#5739521b604aa962f29640c4cab75e200f2140b8" - integrity sha512-Iv+yjIfPq8TZjU+NNxbTED8qT/ux0FdDEpz2DtgCPo/2joq5W5CRbLLIJv5P4kNVs/ZOwwL4+RBhNjdKVJJGvQ== +"@dhis2-ui/tab@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/tab/-/tab-9.2.0.tgz#910223a907f8c007921924138306b53c2014f0fd" + integrity sha512-zVxwQ7WgjcrCGd+qWzLx+OtTWlGOIuC+AuSknHz0wpGgW3vawr1rMDA6j129l4O+ezzPs5bw8vN3xUQORbj0Bg== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" - "@dhis2/ui-icons" "8.4.11" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-icons" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/table@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/table/-/table-8.4.11.tgz#8dba83268536452fad797c587c2c934ed0b8a25c" - integrity sha512-yJbkN2cVPIjcAR08MY/Zbi3WCZY7BYWPzE4/CvQtyXJVT7AQymQfXpn4yzv2LWG3qOlBl+S36qYQnL8PBTko+w== +"@dhis2-ui/table@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/table/-/table-9.2.0.tgz#83d535e6c60ff9c9c3f436e592489a131caef772" + integrity sha512-wep8wPQKRg5vYPGg3HLAvsAEo+lUk3L7wO7axHyzx/XuHR40QXRirpv4tsFVn8VPpSFVq0w/BFGp8ijp3Y6RtA== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" - "@dhis2/ui-icons" "8.4.11" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-icons" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/tag@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/tag/-/tag-8.4.11.tgz#897a7045e07f0b224613079be96b23665dde3dbe" - integrity sha512-HsTCVnc6NB8Vj4snOqiAJ0W66jrFF1rWPSGOPH2ucXzhu2FyuQIbyIPCYMT+/ntoo1Ht2d4BqBFtswBsHbBMWA== +"@dhis2-ui/tag@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/tag/-/tag-9.2.0.tgz#c6a11fbdfc20151cc75c323e6a6b1470ef064e58" + integrity sha512-qt/FUIFPZkghfF/fhhLHr4oOl54d1Zwy4CKf4cZmO2DSd09h7E7cfIUxXaX/shRHsSmeUhrFP2Nv5LAvu8XVUg== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/text-area@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/text-area/-/text-area-8.4.11.tgz#49e27a2c327ff34e5c695cff73629bfa1291862b" - integrity sha512-pRRuFTwYM1Ae/yshrOOaeLqsRFEw9JPD1npZkTn45QHo3u8BITkSXhzhR4DK+U1Wi3qDd2ZpiVDFZ1b/vQVwgA== +"@dhis2-ui/text-area@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/text-area/-/text-area-9.2.0.tgz#75f279281f1513801148f11c2693537a5e9beed7" + integrity sha512-KLBlerlO3OvBvzTVKKolfghaAMjDPUZkxFsyRvWEIPh1RRxS9ZprunXpBWRkNO2I6U6uwqN0DiBLTNbh/zrafg== dependencies: - "@dhis2-ui/box" "8.4.11" - "@dhis2-ui/field" "8.4.11" - "@dhis2-ui/loader" "8.4.11" - "@dhis2-ui/status-icon" "8.4.11" + "@dhis2-ui/box" "9.2.0" + "@dhis2-ui/field" "9.2.0" + "@dhis2-ui/loader" "9.2.0" + "@dhis2-ui/status-icon" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" - "@dhis2/ui-icons" "8.4.11" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-icons" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/tooltip@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/tooltip/-/tooltip-8.4.11.tgz#4fd2b9e9c151b931d351f0b8d2393ba2013c5e37" - integrity sha512-/wihjoTpgp0CakwXy4j2mAJ0o5ByPb7pwsue8BsxB+ETP4LumYmndAQKPcbZLM2jKEsRRmageD0GbUdSM+6TUA== +"@dhis2-ui/tooltip@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/tooltip/-/tooltip-9.2.0.tgz#e7a0827d9ca6b30b69073916f7801d01fb7c9793" + integrity sha512-7Gb/Occ5/Bju95dnxUGzt/Q4129zqGWrxu1+S2uhc0YPRSx83JcG0MivPsVsr0BeU+p+8xwTVdGBOhMmqLpL9Q== dependencies: - "@dhis2-ui/popper" "8.4.11" - "@dhis2-ui/portal" "8.4.11" + "@dhis2-ui/popper" "9.2.0" + "@dhis2-ui/portal" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/transfer@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/transfer/-/transfer-8.4.11.tgz#cbda7c97597ef678777d63429299c2b81764a8a2" - integrity sha512-BrMJkDP0EN/3FQwKwq7H8xVHFazwHH/cCud/lEvL2EWhAkCE0e9Y0Be9PW8UEgkQJ132noBo51MhGaclB169lg== - dependencies: - "@dhis2-ui/button" "8.4.11" - "@dhis2-ui/field" "8.4.11" - "@dhis2-ui/input" "8.4.11" - "@dhis2-ui/intersection-detector" "8.4.11" - "@dhis2-ui/loader" "8.4.11" +"@dhis2-ui/transfer@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/transfer/-/transfer-9.2.0.tgz#3624d08100b72143da5639ce231b53d57706ffdc" + integrity sha512-1+SdfeCBr+iOLaXf4gkpNLlysaBsWLOmlVzBdGVqFt4I4SMJrhFrCMUTz4A1Nsw7GpKGds35vGClQUe5LcuVSw== + dependencies: + "@dhis2-ui/button" "9.2.0" + "@dhis2-ui/field" "9.2.0" + "@dhis2-ui/input" "9.2.0" + "@dhis2-ui/intersection-detector" "9.2.0" + "@dhis2-ui/loader" "9.2.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/user-avatar@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/user-avatar/-/user-avatar-8.4.11.tgz#764c8da9ee8dadbd40aa298ac5e88a408053d3be" - integrity sha512-HIwOosHytYKeA0EQ4TI3FeOFUsoNZGJy5ysprV3/Tym4wrIfYkpii5AUCbq7kCap2IxCcJnNsnPHWu7zA2p/wA== +"@dhis2-ui/user-avatar@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/user-avatar/-/user-avatar-9.2.0.tgz#722b4bad239aff8eb2b639ca51bd6528a08da1e4" + integrity sha512-XRop5Mmc5q1GnrM3YgIEdjw0OX7/KA9ZdxRNS4AU7ifYMEjUNutYmq8a2bJ1M6eZfq2DrhRQui9/1E7MvK+Evw== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.4.11" + "@dhis2/ui-constants" "9.2.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -2450,6 +2469,14 @@ "@js-temporal/polyfill" "^0.4.2" classnames "^2.3.2" +"@dhis2/multi-calendar-dates@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@dhis2/multi-calendar-dates/-/multi-calendar-dates-1.0.2.tgz#e54dc85e512aba93fceef3004e67e199077f3ba8" + integrity sha512-oQZ7PFMwHFpt4ygDN9DmAeYO3g07L7AHJW6diZ37mzpkEF/DyMafhsZHnJWNlTH5HDp8nYuO3EjBiM7fZN6C0g== + dependencies: + "@js-temporal/polyfill" "^0.4.2" + classnames "^2.3.2" + "@dhis2/prop-types@^3.1.2": version "3.1.2" resolved "https://registry.yarnpkg.com/@dhis2/prop-types/-/prop-types-3.1.2.tgz#65b8ad2da8cd2f72bc8b951049a6c9d1b97af3e9" @@ -2466,90 +2493,91 @@ workbox-routing "^6.1.5" workbox-strategies "^6.1.5" -"@dhis2/ui-constants@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2/ui-constants/-/ui-constants-8.4.11.tgz#3869fd3060e2ca8a60f4e2afb501d4539f646756" - integrity sha512-2Px9rUrYQqqKzQ69abgSiIBy773kEl2baYOKn/wXzDrfx8uA7VZCIu1PCHgtc0cPyZwPeD80gOfqdQw8d8lzyQ== +"@dhis2/ui-constants@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2/ui-constants/-/ui-constants-9.2.0.tgz#47506acaec5e4ce28630519a630a05657d2d29f6" + integrity sha512-gMbnVJQJmCPoGJHnY09BoDe6Z1vukzFdUcm0HPYyijs8ZWnclLs+69iVamxhskOnNWgj8hEt/FVs4mfhMcW3Cg== dependencies: prop-types "^15.7.2" -"@dhis2/ui-forms@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2/ui-forms/-/ui-forms-8.4.11.tgz#cf778974d922f506bf8cb6d457dda9ebff94b81d" - integrity sha512-HBwTFHt/JLdCs46oDM6degEoaTaaRiJBE+ZAKwP+9kQsLDlk20Av7XVE90uKNlNhI+CGztClJ8QLcz4YnVyDng== - dependencies: - "@dhis2-ui/button" "8.4.11" - "@dhis2-ui/checkbox" "8.4.11" - "@dhis2-ui/field" "8.4.11" - "@dhis2-ui/file-input" "8.4.11" - "@dhis2-ui/input" "8.4.11" - "@dhis2-ui/radio" "8.4.11" - "@dhis2-ui/select" "8.4.11" - "@dhis2-ui/switch" "8.4.11" - "@dhis2-ui/text-area" "8.4.11" +"@dhis2/ui-forms@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2/ui-forms/-/ui-forms-9.2.0.tgz#a5651dc5010a495c8a52ef5484e7d44b2583b715" + integrity sha512-eodiPW+ahR5wVsgrl/bFvj2zyeJD+DR9woqys4ZyoaHlKjOdeLqDNbJDnrS+AmHfte5uorF/aWzmEZr825LBVg== + dependencies: + "@dhis2-ui/button" "9.2.0" + "@dhis2-ui/checkbox" "9.2.0" + "@dhis2-ui/field" "9.2.0" + "@dhis2-ui/file-input" "9.2.0" + "@dhis2-ui/input" "9.2.0" + "@dhis2-ui/radio" "9.2.0" + "@dhis2-ui/select" "9.2.0" + "@dhis2-ui/switch" "9.2.0" + "@dhis2-ui/text-area" "9.2.0" "@dhis2/prop-types" "^3.1.2" classnames "^2.3.1" final-form "^4.20.2" prop-types "^15.7.2" react-final-form "^6.5.3" -"@dhis2/ui-icons@8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2/ui-icons/-/ui-icons-8.4.11.tgz#a796f2d6fed5e51be5c36981382ee329e40edac4" - integrity sha512-aUXDXjQvSy4kNEFW/NC67PXFbz9IYXxYIEgpcXDKAzdCe8Sfq88ghgTCw9CIQftyWOqKOQO7s2SB3IE+n5zVdQ== - -"@dhis2/ui@^8.0.0", "@dhis2/ui@^8.4.11": - version "8.4.11" - resolved "https://registry.yarnpkg.com/@dhis2/ui/-/ui-8.4.11.tgz#41241f8ae450bd3ba3c7e6f7e72fe08e748cac48" - integrity sha512-WTabBRO3yp54pxR/xhy7KKgpyYMAglbiBxq6GWykNYIVeSYA/dwerXEqv+gZEm4vU0AzDt9O7N45XD/SYfKomg== - dependencies: - "@dhis2-ui/alert" "8.4.11" - "@dhis2-ui/box" "8.4.11" - "@dhis2-ui/button" "8.4.11" - "@dhis2-ui/card" "8.4.11" - "@dhis2-ui/center" "8.4.11" - "@dhis2-ui/checkbox" "8.4.11" - "@dhis2-ui/chip" "8.4.11" - "@dhis2-ui/cover" "8.4.11" - "@dhis2-ui/css" "8.4.11" - "@dhis2-ui/divider" "8.4.11" - "@dhis2-ui/field" "8.4.11" - "@dhis2-ui/file-input" "8.4.11" - "@dhis2-ui/header-bar" "8.4.11" - "@dhis2-ui/help" "8.4.11" - "@dhis2-ui/input" "8.4.11" - "@dhis2-ui/intersection-detector" "8.4.11" - "@dhis2-ui/label" "8.4.11" - "@dhis2-ui/layer" "8.4.11" - "@dhis2-ui/legend" "8.4.11" - "@dhis2-ui/loader" "8.4.11" - "@dhis2-ui/logo" "8.4.11" - "@dhis2-ui/menu" "8.4.11" - "@dhis2-ui/modal" "8.4.11" - "@dhis2-ui/node" "8.4.11" - "@dhis2-ui/notice-box" "8.4.11" - "@dhis2-ui/organisation-unit-tree" "8.4.11" - "@dhis2-ui/pagination" "8.4.11" - "@dhis2-ui/popover" "8.4.11" - "@dhis2-ui/popper" "8.4.11" - "@dhis2-ui/portal" "8.4.11" - "@dhis2-ui/radio" "8.4.11" - "@dhis2-ui/required" "8.4.11" - "@dhis2-ui/segmented-control" "8.4.11" - "@dhis2-ui/select" "8.4.11" - "@dhis2-ui/selector-bar" "8.4.11" - "@dhis2-ui/sharing-dialog" "8.4.11" - "@dhis2-ui/switch" "8.4.11" - "@dhis2-ui/tab" "8.4.11" - "@dhis2-ui/table" "8.4.11" - "@dhis2-ui/tag" "8.4.11" - "@dhis2-ui/text-area" "8.4.11" - "@dhis2-ui/tooltip" "8.4.11" - "@dhis2-ui/transfer" "8.4.11" - "@dhis2-ui/user-avatar" "8.4.11" - "@dhis2/ui-constants" "8.4.11" - "@dhis2/ui-forms" "8.4.11" - "@dhis2/ui-icons" "8.4.11" +"@dhis2/ui-icons@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2/ui-icons/-/ui-icons-9.2.0.tgz#bddf5223cabec93c9499281b2181eb165346b639" + integrity sha512-g9993UGWVLwDcbV+wp3HqrK8AXFu49aped0GpZsQUlGbHIzEl1EgmjiII44N40VbXwVUnqIDmu99wBxpH5Gd+g== + +"@dhis2/ui@^8.0.0", "@dhis2/ui@^9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@dhis2/ui/-/ui-9.2.0.tgz#33d474cbc7cd95f8a714e019d6c69e144f77f86b" + integrity sha512-nhKwW5bmIfQvt3L16PffFO2NsDk9BgYb91vHx06fPgM56UdwGYSejpax8eU29vE9urmHSkijSpnBqY4buZy6Ow== + dependencies: + "@dhis2-ui/alert" "9.2.0" + "@dhis2-ui/box" "9.2.0" + "@dhis2-ui/button" "9.2.0" + "@dhis2-ui/calendar" "9.2.0" + "@dhis2-ui/card" "9.2.0" + "@dhis2-ui/center" "9.2.0" + "@dhis2-ui/checkbox" "9.2.0" + "@dhis2-ui/chip" "9.2.0" + "@dhis2-ui/cover" "9.2.0" + "@dhis2-ui/css" "9.2.0" + "@dhis2-ui/divider" "9.2.0" + "@dhis2-ui/field" "9.2.0" + "@dhis2-ui/file-input" "9.2.0" + "@dhis2-ui/header-bar" "9.2.0" + "@dhis2-ui/help" "9.2.0" + "@dhis2-ui/input" "9.2.0" + "@dhis2-ui/intersection-detector" "9.2.0" + "@dhis2-ui/label" "9.2.0" + "@dhis2-ui/layer" "9.2.0" + "@dhis2-ui/legend" "9.2.0" + "@dhis2-ui/loader" "9.2.0" + "@dhis2-ui/logo" "9.2.0" + "@dhis2-ui/menu" "9.2.0" + "@dhis2-ui/modal" "9.2.0" + "@dhis2-ui/node" "9.2.0" + "@dhis2-ui/notice-box" "9.2.0" + "@dhis2-ui/organisation-unit-tree" "9.2.0" + "@dhis2-ui/pagination" "9.2.0" + "@dhis2-ui/popover" "9.2.0" + "@dhis2-ui/popper" "9.2.0" + "@dhis2-ui/portal" "9.2.0" + "@dhis2-ui/radio" "9.2.0" + "@dhis2-ui/required" "9.2.0" + "@dhis2-ui/segmented-control" "9.2.0" + "@dhis2-ui/select" "9.2.0" + "@dhis2-ui/selector-bar" "9.2.0" + "@dhis2-ui/sharing-dialog" "9.2.0" + "@dhis2-ui/switch" "9.2.0" + "@dhis2-ui/tab" "9.2.0" + "@dhis2-ui/table" "9.2.0" + "@dhis2-ui/tag" "9.2.0" + "@dhis2-ui/text-area" "9.2.0" + "@dhis2-ui/tooltip" "9.2.0" + "@dhis2-ui/transfer" "9.2.0" + "@dhis2-ui/user-avatar" "9.2.0" + "@dhis2/ui-constants" "9.2.0" + "@dhis2/ui-forms" "9.2.0" + "@dhis2/ui-icons" "9.2.0" prop-types "^15.7.2" "@discoveryjs/json-ext@^0.5.3": From e1ebe372a1063f4fd1beff1d1886444c93a7512b Mon Sep 17 00:00:00 2001 From: Edoardo Sabadelli Date: Wed, 20 Dec 2023 15:05:42 +0100 Subject: [PATCH 2/6] feat: support outlierDetection endpoint (DHIS2-16345) --- src/__fixtures__/fixtures.js | 4 + .../json/api/analytics/outlierDetection.json | 213 ++++++++++++++++++ src/api/analytics/AnalyticsAggregate.js | 28 ++- src/api/analytics/AnalyticsBase.js | 18 +- src/api/analytics/AnalyticsResponse.js | 93 ++++---- 5 files changed, 304 insertions(+), 52 deletions(-) create mode 100644 src/__fixtures__/json/api/analytics/outlierDetection.json diff --git a/src/__fixtures__/fixtures.js b/src/__fixtures__/fixtures.js index 4fab49688..b900ea724 100644 --- a/src/__fixtures__/fixtures.js +++ b/src/__fixtures__/fixtures.js @@ -114,6 +114,10 @@ export default (function x() { require('./json/api/analytics/enrollments.json') ) + addFixture( + '/api/analytics/outlierDetection', + require('./json/api/analytics/outlierDetection.json') + ) return { get: getFixture, add: addFixture, diff --git a/src/__fixtures__/json/api/analytics/outlierDetection.json b/src/__fixtures__/json/api/analytics/outlierDetection.json new file mode 100644 index 000000000..014ac1183 --- /dev/null +++ b/src/__fixtures__/json/api/analytics/outlierDetection.json @@ -0,0 +1,213 @@ +{ + "headers": [ + { + "name": "dxname", + "column": "Data name", + "valueType": "TEXT", + "type": "java.lang.String", + "hidden": false, + "meta": false + }, + { + "name": "ouname", + "column": "Organisation unit name", + "valueType": "TEXT", + "type": "java.lang.String", + "hidden": false, + "meta": false + }, + { + "name": "value", + "column": "Value", + "valueType": "NUMBER", + "type": "java.lang.Double", + "hidden": false, + "meta": false + }, + { + "name": "absdev", + "column": "Absolute deviation", + "valueType": "NUMBER", + "type": "java.lang.Double", + "hidden": false, + "meta": false + }, + { + "name": "modifiedzscore", + "column": "Modified zScore", + "valueType": "NUMBER", + "type": "java.lang.Double", + "hidden": false, + "meta": false + }, + { + "name": "median", + "column": "Median", + "valueType": "NUMBER", + "type": "java.lang.Double", + "hidden": false, + "meta": false + }, + { + "name": "lowerbound", + "column": "Lower boundary", + "valueType": "NUMBER", + "type": "java.lang.Double", + "hidden": false, + "meta": false + }, + { + "name": "upperbound", + "column": "Upper boundary", + "valueType": "NUMBER", + "type": "java.lang.Double", + "hidden": false, + "meta": false + } + ], + "metaData": { + "maxResults": 100, + "count": 13, + "orderBy": "mean_abs_dev", + "threshold": 3.0, + "algorithm": "MOD_Z_SCORE" + }, + "rowContext": { + + }, + "rows": [ + [ + "ANC 2nd visit", + "UMC (Urban Centre) Hospital", + "1669.0", + "920.0", + "3.902767295597484", + "749.0", + "-290.93013894085743", + "1788.9301389408574" + ], + [ + "ANC 1st visit", + "Charlotte CHP", + "49.0", + "39.0", + "7.515857142857143", + "10.0", + "-23.948490393535913", + "43.94849039353591" + ], + [ + "ANC 2nd visit", + "Charlotte CHP", + "40.0", + "33.0", + "22.258499999999998", + "7.0", + "-30.86489139031037", + "44.86489139031037" + ], + [ + "ANC 2nd visit", + "Wilberforce CHC", + "56.0", + "24.5", + "3.004590909090909", + "31.5", + "-1.3890179239210454", + "64.38901792392105" + ], + [ + "ANC 1st visit", + "Deep Eye water MCHP", + "40.0", + "16.0", + "3.5973333333333333", + "24.0", + "4.850913859925324", + "43.14908614007467" + ], + [ + "ANC 2nd visit", + "Lion for Lion Clinic", + "30.0", + "16.0", + "3.5973333333333333", + "14.0", + "-5.620896815302167", + "33.62089681530217" + ], + [ + "ANC 2nd visit", + "Deep Eye water MCHP", + "33.0", + "14.5", + "3.9121", + "18.5", + "-3.4245866551686603", + "40.42458665516866" + ], + [ + "ANC 2nd visit", + "Blessed Mokaka East Clinic", + "2.0", + "13.0", + "4.38425", + "15.0", + "-3.417043736713012", + "33.41704373671301" + ], + [ + "ANC 2nd visit", + "Malambay CHP", + "15.0", + "12.0", + "5.396", + "3.0", + "-13.770509831248425", + "19.770509831248425" + ], + [ + "ANC 2nd visit", + "Wellbody MCHP", + "20.0", + "10.0", + "3.3725", + "10.0", + "-10.999999999999996", + "30.999999999999996" + ], + [ + "ANC 1st visit", + "Blessed Mokaka East Clinic", + "26.0", + "10.0", + "4.496666666666667", + "16.0", + "1.6734512181055958", + "30.326548781894402" + ], + [ + "ANC 1st visit", + "Murray Town CHC", + "18.0", + "9.0", + "6.0705", + "9.0", + "-2.43571205496543", + "20.43571205496543" + ], + [ + "ANC 2nd visit", + "Thompson Bay MCHP", + "11.0", + "6.0", + "4.047", + "5.0", + "-2.038266127580332", + "12.038266127580332" + ] + ], + "headerWidth": 8, + "width": 8, + "height": 13 +} diff --git a/src/api/analytics/AnalyticsAggregate.js b/src/api/analytics/AnalyticsAggregate.js index 88adebc81..93de9f8b4 100644 --- a/src/api/analytics/AnalyticsAggregate.js +++ b/src/api/analytics/AnalyticsAggregate.js @@ -68,12 +68,38 @@ class AnalyticsAggregate extends AnalyticsBase { * .withStartDate('2017-10-01') * .withEndDate('2017-10-31'); * - * analytics.aggregate.getDebugSql(req); + * analytics.aggregate.getDebugSql(req) * .then(console.log); */ getDebugSql(req) { return this.fetch(req.withPath('debug/sql')) } + + /** + * @param {!AnalyticsRequest} req Request object + * + * @returns {Promise} Promise that resolves with the SQL statement used to query the database. + * + * @example + * const req = new analytics.request() + * .withParameters({ + * dx: 'fbfJHSPpUQD,cYeuwXTCPkU', + * pe: 'THIS_YEAR', + * ou: 'USER_ORGUNIT,USER_ORGUNIT_CHILDREN', + * headers: 'dxname,pename,ouname,value,absdev,modifiedzscore,median,lowerbound,upperbound', + * algorithm: 'MODIFIED_Z_SCORE', + * maxResults: 100, + * threshold: 3, + orderBy: 'value', + sortOrder: 'desc', + * }); + * + * analytics.aggregate.getOutliersData(req) + * .then(console.log); + */ + getOutliersData(req) { + return this.fetch(req.withPath('outlierDetection')) + } } export default AnalyticsAggregate diff --git a/src/api/analytics/AnalyticsBase.js b/src/api/analytics/AnalyticsBase.js index 41b2fcf62..2d692491a 100644 --- a/src/api/analytics/AnalyticsBase.js +++ b/src/api/analytics/AnalyticsBase.js @@ -7,8 +7,8 @@ const analyticsQuery = { return [path, program].filter(Boolean).join('/') }, params: ({ dimensions, filters, parameters }) => ({ - dimension: dimensions, - filter: filters, + dimension: dimensions.length ? dimensions : undefined, + filter: filters.length ? filters : undefined, ...parameters, }), } @@ -20,8 +20,8 @@ const analyticsDataQuery = { }, params: ({ dimensions, filters, parameters }) => { return { - dimension: dimensions, - filter: filters, + dimension: dimensions.length ? dimensions : undefined, + filter: filters.length ? filters : undefined, ...parameters, skipMeta: true, skipData: false, @@ -35,8 +35,8 @@ const analyticsMetaDataQuery = { return [path, program].filter(Boolean).join('/') }, params: ({ dimensions, filters, parameters }) => ({ - dimension: dimensions, - filter: filters, + dimension: dimensions.length ? dimensions : undefined, + filter: filters.length ? filters : undefined, ...parameters, skipMeta: false, skipData: true, @@ -158,17 +158,23 @@ class AnalyticsBase { * // { metaData: { ... }, rows: [], height: 0, width: 0 } */ async fetch(req, options) { + const dimensions = generateDimensionStrings(req.dimensions, options) + const filters = generateDimensionStrings(req.filters, options) + const response = await this.dataEngine.query( { data: analyticsQuery }, { variables: { path: req.path, program: req.program, + dimensions, + filters /* dimensions: generateDimensionStrings( req.dimensions, options ), filters: generateDimensionStrings(req.filters, options), + */, parameters: req.parameters, }, } diff --git a/src/api/analytics/AnalyticsResponse.js b/src/api/analytics/AnalyticsResponse.js index a7bcd8014..a2293378e 100644 --- a/src/api/analytics/AnalyticsResponse.js +++ b/src/api/analytics/AnalyticsResponse.js @@ -81,7 +81,8 @@ class AnalyticsResponse { } extractHeaders() { - const dimensions = this.response.metaData.dimensions + // some endpoints (ie. outlierDetectio) don't return dimensions in metaData + const dimensions = this.response.metaData.dimensions || {} const headers = this.response.headers || [] return headers.map( @@ -125,59 +126,61 @@ class AnalyticsResponse { const { dimensions, items } = metaData - // populate metaData dimensions and items - this.headers - .filter( - (header) => - !DEFAULT_COLLECT_IGNORE_HEADERS.includes(header.name) - ) - .forEach((header) => { - let ids - - // collect row values - if (header.isCollect) { - ids = this.getSortedUniqueRowIdStringsByHeader(header) - dimensions[header.name] = ids - } else { - ids = dimensions[header.name] - } + // some endpoints (ie. outlierDetection) don't return dimensions or items + if (dimensions && items) { + this.headers + .filter( + (header) => + !DEFAULT_COLLECT_IGNORE_HEADERS.includes(header.name) + ) + .forEach((header) => { + let ids + + // collect row values + if (header.isCollect) { + ids = this.getSortedUniqueRowIdStringsByHeader(header) + dimensions[header.name] = ids + } else { + ids = dimensions[header.name] + } - if (header.isPrefix) { - // create prefixed dimensions array - dimensions[header.name] = ids.map((id) => - getPrefixedId(id, header.name) - ) + if (header.isPrefix) { + // create prefixed dimensions array + dimensions[header.name] = ids.map((id) => + getPrefixedId(id, header.name) + ) - // create items - dimensions[header.name].forEach((prefixedId, index) => { - const id = ids[index] - const valueType = header.valueType + // create items + dimensions[header.name].forEach((prefixedId, index) => { + const id = ids[index] + const valueType = header.valueType - const name = getNameByIdsByValueType(id, valueType) + const name = getNameByIdsByValueType(id, valueType) - items[prefixedId] = { name } - }) - } - }) + items[prefixedId] = { name } + }) + } + }) - // for events, add items from 'ouname' - if (this.hasHeader(OUNAME) && this.hasHeader(OU)) { - const ouNameHeaderIndex = this.getHeader(OUNAME).getIndex() - const ouHeaderIndex = this.getHeader(OU).getIndex() - let ouId - let ouName + // for events, add items from 'ouname' + if (this.hasHeader(OUNAME) && this.hasHeader(OU)) { + const ouNameHeaderIndex = this.getHeader(OUNAME).getIndex() + const ouHeaderIndex = this.getHeader(OU).getIndex() + let ouId + let ouName - this.rows.forEach((row) => { - ouId = row[ouHeaderIndex] + this.rows.forEach((row) => { + ouId = row[ouHeaderIndex] - if (items[ouId] === undefined) { - ouName = row[ouNameHeaderIndex] + if (items[ouId] === undefined) { + ouName = row[ouNameHeaderIndex] - items[ouId] = { - name: ouName, + items[ouId] = { + name: ouName, + } } - } - }) + }) + } } return metaData From 213c5ab2a6012f2dd270805dba94cfc701712ad4 Mon Sep 17 00:00:00 2001 From: Edoardo Sabadelli Date: Wed, 20 Dec 2023 15:08:08 +0100 Subject: [PATCH 3/6] chore: cleanup code and unit tests --- src/api/analytics/AnalyticsBase.js | 8 +--- src/api/analytics/AnalyticsRequestBase.js | 18 +++++---- src/api/analytics/AnalyticsResponse.js | 2 +- src/api/analytics/__tests__/Analytics.spec.js | 6 +++ .../__tests__/AnalyticsAggregate.spec.js | 40 +++++++++++++++++++ .../analytics/__tests__/AnalyticsBase.spec.js | 35 +++++++++++++++- 6 files changed, 93 insertions(+), 16 deletions(-) diff --git a/src/api/analytics/AnalyticsBase.js b/src/api/analytics/AnalyticsBase.js index 2d692491a..70d2c2b5b 100644 --- a/src/api/analytics/AnalyticsBase.js +++ b/src/api/analytics/AnalyticsBase.js @@ -44,7 +44,7 @@ const analyticsMetaDataQuery = { }), } -const generateDimensionStrings = (dimensions = [], options) => { +export const generateDimensionStrings = (dimensions = [], options) => { if (options && options.sorted) { dimensions = sortBy(dimensions, 'dimension') } @@ -158,23 +158,17 @@ class AnalyticsBase { * // { metaData: { ... }, rows: [], height: 0, width: 0 } */ async fetch(req, options) { - const dimensions = generateDimensionStrings(req.dimensions, options) - const filters = generateDimensionStrings(req.filters, options) - const response = await this.dataEngine.query( { data: analyticsQuery }, { variables: { path: req.path, program: req.program, - dimensions, - filters /* dimensions: generateDimensionStrings( req.dimensions, options ), filters: generateDimensionStrings(req.filters, options), - */, parameters: req.parameters, }, } diff --git a/src/api/analytics/AnalyticsRequestBase.js b/src/api/analytics/AnalyticsRequestBase.js index 780d3e6cb..2bc24b108 100644 --- a/src/api/analytics/AnalyticsRequestBase.js +++ b/src/api/analytics/AnalyticsRequestBase.js @@ -46,14 +46,14 @@ class AnalyticsRequestBase { // at least 1 dimension is required let dimensions = this.dimensions - if (options && options.sorted) { + if (dimensions.length && options?.sorted) { dimensions = sortBy(dimensions, 'dimension') } const encodedDimensions = dimensions.map(({ dimension, items }) => { if (Array.isArray(items) && items.length) { const encodedItems = items.map(customEncodeURIComponent) - if (options && options.sorted) { + if (options?.sorted) { encodedItems.sort() } @@ -67,9 +67,13 @@ class AnalyticsRequestBase { .filter((e) => !!e) .join('/') - return `${endPoint}.${this.format}?dimension=${encodedDimensions.join( - '&dimension=' - )}` + let url = `${endPoint}.${this.format}` + + if (encodedDimensions.length) { + url += `?dimension=${encodedDimensions.join('&dimension=')}` + } + + return url } /** @@ -88,14 +92,14 @@ class AnalyticsRequestBase { buildQuery(options) { let filters = this.filters - if (options && options.sorted) { + if (filters.length && options?.sorted) { filters = sortBy(filters, 'dimension') } const encodedFilters = filters.map(({ dimension, items }) => { if (Array.isArray(items) && items.length) { const encodedItems = items.map(customEncodeURIComponent) - if (options && options.sorted) { + if (options?.sorted) { encodedItems.sort() } diff --git a/src/api/analytics/AnalyticsResponse.js b/src/api/analytics/AnalyticsResponse.js index a2293378e..525393de6 100644 --- a/src/api/analytics/AnalyticsResponse.js +++ b/src/api/analytics/AnalyticsResponse.js @@ -81,7 +81,7 @@ class AnalyticsResponse { } extractHeaders() { - // some endpoints (ie. outlierDetectio) don't return dimensions in metaData + // some endpoints (ie. outlierDetection) don't return dimensions in metaData const dimensions = this.response.metaData.dimensions || {} const headers = this.response.headers || [] diff --git a/src/api/analytics/__tests__/Analytics.spec.js b/src/api/analytics/__tests__/Analytics.spec.js index 4add9c1b1..17d88344c 100644 --- a/src/api/analytics/__tests__/Analytics.spec.js +++ b/src/api/analytics/__tests__/Analytics.spec.js @@ -1,5 +1,6 @@ import Analytics from '../Analytics.js' import AnalyticsAggregate from '../AnalyticsAggregate.js' +import AnalyticsEnrollments from '../AnalyticsEnrollments.js' import AnalyticsEvents from '../AnalyticsEvents.js' import AnalyticsRequest from '../AnalyticsRequest.js' @@ -9,6 +10,7 @@ describe('Analytics', () => { beforeEach(() => { analytics = new Analytics({ aggregate: new AnalyticsAggregate(), + enrollments: new AnalyticsEnrollments(), events: new AnalyticsEvents(), request: AnalyticsRequest, }) @@ -26,6 +28,10 @@ describe('Analytics', () => { expect(analytics.aggregate).toBeInstanceOf(AnalyticsAggregate) }) + it('should contain an instance of AnalyticsEnrollments', () => { + expect(analytics.enrollments).toBeInstanceOf(AnalyticsEnrollments) + }) + it('should contain an instance of AnalyticsEvents', () => { expect(analytics.events).toBeInstanceOf(AnalyticsEvents) }) diff --git a/src/api/analytics/__tests__/AnalyticsAggregate.spec.js b/src/api/analytics/__tests__/AnalyticsAggregate.spec.js index f7fecba11..4a5d40d54 100644 --- a/src/api/analytics/__tests__/AnalyticsAggregate.spec.js +++ b/src/api/analytics/__tests__/AnalyticsAggregate.spec.js @@ -133,4 +133,44 @@ describe('Analytics.aggregate', () => { expect(data.height).toEqual(0) })) }) + + describe('.getOutliersData', () => { + beforeEach(() => { + aggregate = new AnalyticsAggregate(new DataEngineMock()) + + request = new AnalyticsRequest() + + request.withParameters({ + dx: 'fbfJHSPpUQD,cYeuwXTCPkU', + pe: 'THIS_YEAR', + ou: 'at6UHUQatSo', + headers: + 'dxname,pename,ouname,value,absdev,modifiedzscore,median,lowerbound,upperbound', + algorithm: 'MOD_Z_SCORE', + maxResults: 100, + threshold: 3, + }) + + fixture = fixtures.get('/api/analytics/outlierDetection') + + dataEngineMock.query.mockReturnValue( + Promise.resolve({ data: fixture }) + ) + }) + + it('should be a function', () => { + expect(aggregate.getOutliersData).toBeInstanceOf(Function) + }) + + it('should resolve a promise with data', () => + aggregate.getOutliersData(request).then((data) => { + expect(data.metaData.items).toEqual(fixture.metaData.items) + expect(data.metaData.dimensions).toEqual( + fixture.metaData.dimensions + ) + expect(data.headers).toEqual(fixture.headers) + expect(data.width).toEqual(8) + expect(data.height).toEqual(13) + })) + }) }) diff --git a/src/api/analytics/__tests__/AnalyticsBase.spec.js b/src/api/analytics/__tests__/AnalyticsBase.spec.js index 729eebdb7..c5d3fb1d8 100644 --- a/src/api/analytics/__tests__/AnalyticsBase.spec.js +++ b/src/api/analytics/__tests__/AnalyticsBase.spec.js @@ -1,4 +1,4 @@ -import AnalyticsBase from '../AnalyticsBase.js' +import AnalyticsBase, { generateDimensionStrings } from '../AnalyticsBase.js' let base @@ -19,3 +19,36 @@ describe('constructor', () => { expect(base.dataEngine).toBe(dataEngineMock) }) }) + +describe('generateDimensionString', () => { + const tests = [ + { + input: [ + { + dimension: 'dim2', + items: ['item2', 'item1'], + }, + { + dimension: 'dim1', + items: ['item1'], + }, + ], + output: ['dim2:item2;item1', 'dim1:item1'], + outputSorted: ['dim1:item1', 'dim2:item1;item2'], + }, + ] + + it('should return dimension strings correctly formatted', () => { + tests.forEach(({ input, output }) => { + expect(generateDimensionStrings(input)).toEqual(output) + }) + }) + + it('should return dimension strings correctly formatted and sorted', () => { + tests.forEach(({ input, outputSorted }) => { + expect(generateDimensionStrings(input, { sorted: true })).toEqual( + outputSorted + ) + }) + }) +}) From 1940f08b1b0f3cf3ca0bb62d66cb86720d38cfa9 Mon Sep 17 00:00:00 2001 From: Edoardo Sabadelli Date: Thu, 4 Jan 2024 09:21:01 +0100 Subject: [PATCH 4/6] feat: add support for period item max limit (DHIS2-16399) --- i18n/en.pot | 6 +- .../PeriodDimension/PeriodDimension.js | 5 +- .../PeriodDimension/PeriodTransfer.js | 88 ++++++++++++------- .../PeriodDimension.spec.js.snap | 2 +- .../__snapshots__/PeriodSelector.spec.js.snap | 13 +-- src/index.js | 2 + .../layoutUiRules/__tests__/rules.spec.js | 21 +++++ src/modules/layoutUiRules/index.js | 2 + src/modules/layoutUiRules/rules.js | 10 +++ src/modules/layoutUiRules/rulesHelper.js | 4 + src/modules/layoutUiRules/rulesUtils.js | 16 ++++ 11 files changed, 119 insertions(+), 50 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 4bf30c374..1ddb5adb5 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -657,15 +657,15 @@ msgstr "Select year" msgid "Period" msgstr "Period" +msgid "Selected Periods" +msgstr "Selected Periods" + msgid "Relative periods" msgstr "Relative periods" msgid "Fixed periods" msgstr "Fixed periods" -msgid "Selected Periods" -msgstr "Selected Periods" - msgid "No periods selected" msgstr "No periods selected" diff --git a/src/components/PeriodDimension/PeriodDimension.js b/src/components/PeriodDimension/PeriodDimension.js index e60cdf029..7e20dbc6e 100644 --- a/src/components/PeriodDimension/PeriodDimension.js +++ b/src/components/PeriodDimension/PeriodDimension.js @@ -18,6 +18,7 @@ const PeriodDimension = ({ selectedPeriods, rightFooter, excludedPeriodTypes, + infoBoxMessage, }) => { const { systemInfo } = useConfig() const result = useDataQuery(userSettingsQuery) @@ -36,7 +37,8 @@ const PeriodDimension = ({ return ( ( + <> +

{i18n.t('Selected Periods')}

+ {infoText && ( +
+
+ +
+ {infoText} +
+ )} + + +) + +RightHeader.propTypes = { + infoText: PropTypes.string, +} + const PeriodTransfer = ({ onSelect, dataTest, - initialSelectedPeriods, + selectedItems, rightFooter, excludedPeriodTypes, periodsSettings, + infoBoxMessage, }) => { const defaultRelativePeriodType = excludedPeriodTypes.includes(MONTHLY) ? getRelativePeriodsOptionsById(QUARTERLY) @@ -46,9 +66,6 @@ const PeriodTransfer = ({ const [allPeriods, setAllPeriods] = useState( defaultRelativePeriodType.getPeriods() ) - const [selectedPeriods, setSelectedPeriods] = useState( - initialSelectedPeriods - ) const [isRelative, setIsRelative] = useState(true) const [relativeFilter, setRelativeFilter] = useState({ periodType: defaultRelativePeriodType.id, @@ -58,6 +75,11 @@ const PeriodTransfer = ({ year: defaultFixedPeriodYear.toString(), }) + const isActive = (value) => { + const item = selectedItems.find((item) => item.id === value) + return !item || item.isActive + } + const onIsRelativeClick = (state) => { if (state !== isRelative) { setIsRelative(state) @@ -132,13 +154,6 @@ const PeriodTransfer = ({ ) - const renderRightHeader = () => ( - <> -

{i18n.t('Selected Periods')}

- - - ) - const onSelectFixedPeriods = (filter) => { setFixedFilter(filter) setAllPeriods( @@ -162,35 +177,40 @@ const PeriodTransfer = ({ return ( { - const formattedItems = selected.map((id) => ({ - id, - name: [...allPeriods, ...selectedPeriods].find( + const formattedItems = selected.map((id) => { + const matchingItem = [...allPeriods, ...selectedItems].find( (item) => item.id === id - ).name, - })) - setSelectedPeriods(formattedItems) + ) + + return { + id, + name: matchingItem.name, + isActive: matchingItem.isActive, + } + }) onSelect(formattedItems) }} - selected={selectedPeriods.map((period) => period.id)} + selected={selectedItems.map((period) => period.id)} leftHeader={renderLeftHeader()} enableOrderChange height={TRANSFER_HEIGHT} optionsWidth={TRANSFER_OPTIONS_WIDTH} selectedWidth={TRANSFER_SELECTED_WIDTH} selectedEmptyComponent={renderEmptySelection()} - rightHeader={renderRightHeader()} + rightHeader={} rightFooter={rightFooter} - options={[...allPeriods, ...selectedPeriods].map( - ({ id, name }) => ({ - label: name, - value: id, - }) - )} + options={[...allPeriods, ...selectedItems].map(({ id, name }) => ({ + label: name, + value: id, + }))} renderOption={(props) => ( )} dataTest={`${dataTest}-transfer`} @@ -199,7 +219,7 @@ const PeriodTransfer = ({ } PeriodTransfer.defaultProps = { - initialSelectedPeriods: [], + selectedItems: [], excludedPeriodTypes: [], periodsSettings: { calendar: 'gregory', @@ -211,17 +231,19 @@ PeriodTransfer.propTypes = { onSelect: PropTypes.func.isRequired, dataTest: PropTypes.string, excludedPeriodTypes: PropTypes.arrayOf(PropTypes.string), - initialSelectedPeriods: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.string, - name: PropTypes.string, - }) - ), + infoBoxMessage: PropTypes.string, periodsSettings: PropTypes.shape({ calendar: PropTypes.string, locale: PropTypes.string, }), rightFooter: PropTypes.node, + selectedItems: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.string, + isActive: PropTypes.bool, + name: PropTypes.string, + }) + ), } export default PeriodTransfer diff --git a/src/components/PeriodDimension/__tests__/__snapshots__/PeriodDimension.spec.js.snap b/src/components/PeriodDimension/__tests__/__snapshots__/PeriodDimension.spec.js.snap index 1bf93ba94..4ad9ab073 100644 --- a/src/components/PeriodDimension/__tests__/__snapshots__/PeriodDimension.spec.js.snap +++ b/src/components/PeriodDimension/__tests__/__snapshots__/PeriodDimension.spec.js.snap @@ -4,7 +4,6 @@ exports[`The Period Dimension component matches the snapshot 1`] = ` } + selectedItems={Array []} /> `; diff --git a/src/components/PeriodDimension/__tests__/__snapshots__/PeriodSelector.spec.js.snap b/src/components/PeriodDimension/__tests__/__snapshots__/PeriodSelector.spec.js.snap index fa5dbe8d4..d13930874 100644 --- a/src/components/PeriodDimension/__tests__/__snapshots__/PeriodSelector.spec.js.snap +++ b/src/components/PeriodDimension/__tests__/__snapshots__/PeriodSelector.spec.js.snap @@ -77,18 +77,7 @@ exports[`The Period Selector component matches the snapshot 1`] = ` optionsWidth="420px" renderOption={[Function]} rightFooter={} - rightHeader={ - -

- Selected Periods -

- -
- } + rightHeader={} selected={Array []} selectedEmptyComponent={ diff --git a/src/index.js b/src/index.js index 07e1f9cbe..307f898d2 100644 --- a/src/index.js +++ b/src/index.js @@ -238,10 +238,12 @@ export { export { getAvailableAxes, getDisallowedDimensions, + getDimensionMaxNumberOfItems, getAxisMaxNumberOfItems, getAxisMaxNumberOfDimensions, getAxisMinNumberOfDimensions, hasAxisTooManyItems, + hasDimensionTooManyItems, getAxisPerLockedDimension, getAllLockedDimensionIds, canDimensionBeAddedToAxis, diff --git a/src/modules/layoutUiRules/__tests__/rules.spec.js b/src/modules/layoutUiRules/__tests__/rules.spec.js index 0b4598e91..79565e748 100644 --- a/src/modules/layoutUiRules/__tests__/rules.spec.js +++ b/src/modules/layoutUiRules/__tests__/rules.spec.js @@ -25,6 +25,9 @@ const allArrayItemsAreValid = (allItems, validItems) => const allArrayItemsAreValidAxisIds = (array) => allArrayItemsAreValid(array, ALL_AXIS_IDS) +const allArrayItemsAreValidDimensionIds = (array) => + allArrayItemsAreValid(array, lockableDims) + const onlyRulesWithProp = (ruleProp) => testResourceRules.filter((rule) => rule[ruleProp]) @@ -49,6 +52,15 @@ const testPropIsArray = (ruleProp) => ).toBe(true) }) +const testKeysAreValidDimensionIds = (ruleProp) => + it('keys should be valid dimension ids', () => { + expect( + onlyRulesWithProp(ruleProp).every((rule) => + allArrayItemsAreValidDimensionIds(Object.keys(rule[ruleProp])) + ) + ).toBe(true) + }) + const testKeysAreValidAxisIds = (ruleProp) => it('keys should be valid axis ids', () => { expect( @@ -127,6 +139,15 @@ describe("verify each rule's ", () => { }) }) + describe('MAX_ITEMS_PER_DIMENSION', () => { + const ruleProp = testResourceAllRuleProps['MAX_ITEMS_PER_DIMENSION'] + + testPropHasKeysAndValues(ruleProp) + testKeysAreValidDimensionIds(ruleProp) + testNoValuesZero(ruleProp) + testNoValuesNegative(ruleProp) + }) + describe('AVAILABLE_AXES', () => { const ruleProp = testResourceAllRuleProps['AVAILABLE_AXES'] diff --git a/src/modules/layoutUiRules/index.js b/src/modules/layoutUiRules/index.js index 3d860c669..ceeb78759 100644 --- a/src/modules/layoutUiRules/index.js +++ b/src/modules/layoutUiRules/index.js @@ -6,10 +6,12 @@ export { getAxisMinNumberOfDimsByVisType as getAxisMinNumberOfDimensions, getAxisPerLockedDimByVisType as getAxisPerLockedDimension, getAllLockedDimIdsByVisType as getAllLockedDimensionIds, + getDimensionMaxNumberOfItemsByVisType as getDimensionMaxNumberOfItems, } from './rulesHelper.js' export { hasAxisTooManyItemsByVisType as hasAxisTooManyItems, + hasDimensionTooManyItemsByVisType as hasDimensionTooManyItems, isDimensionLockedByVisType as isDimensionLocked, isAxisFullByVisType as isAxisFull, canDimensionBeAddedToAxisByVisType as canDimensionBeAddedToAxis, diff --git a/src/modules/layoutUiRules/rules.js b/src/modules/layoutUiRules/rules.js index 40f9f7537..addb3dc80 100644 --- a/src/modules/layoutUiRules/rules.js +++ b/src/modules/layoutUiRules/rules.js @@ -7,6 +7,7 @@ import { DIMENSION_ID_PERIOD, DIMENSION_ID_DATA, DIMENSION_ID_ORGUNIT, + DIMENSION_ID_ASSIGNED_CATEGORIES, } from '../predefinedDimensions.js' import { VIS_TYPE_COLUMN, @@ -32,6 +33,7 @@ const RULE_PROP_AVAILABLE_AXES = 'availableAxes', RULE_PROP_MAX_DIMS_PER_AXIS = 'maxNumberOfDimsPerAxis', RULE_PROP_MIN_DIMS_PER_AXIS = 'minNumberOfDimsPerAxis', RULE_PROP_MAX_ITEMS_PER_AXIS = 'maxNumberOfItemsPerAxis', + RULE_PROP_MAX_ITEMS_PER_DIMENSION = 'maxNumberOfItemsPerDimension', RULE_PROP_DISALLOWED_DIMS = 'disallowedDims', RULE_PROP_LOCKED_DIMS = 'lockedDims' @@ -135,11 +137,15 @@ const outlierTableRules = { [RULE_PROP_MIN_DIMS_PER_AXIS]: { [AXIS_ID_COLUMNS]: 3, }, + [RULE_PROP_MAX_ITEMS_PER_DIMENSION]: { + [DIMENSION_ID_PERIOD]: 1, + }, [RULE_PROP_LOCKED_DIMS]: { [DIMENSION_ID_DATA]: AXIS_ID_COLUMNS, [DIMENSION_ID_PERIOD]: AXIS_ID_COLUMNS, [DIMENSION_ID_ORGUNIT]: AXIS_ID_COLUMNS, }, + [RULE_PROP_DISALLOWED_DIMS]: [DIMENSION_ID_ASSIGNED_CATEGORIES], } const visTypeToRules = { @@ -183,6 +189,9 @@ export const getMaxNumberOfDimsPerAxisByVisType = (visType) => export const getMinNumberOfDimsPerAxisByVisType = (visType) => getRulesByVisType(visType)[RULE_PROP_MIN_DIMS_PER_AXIS] || {} +export const getMaxNumberOfItemsPerDimensionByVisType = (visType) => + getRulesByVisType(visType)[RULE_PROP_MAX_ITEMS_PER_DIMENSION] || {} + export const getMaxNumberOfItemsPerAxisByVisType = (visType) => getRulesByVisType(visType)[RULE_PROP_MAX_ITEMS_PER_AXIS] || {} @@ -203,6 +212,7 @@ export const testResourceAllRuleProps = { MAX_DIMS_PER_AXIS: RULE_PROP_MAX_DIMS_PER_AXIS, MIN_DIMS_PER_AXIS: RULE_PROP_MIN_DIMS_PER_AXIS, MAX_ITEMS_PER_AXIS: RULE_PROP_MAX_ITEMS_PER_AXIS, + MAX_ITEMS_PER_DIMENSION: RULE_PROP_MAX_ITEMS_PER_DIMENSION, DISALLOWED_DIMS: RULE_PROP_DISALLOWED_DIMS, LOCKED_DIMS: RULE_PROP_LOCKED_DIMS, } diff --git a/src/modules/layoutUiRules/rulesHelper.js b/src/modules/layoutUiRules/rulesHelper.js index d0f85d90e..8d0a86bd9 100644 --- a/src/modules/layoutUiRules/rulesHelper.js +++ b/src/modules/layoutUiRules/rulesHelper.js @@ -1,4 +1,5 @@ import { + getMaxNumberOfItemsPerDimensionByVisType, getMaxNumberOfItemsPerAxisByVisType, getMaxNumberOfDimsPerAxisByVisType, getMinNumberOfDimsPerAxisByVisType, @@ -26,3 +27,6 @@ export const getAxisPerLockedDimByVisType = (visType, dimensionId) => export const getAllLockedDimIdsByVisType = (visType) => Object.keys(getLockedDimsByVisType(visType)) + +export const getDimensionMaxNumberOfItemsByVisType = (visType, dimensionId) => + getMaxNumberOfItemsPerDimensionByVisType(visType)[dimensionId] diff --git a/src/modules/layoutUiRules/rulesUtils.js b/src/modules/layoutUiRules/rulesUtils.js index 4f95f2831..be75b1e64 100644 --- a/src/modules/layoutUiRules/rulesUtils.js +++ b/src/modules/layoutUiRules/rulesUtils.js @@ -2,6 +2,7 @@ import { getLockedDimsByVisType } from './rules.js' import { getAxisMaxNumberOfDimsByVisType, getAxisMaxNumberOfItemsByVisType, + getDimensionMaxNumberOfItemsByVisType, getAllLockedDimIdsByVisType, } from './rulesHelper.js' @@ -20,6 +21,21 @@ export const hasAxisTooManyItemsByVisType = ( : false } +export const hasDimensionTooManyItemsByVisType = ( + visType, + dimensionId, + numberOfItems +) => { + const maxNumberOfItemsPerDimension = getDimensionMaxNumberOfItemsByVisType( + visType, + dimensionId + ) + + return maxNumberOfItemsPerDimension + ? numberOfItems > maxNumberOfItemsPerDimension + : false +} + export const isDimensionLockedByVisType = (visType, dimensionId) => getAllLockedDimIdsByVisType(visType).includes(dimensionId) From 81536d68cd6c4db3697bc05742241eebcb03a53a Mon Sep 17 00:00:00 2001 From: Edoardo Sabadelli Date: Thu, 25 Jan 2024 14:40:01 +0100 Subject: [PATCH 5/6] feat: allow pass data types in DataDimension (DHIS2-16433) --- i18n/en.pot | 10 +- src/components/DataDimension/DataDimension.js | 80 ++++++--- .../DataDimension/DataTypeSelector.js | 82 ++++++---- src/components/DataDimension/GroupSelector.js | 24 +-- src/components/DataDimension/ItemSelector.js | 154 +++++++++++------- src/index.js | 1 + 6 files changed, 226 insertions(+), 125 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 1ddb5adb5..b5b192be5 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -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: 2024-01-17T09:19:49.801Z\n" -"PO-Revision-Date: 2024-01-17T09:19:49.802Z\n" +"POT-Creation-Date: 2024-01-25T12:05:03.360Z\n" +"PO-Revision-Date: 2024-01-25T12:05:03.360Z\n" msgid "view only" msgstr "view only" @@ -183,6 +183,9 @@ msgstr "No event data items found" msgid "No program indicators found" msgstr "No program indicators found" +msgid "No calculations found" +msgstr "No calculations found" + msgid "No indicators found for \"{{- searchTerm}}\"" msgstr "No indicators found for \"{{- searchTerm}}\"" @@ -195,6 +198,9 @@ msgstr "No event data items found for \"{{- searchTerm}}\"" msgid "No program indicators found for \"{{- searchTerm}}\"" msgstr "No program indicators found for \"{{- searchTerm}}\"" +msgid "No calculations found for \"{{- searchTerm}}\"" +msgstr "No calculations found for \"{{- searchTerm}}\"" + msgid "Nothing found for \"{{- searchTerm}}\"" msgstr "Nothing found for \"{{- searchTerm}}\"" diff --git a/src/components/DataDimension/DataDimension.js b/src/components/DataDimension/DataDimension.js index 52f2c33ea..cc8932eef 100644 --- a/src/components/DataDimension/DataDimension.js +++ b/src/components/DataDimension/DataDimension.js @@ -1,21 +1,46 @@ import { useConfig } from '@dhis2/app-runtime' import PropTypes from 'prop-types' -import React from 'react' +import React, { + createContext, + useContext, + useCallback, + useEffect, + useState, +} from 'react' +import { + dataTypeMap, + DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM, +} from '../../modules/dataTypes.js' import { DIMENSION_ID_DATA } from '../../modules/predefinedDimensions.js' import ItemSelector from './ItemSelector.js' +const DataDimensionCtx = createContext({}) + const DataDimension = ({ onSelect, selectedDimensions, displayNameProp, + enabledDataTypes, infoBoxMessage, onCalculationSave, + visType, }) => { const { serverVersion } = useConfig() - const supportsEDI = - `${serverVersion.major}.${serverVersion.minor}.${ - serverVersion.patch || 0 - }` >= '2.40.0' + + const filterDataTypesByVersion = useCallback( + (dataTypes) => + dataTypes.filter(({ id }) => + // Calculations only available from 2.40 + id === DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM + ? serverVersion.minor >= 40 + : true + ), + [serverVersion.minor] + ) + + const [dataTypes, setDataTypes] = useState( + filterDataTypesByVersion(enabledDataTypes || Object.values(dataTypeMap)) + ) const onSelectItems = (selectedItem) => onSelect({ @@ -28,23 +53,32 @@ const DataDimension = ({ })), }) + useEffect( + () => + enabledDataTypes && + setDataTypes(filterDataTypesByVersion(enabledDataTypes)), + [enabledDataTypes, filterDataTypesByVersion] + ) + return ( - ({ - value: item.id, - label: item.name, - isActive: item.isActive, - type: item.type, - expression: item.expression, - access: item.access, - }))} - onSelect={onSelectItems} - displayNameProp={displayNameProp} - infoBoxMessage={infoBoxMessage} - dataTest={'data-dimension'} - supportsEDI={supportsEDI} - onEDISave={onCalculationSave} - /> + + ({ + value: item.id, + label: item.name, + isActive: item.isActive, + type: item.type, + expression: item.expression, + access: item.access, + }))} + onSelect={onSelectItems} + displayNameProp={displayNameProp} + infoBoxMessage={infoBoxMessage} + dataTest={'data-dimension'} + dataTypes={dataTypes} + onEDISave={onCalculationSave} + /> + ) } @@ -60,7 +94,9 @@ DataDimension.propTypes = { }) ).isRequired, onSelect: PropTypes.func.isRequired, + enabledDataTypes: PropTypes.array, infoBoxMessage: PropTypes.string, + visType: PropTypes.string, onCalculationSave: PropTypes.func, } @@ -69,4 +105,6 @@ DataDimension.defaultProps = { onSelect: Function.prototype, } +export const useDataDimensionContext = () => useContext(DataDimensionCtx) + export default DataDimension diff --git a/src/components/DataDimension/DataTypeSelector.js b/src/components/DataDimension/DataTypeSelector.js index b5485e9ed..332cb90e1 100644 --- a/src/components/DataDimension/DataTypeSelector.js +++ b/src/components/DataDimension/DataTypeSelector.js @@ -2,40 +2,55 @@ import { SingleSelectField, SingleSelectOption } from '@dhis2/ui' import PropTypes from 'prop-types' import React from 'react' import i18n from '../../locales/index.js' -import { - DIMENSION_TYPE_ALL, - dataTypeMap as dataTypes, - DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM, -} from '../../modules/dataTypes.js' +import { DIMENSION_TYPE_ALL, dataTypeMap } from '../../modules/dataTypes.js' +import { getDisplayNameByVisType } from '../../modules/visTypes.js' +import { useDataDimensionContext } from './DataDimension.js' import styles from './styles/DataTypeSelector.style.js' const DataTypeSelector = ({ currentDataType, + dataTypes, onChange, dataTest, - includeCalculations, -}) => ( -
- onChange(ref.selected)} - dense - > - - {Object.values(dataTypes) - .filter( - (type) => - type.id !== DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM || - includeCalculations - ) - .map((type) => ( +}) => { + const { visType } = useDataDimensionContext() + + return ( +
+ onChange(ref.selected)} + dense + disabled={dataTypes.length === 1} + helpText={ + dataTypes.length === 1 && visType + ? i18n.t( + 'Only {{dataType}} can be used in {{visType}}', + { + dataType: + dataTypeMap[dataTypes[0].id].getName(), + visType: getDisplayNameByVisType(visType), + } + ) + : '' + } + > + {dataTypes.length > 1 && ( + + )} + {dataTypes.map((type) => ( ))} - - -
-) +
+ +
+ ) +} DataTypeSelector.propTypes = { currentDataType: PropTypes.string.isRequired, onChange: PropTypes.func.isRequired, dataTest: PropTypes.string, - includeCalculations: PropTypes.bool, + dataTypes: PropTypes.array, } export default DataTypeSelector diff --git a/src/components/DataDimension/GroupSelector.js b/src/components/DataDimension/GroupSelector.js index 7b6173b42..d20fd866c 100644 --- a/src/components/DataDimension/GroupSelector.js +++ b/src/components/DataDimension/GroupSelector.js @@ -28,20 +28,20 @@ const GroupSelector = ({ const defaultGroup = dataTypes[dataType]?.defaultGroup const subGroupType = dataTypes[dataType]?.subGroup - const fetchGroups = async () => { - setIsLoading(true) - const result = await apiFetchGroups( - dataEngine, - dataType, - displayNameProp - ) - setGroups(result) - setIsLoading(false) - } - useEffect(() => { + const fetchGroups = async () => { + setIsLoading(true) + const result = await apiFetchGroups( + dataEngine, + dataType, + displayNameProp + ) + setGroups(result) + setIsLoading(false) + } + fetchGroups() - }, [dataType]) + }, [dataEngine, dataType, displayNameProp]) return (
diff --git a/src/components/DataDimension/ItemSelector.js b/src/components/DataDimension/ItemSelector.js index 6256be9ba..3f756674a 100644 --- a/src/components/DataDimension/ItemSelector.js +++ b/src/components/DataDimension/ItemSelector.js @@ -6,7 +6,6 @@ import { apiFetchOptions } from '../../api/dimensions.js' import i18n from '../../locales/index.js' import { DATA_SETS_CONSTANTS, REPORTING_RATE } from '../../modules/dataSets.js' import { - dataTypeMap as dataTypes, DIMENSION_TYPE_ALL, DIMENSION_TYPE_DATA_ELEMENT, DIMENSION_TYPE_DATA_SET, @@ -33,14 +32,15 @@ const LeftHeader = ({ searchTerm, setSearchTerm, dataType, + dataTypes, setDataType, group, setGroup, subGroup, setSubGroup, displayNameProp, + dataTest, - supportsEDI, }) => ( <>
@@ -57,20 +57,22 @@ const LeftHeader = ({ currentDataType={dataType} onChange={setDataType} dataTest={`${dataTest}-data-types-select-field`} - includeCalculations={supportsEDI} + dataTypes={dataTypes} /> - {dataTypes[dataType] && - dataType !== DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM && ( - - )} + {![ + DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM, + DIMENSION_TYPE_ALL, + ].includes(dataType) && ( + + )}
@@ -79,6 +81,7 @@ const LeftHeader = ({ LeftHeader.propTypes = { dataTest: PropTypes.string, dataType: PropTypes.string, + dataTypes: PropTypes.array, displayNameProp: PropTypes.string, group: PropTypes.string, searchTerm: PropTypes.string, @@ -87,7 +90,6 @@ LeftHeader.propTypes = { setSearchTerm: PropTypes.func, setSubGroup: PropTypes.func, subGroup: PropTypes.string, - supportsEDI: PropTypes.bool, } const EmptySelection = () => ( @@ -96,6 +98,7 @@ const EmptySelection = () => ( ) + const RightHeader = ({ infoText }) => ( <>

{i18n.t('Selected Items')}

@@ -110,9 +113,11 @@ const RightHeader = ({ infoText }) => ( ) + RightHeader.propTypes = { infoText: PropTypes.string, } + const SourceEmptyPlaceholder = ({ loading, searchTerm, @@ -142,6 +147,9 @@ const SourceEmptyPlaceholder = ({ case DIMENSION_TYPE_PROGRAM_INDICATOR: message = i18n.t('No program indicators found') break + case DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM: + message = i18n.t('No calculations found') + break default: message = i18n.t('No data') break @@ -151,41 +159,43 @@ const SourceEmptyPlaceholder = ({ switch (dataType) { case DIMENSION_TYPE_INDICATOR: message = i18n.t('No indicators found for "{{- searchTerm}}"', { - searchTerm: searchTerm, + searchTerm, }) break case DIMENSION_TYPE_DATA_ELEMENT: message = i18n.t( 'No data elements found for "{{- searchTerm}}"', { - searchTerm: searchTerm, + searchTerm, } ) break case DIMENSION_TYPE_DATA_SET: message = i18n.t('No data sets found for "{{- searchTerm}}"', { - searchTerm: searchTerm, + searchTerm, }) break case DIMENSION_TYPE_EVENT_DATA_ITEM: message = i18n.t( 'No event data items found for "{{- searchTerm}}"', - { - searchTerm: searchTerm, - } + { searchTerm } ) break case DIMENSION_TYPE_PROGRAM_INDICATOR: message = i18n.t( 'No program indicators found for "{{- searchTerm}}"', - { - searchTerm: searchTerm, - } + { searchTerm } + ) + break + case DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM: + message = i18n.t( + 'No calculations found for "{{- searchTerm}}"', + { searchTerm } ) break default: message = i18n.t('Nothing found for "{{- searchTerm}}"', { - searchTerm: searchTerm, + searchTerm, }) break } @@ -218,27 +228,42 @@ const ItemSelector = ({ rightFooter, displayNameProp, infoBoxMessage, + dataTypes, dataTest, - supportsEDI, onEDISave, }) => { const [state, setState] = useState({ searchTerm: '', + dataTypes, filter: { - dataType: DIMENSION_TYPE_ALL, + dataType: + dataTypes.length === 1 ? dataTypes[0].id : DIMENSION_TYPE_ALL, + group: null, + subGroup: + dataTypes.length === 1 && + dataTypes[0].id === DIMENSION_TYPE_DATA_ELEMENT + ? TOTALS + : null, }, options: [], loading: true, nextPage: 1, + supportsEDI: dataTypes + .map(({ id }) => id) + .includes(DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM), }) const [currentCalculation, setCurrentCalculation] = useState() const dataEngine = useDataEngine() const setSearchTerm = (searchTerm) => setState((state) => ({ ...state, searchTerm })) - const setFilter = (filter) => setState((state) => ({ ...state, filter })) const debouncedSearchTerm = useDebounce(state.searchTerm, 500) + const fetchItems = async (page) => { - setState((state) => ({ ...state, loading: true })) + setState((state) => ({ + ...state, + nextPage: page === 1 ? 1 : state.nextPage, + loading: true, + })) const result = await apiFetchOptions({ dataEngine, nameProp: displayNameProp, @@ -314,13 +339,9 @@ const ItemSelector = ({ } useDidUpdateEffect(() => { - setState((state) => ({ - ...state, - loading: true, - nextPage: 1, - })) fetchItems(1) }, [debouncedSearchTerm, state.filter]) + const onChange = (newSelected) => { onSelect( newSelected.map((value) => { @@ -391,6 +412,39 @@ const ItemSelector = ({ onSelect([...selectedItems.filter((item) => item.value !== id)]) } + const onSetGroup = (group) => + setState((state) => ({ + ...state, + nextPage: 1, + filter: { + ...state.filter, + group, + }, + })) + + const onSetSubGroup = (subGroup) => + setState((state) => ({ + ...state, + nextPage: 1, + filter: { + ...state.filter, + subGroup, + }, + })) + + const onSetDataType = (dataType) => + setState((state) => ({ + ...state, + nextPage: 1, + filter: { + ...state.filter, + dataType, + group: null, + subGroup: + dataType === DIMENSION_TYPE_DATA_ELEMENT ? TOTALS : null, + }, + })) + return ( <> { - setFilter({ - ...state.filter, - dataType, - group: null, - subGroup: - dataType === DIMENSION_TYPE_DATA_ELEMENT - ? TOTALS - : null, - }) - }} + setDataType={onSetDataType} group={state.filter.group} - setGroup={(group) => { - setFilter({ ...state.filter, group }) - }} + setGroup={onSetGroup} subGroup={state.filter.subGroup} - setSubGroup={(subGroup) => { - setFilter({ ...state.filter, subGroup }) - }} + setSubGroup={onSetSubGroup} searchTerm={state.searchTerm} setSearchTerm={setSearchTerm} displayNameProp={displayNameProp} dataTest={`${dataTest}-left-header`} - supportsEDI={supportsEDI} /> } leftFooter={ - supportsEDI ? ( + state.supportsEDI ? (