From 1606031008fb8487408bcbe79047a0e3d192e1a9 Mon Sep 17 00:00:00 2001 From: Adel Bensaad Date: Tue, 14 May 2024 07:22:44 +0100 Subject: [PATCH] chore: enhance parallel test execution setup in CI --- .github/workflows/dhis2-verify-app.yml | 15 +- cypress/elements/layout.js | 21 +- cypress/integration/dimensions/data.cy.js | 16 +- cypress/integration/visTypes/scatter.cy.js | 76 +- cypress/support/generateTestMatrix.js | 35 + i18n/ar.po | 19 +- i18n/cs.po | 19 +- i18n/en.pot | 13 +- i18n/es.po | 17 +- i18n/fr.po | 26 +- i18n/id.po | 13 +- i18n/lo.po | 23 +- i18n/nb.po | 17 +- i18n/nl.po | 17 +- package.json | 4 +- .../DimensionsPanel/Dialogs/DialogManager.js | 8 +- .../styles/DndDimensionList.module.css | 3 + .../IconButton/styles/IconButton.module.css | 10 +- src/components/Layout/Chip.js | 116 +-- .../styles/DefaultAxis.module.css | 2 +- src/components/Layout/styles/Chip.module.css | 119 +++ src/components/Layout/styles/Chip.style.js | 67 -- src/components/Layout/styles/style.js | 16 +- .../VisualizationPlugin.js | 31 +- yarn.lock | 871 +++++++++--------- 25 files changed, 832 insertions(+), 742 deletions(-) create mode 100644 cypress/support/generateTestMatrix.js create mode 100644 src/components/Layout/styles/Chip.module.css delete mode 100644 src/components/Layout/styles/Chip.style.js diff --git a/.github/workflows/dhis2-verify-app.yml b/.github/workflows/dhis2-verify-app.yml index aede8e66aa..6a08c9a796 100644 --- a/.github/workflows/dhis2-verify-app.yml +++ b/.github/workflows/dhis2-verify-app.yml @@ -21,6 +21,16 @@ env: D2_VERBOSE: true jobs: + setup-matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.specs }} + steps: + - uses: actions/checkout@v3 + - name: Generate test matrix + id: set-matrix + run: echo "::set-output name=specs::$(node cypress/support/generateTestMatrix.js)" + build: runs-on: ubuntu-latest steps: @@ -83,10 +93,11 @@ jobs: call-workflow-e2e-prod: if: "!contains(github.event.head_commit.message, '[skip ci]')" - needs: [build, lint, test] - uses: dhis2/workflows/.github/workflows/analytics-e2e-tests-prod.yml@master + needs: [build, lint, test, setup-matrix] + uses: dhis2/workflows/.github/workflows/analytics-e2e-tests-prod.yml@update-cypress-setup with: should_record: ${{ contains(github.event.head_commit.message, '[e2e record]') || contains(join(github.event.pull_request.labels.*.name), 'e2e record')}} + spec-group: ${{ needs.setup-matrix.outputs.matrix }} secrets: username: ${{ secrets.CYPRESS_DHIS2_USERNAME }} password: ${{ secrets.CYPRESS_DHIS2_PASSWORD }} diff --git a/cypress/elements/layout.js b/cypress/elements/layout.js index 4fc765f5b5..aabf938e52 100644 --- a/cypress/elements/layout.js +++ b/cypress/elements/layout.js @@ -1,3 +1,5 @@ +import { EXTENDED_TIMEOUT } from '../support/utils.js' + const yoyCategorySelectEl = 'yoy-layout-rows-select' const youCategorySelectOptionEl = 'yoy-layout-rows-select-option' const chipEl = 'layout-chip' @@ -6,7 +8,7 @@ const chipMenuRemoveOptionEl = 'layout-chip-menu-dimension-menu-item-remove' const chipMenuActionOptionEl = 'layout-chip-menu-dimension-menu-item-action' const chipMenuSubMenuOptionEl = 'layout-chip-menu-dimension-menu-item-DIMENSIONID-menu' -const chipSelectedBackgroundColor = 'rgb(178, 223, 219)' +const chipSelectedBackgroundColor = 'rgb(224, 242, 241)' const getAxisEl = (axisId) => `${axisId}-axis` const getDimensionChipEl = (dimensionId) => `${chipEl}-${dimensionId}` @@ -54,13 +56,14 @@ export const expectDimensionToHaveItemAmount = ( itemAmount ? cy .getBySel(getDimensionChipEl(dimensionId)) - .contains(`${itemAmount} selected`) - .should('have.length', 1) - .and('be.visible') + .parent() + .findBySelLike('chip-suffix') + .contains(itemAmount, EXTENDED_TIMEOUT) : cy .getBySel(getDimensionChipEl(dimensionId)) - .contains(`selected`) - .should('have.length', 0) + .parent() + .findBySelLike('chip-suffix') + .should('not.exist') } else { throw new Error('dimensionId not provided') } @@ -69,9 +72,9 @@ export const expectDimensionToHaveItemAmount = ( export const expectDimensionToHaveAllItemsSelected = (dimensionId) => cy .getBySel(getDimensionChipEl(dimensionId)) - .contains(`: All`) - .should('have.length', 1) - .and('be.visible') + .parent() + .findBySelLike('chip-suffix') + .contains('all', EXTENDED_TIMEOUT) export const expectDimensionOnAxisToHaveLockIcon = (dimensionId, axisId) => cy diff --git a/cypress/integration/dimensions/data.cy.js b/cypress/integration/dimensions/data.cy.js index 8b8244eba8..9f70996f7c 100644 --- a/cypress/integration/dimensions/data.cy.js +++ b/cypress/integration/dimensions/data.cy.js @@ -301,10 +301,10 @@ describe('Data dimension', () => { if (testDataType.endpoint.hasMultiplePages) { // more items are fetched when scrolling down cy.intercept('GET', testDataType.endpoint.requestUrl).as( - 'request' + 'requestSecondPage' ) scrollSourceToBottom() - cy.wait('@request').then(({ request, response }) => { + cy.wait('@requestSecondPage').then(({ request, response }) => { expect(request.url).to.contain('page=2') expect(response.statusCode).to.eq(200) expect( @@ -384,13 +384,15 @@ describe('Data dimension', () => { `default sub group: ${testDataType.defaultSubGroup.name}` ) cy.intercept('GET', testDataType.endpoint.requestUrl).as( - 'request' + 'requestDefaultSubGroup' ) switchSubGroupTo(testDataType.defaultSubGroup.name) - cy.wait('@request').then(({ request, response }) => { - expect(request.url).to.contain('page=1') - expect(response.statusCode).to.eq(200) - }) + cy.wait('@requestDefaultSubGroup').then( + ({ request, response }) => { + expect(request.url).to.contain('page=1') + expect(response.statusCode).to.eq(200) + } + ) expectSourceToNotBeLoading() expectSubGroupSelectToBe(testDataType.defaultSubGroup.name) expectSelectableDataItemsAmountToBe( diff --git a/cypress/integration/visTypes/scatter.cy.js b/cypress/integration/visTypes/scatter.cy.js index d35eba4e66..98e00518bc 100644 --- a/cypress/integration/visTypes/scatter.cy.js +++ b/cypress/integration/visTypes/scatter.cy.js @@ -68,23 +68,26 @@ const TEST_INDICATOR_NAMES = TEST_INDICATORS.slice(1, 4).map( ) const TEST_VIS_NAME = `TEST SCATTER ${new Date().toLocaleString()}` -describe('using a Scatter chart', () => { - it('navigates to a new Scatter chart', () => { +describe('Scatter chart', () => { + it('works correctly (create, save, load, swap etc)', () => { + cy.log('navigates to a new Scatter chart') goToStartPage() changeVisType(visTypeDisplayNames[VIS_TYPE_SCATTER]) - }) - it("shows an error message when vertical and horizontal isn't selected", () => { + + cy.log( + "shows an error message when vertical and horizontal isn't selected" + ) clickMenuBarUpdateButton() expectErrorToContainTitle('Vertical is empty') - }) - it('adds a vertical item and shows an error message', () => { + + cy.log('adds a vertical item and shows an error message') openDimension(DIMENSION_ID_DATA) selectIndicators([TEST_INDICATOR_NAMES[0]]) clickDimensionModalUpdateButton() expectErrorToContainTitle('Horizontal is empty') expectVerticalToContainDimensionLabel(TEST_INDICATOR_NAMES[0]) - }) - it('adds a horizontal item and displays a chart', () => { + + cy.log('adds a horizontal item and displays a chart') openDimension(DIMENSION_ID_DATA) switchDataTab('Horizontal') selectIndicators([TEST_INDICATOR_NAMES[1]]) @@ -94,14 +97,14 @@ describe('using a Scatter chart', () => { //expectStoreCurrentColumnsToHaveLength(1) // FIXME: Store is always in default state expectVerticalToContainDimensionLabel(TEST_INDICATOR_NAMES[0]) expectHorizontalToContainDimensionLabel(TEST_INDICATOR_NAMES[1]) - }) - it('selects org unit level Facility', () => { + + cy.log('selects org unit level Facility') const TEST_ORG_UNIT_LEVEL = 'Facility' openDimension(DIMENSION_ID_ORGUNIT) expectOrgUnitDimensionModalToBeVisible() deselectUserOrgUnit('User organisation unit') expectOrgUnitDimensionToNotBeLoading() - // FIXME this selection causes a analytics request that takes too long on test instances + // FIXME: this selection causes a analytics request that takes too long on test instances //selectOrgUnitTreeItem('Sierra Leone') selectOrgUnitTreeItem('Bo') selectOrgUnitTreeItem('Bombali') @@ -109,25 +112,26 @@ describe('using a Scatter chart', () => { expectOrgUnitDimensionModalToBeVisible() clickDimensionModalUpdateButton() expectVisualizationToBeVisible(VIS_TYPE_SCATTER) - }) - it('Data is locked to Vertical', () => { + + cy.log('Data is locked to Vertical') expectDimensionOnAxisToHaveLockIcon(DIMENSION_ID_DATA, 'Vertical') - }) - it('Data is locked to Horizontal', () => { + + cy.log('Data is locked to Horizontal') expectDimensionOnAxisToHaveLockIcon(DIMENSION_ID_DATA, 'Horizontal') - }) - it('Org units is locked to Points', () => { + + cy.log('Org units is locked to Points') expectDimensionOnAxisToHaveLockIcon(DIMENSION_ID_ORGUNIT, AXIS_ID_ROWS) - }) - it('swaps vertical with horizontal', () => { + + cy.log('swaps vertical with horizontal') openContextMenu('VERTICAL') clickContextMenuSwap('VERTICAL', 'HORIZONTAL') clickMenuBarUpdateButton() expectVisualizationToBeVisible(VIS_TYPE_SCATTER) + expectAppToNotBeLoading() expectVerticalToContainDimensionLabel(TEST_INDICATOR_NAMES[1]) expectHorizontalToContainDimensionLabel(TEST_INDICATOR_NAMES[0]) - }) - it('adds a second item to horizontal and displays warning messages', () => { + + cy.log('adds a second item to horizontal and displays warning messages') openDimensionOnAxis(DIMENSION_ID_DATA, 'Horizontal') selectIndicators([TEST_INDICATOR_NAMES[2]]) expectDataDimensionModalWarningToContain( @@ -139,23 +143,27 @@ describe('using a Scatter chart', () => { ).id ) clickDimensionModalUpdateButton() + expectVisualizationToBeVisible(VIS_TYPE_SCATTER) + expectAppToNotBeLoading() expectDimensionOnAxisToHaveWarningIcon(DIMENSION_ID_DATA, 'Horizontal') - }) - it('saves and only displays 1 horizontal item', () => { + + cy.log('saves and only displays 1 horizontal item') saveNewAO(TEST_VIS_NAME) expectVisualizationToBeVisible(VIS_TYPE_SCATTER) + expectAppToNotBeLoading() expectVerticalToContainDimensionLabel(TEST_INDICATOR_NAMES[1]) expectHorizontalToContainDimensionLabel(TEST_INDICATOR_NAMES[0]) - }) - it('swaps vertical with horizontal', () => { + + cy.log('swaps vertical with horizontal') openContextMenu('HORIZONTAL') clickContextMenuSwap('HORIZONTAL', 'VERTICAL') clickMenuBarUpdateButton() expectVisualizationToBeVisible(VIS_TYPE_SCATTER) + expectAppToNotBeLoading() expectVerticalToContainDimensionLabel(TEST_INDICATOR_NAMES[0]) expectHorizontalToContainDimensionLabel(TEST_INDICATOR_NAMES[1]) - }) - it('Options -> Axes -> sets min/max range', () => { + + cy.log('Options -> Axes -> sets min/max range') const TEST_AXES = [ { axis: 'RANGE_0', label: 'Vertical (y) axis', min: 50, max: 150 }, { @@ -177,24 +185,24 @@ describe('using a Scatter chart', () => { expectWindowConfigYAxisToHaveRangeMaxValue(TEST_AXES[0].max) expectWindowConfigXAxisToHaveRangeMinValue(TEST_AXES[1].min) expectWindowConfigXAxisToHaveRangeMaxValue(TEST_AXES[1].max) - }) - it('Options -> Outliers -> enables outliers', () => { + + cy.log('Options -> Outliers -> enables outliers') openOptionsModal(OPTIONS_TAB_OUTLIERS) checkOutliersCheckbox() // TODO: Set more outlier options clickOptionsModalUpdateButton() expectVisualizationToBeVisible(VIS_TYPE_SCATTER) // TODO: Intercept the data returned to simplify / standardise it, then check that the $config has the correct data - }) - it('saves and displays items in the correct places', () => { + + cy.log('saves and displays items in the correct places') saveExistingAO() expectAppToNotBeLoading() expectVisualizationToBeVisible(VIS_TYPE_SCATTER) expectVerticalToContainDimensionLabel(TEST_INDICATOR_NAMES[0]) expectHorizontalToContainDimensionLabel(TEST_INDICATOR_NAMES[1]) - }) - // TODO: Open outlier options again and check that everything was saved correctly - it('deletes saved scatter AO', () => { + + // TODO: Open outlier options again and check that everything was saved correctly + cy.log('deletes saved scatter AO') deleteAO() expectStartScreenToBeVisible() expectRouteToBeEmpty() diff --git a/cypress/support/generateTestMatrix.js b/cypress/support/generateTestMatrix.js new file mode 100644 index 0000000000..c19456ab91 --- /dev/null +++ b/cypress/support/generateTestMatrix.js @@ -0,0 +1,35 @@ +const fs = require('fs') +const path = require('path') + +const getAllFiles = (dirPath, arrayOfFiles = []) => { + const files = fs.readdirSync(dirPath) + + files.forEach((file) => { + if (fs.statSync(path.join(dirPath, file)).isDirectory()) { + arrayOfFiles = getAllFiles(path.join(dirPath, file), arrayOfFiles) + } else if (path.extname(file) === '.js') { + arrayOfFiles.push(path.join(dirPath, file)) + } + }) + + return arrayOfFiles +} + +const createGroups = (files, numberOfGroups = 10) => { + const groups = [] + for (let i = 0; i < numberOfGroups; i++) { + groups.push([]) + } + + files.forEach((file, index) => { + groups[index % numberOfGroups].push(file) + }) + + return groups.map((group, index) => ({ id: index + 1, tests: group })) +} + +const cypressSpecsPath = './cypress/integration' +const specs = getAllFiles(cypressSpecsPath) +const groupedSpecs = createGroups(specs) + +console.log(JSON.stringify(groupedSpecs)) diff --git a/i18n/ar.po b/i18n/ar.po index 404402427d..7770073d21 100644 --- a/i18n/ar.po +++ b/i18n/ar.po @@ -1,16 +1,16 @@ # # Translators: # KRG HIS , 2020 -# Philip Larsen Donnelly, 2020 # Hamza Assada <7amza.it@gmail.com>, 2022 # Viktor Varland , 2022 +# Philip Larsen Donnelly, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" "POT-Creation-Date: 2024-03-01T08:28:43.727Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Viktor Varland , 2022\n" +"Last-Translator: Philip Larsen Donnelly, 2024\n" "Language-Team: Arabic (https://app.transifex.com/hisp-uio/teams/100509/ar/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -163,14 +163,8 @@ msgstr "تحميل" msgid "Hide" msgstr "إخفاء" -msgid "All" -msgstr "الجميع" - -msgid "{{total}} of {{axisMaxNumberOfItems}} selected" -msgstr "{{total}} من {{axisMaxNumberOfItems}} تم تحديدها" - -msgid "{{total}} selected" -msgstr "{{total}} تم تحديدها" +msgid "all" +msgstr "" msgid "{{dimensionName}} is locked to {{axisName}} for {{visTypeName}}" msgstr "{{dimensionName}} مقفل على {{axisName}} لـ {{visTypeName}}" @@ -470,6 +464,9 @@ msgstr "بعد الأخير" msgid "Before first and after last" msgstr "قبل الأول وبعد الأخير" +msgid "All" +msgstr "الجميع" + msgid "Hide empty rows" msgstr "إخفاء الصفوف الفارغة" @@ -729,7 +726,7 @@ msgstr "" "منفصلة." msgid "Category option combination" -msgstr "" +msgstr "مزيج خيارات الفئة" msgid "Absolute deviation" msgstr "" diff --git a/i18n/cs.po b/i18n/cs.po index 15ab75cc84..15fc459492 100644 --- a/i18n/cs.po +++ b/i18n/cs.po @@ -164,14 +164,8 @@ msgstr "Stáhnout" msgid "Hide" msgstr "Skrýt" -msgid "All" -msgstr "Vše" - -msgid "{{total}} of {{axisMaxNumberOfItems}} selected" -msgstr "Je vybráno {{total}} z {{axisMaxNumberOfItems}}" - -msgid "{{total}} selected" -msgstr "Vybráno: {{total}}" +msgid "all" +msgstr "vše" msgid "{{dimensionName}} is locked to {{axisName}} for {{visTypeName}}" msgstr "{{dimensionName}} je pro {{visTypeName}} uzamčen na {{axisName}}" @@ -472,6 +466,9 @@ msgstr "Po poslední" msgid "Before first and after last" msgstr "Před první a po poslední" +msgid "All" +msgstr "Vše" + msgid "Hide empty rows" msgstr "Skrýt prázdné řádky" @@ -761,7 +758,7 @@ msgid "" msgstr "" msgid "Median" -msgstr "" +msgstr "Medián" msgid "" "The middle value in a dataset when the values are arranged in ascending or " @@ -780,7 +777,7 @@ msgid "" msgstr "" msgid "Z-score" -msgstr "" +msgstr "Z-score" msgid "" "A measure of how many standard deviations a data point is from the mean of a" @@ -789,7 +786,7 @@ msgid "" msgstr "" msgid "Mean" -msgstr "" +msgstr "Střední" msgid "Average of the value over time." msgstr "" diff --git a/i18n/en.pot b/i18n/en.pot index 437ec605a4..5057c80c3f 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -160,14 +160,8 @@ msgstr "Download" msgid "Hide" msgstr "Hide" -msgid "All" -msgstr "All" - -msgid "{{total}} of {{axisMaxNumberOfItems}} selected" -msgstr "{{total}} of {{axisMaxNumberOfItems}} selected" - -msgid "{{total}} selected" -msgstr "{{total}} selected" +msgid "all" +msgstr "all" msgid "{{dimensionName}} is locked to {{axisName}} for {{visTypeName}}" msgstr "{{dimensionName}} is locked to {{axisName}} for {{visTypeName}}" @@ -469,6 +463,9 @@ msgstr "After last" msgid "Before first and after last" msgstr "Before first and after last" +msgid "All" +msgstr "All" + msgid "Hide empty rows" msgstr "Hide empty rows" diff --git a/i18n/es.po b/i18n/es.po index f919e03f37..b991d4974d 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -6,16 +6,16 @@ # Prabhjot Singh, 2021 # Viktor Varland , 2021 # Carlos Tejo Alonso, 2022 -# Enzo Nicolas Rossi , 2024 # Philip Larsen Donnelly, 2024 # Janeth Cruz, 2024 +# Enzo Nicolas Rossi , 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" "POT-Creation-Date: 2024-03-01T08:28:43.727Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Janeth Cruz, 2024\n" +"Last-Translator: Enzo Nicolas Rossi , 2024\n" "Language-Team: Spanish (https://app.transifex.com/hisp-uio/teams/100509/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -176,14 +176,8 @@ msgstr "Descargar" msgid "Hide" msgstr "Ocultar" -msgid "All" -msgstr "Todos" - -msgid "{{total}} of {{axisMaxNumberOfItems}} selected" -msgstr "{{total}} de {{axisMaxNumberOfItems}} seleccionados" - -msgid "{{total}} selected" -msgstr "{{total}} seleccionado" +msgid "all" +msgstr "todo" msgid "{{dimensionName}} is locked to {{axisName}} for {{visTypeName}}" msgstr "{{dimensionName}} está bloqueado en {{axisName}} para {{visTypeName}}" @@ -487,6 +481,9 @@ msgstr "Después del último" msgid "Before first and after last" msgstr "Antes del primero y después del último" +msgid "All" +msgstr "Todos" + msgid "Hide empty rows" msgstr "Ocultar filas vacías" diff --git a/i18n/fr.po b/i18n/fr.po index fcf5fbe4d0..527e1b7294 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -2,21 +2,22 @@ # Translators: # Bram Piot , 2019 # Matthieu Pinard , 2019 -# Karoline Tufte Lien , 2020 # Djibril Dione , 2020 # Gabriela Rodriguez , 2020 # Viktor Varland , 2021 -# Yao Selom SAKA (HISP WCA) , 2022 # Edem Kossi , 2023 # Philip Larsen Donnelly, 2024 # Yayra Gomado , 2024 +# Karoline Tufte Lien , 2024 +# Yury Rogachev , 2024 +# Yao Selom SAKA (HISP WCA) , 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" "POT-Creation-Date: 2024-03-01T08:28:43.727Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Yayra Gomado , 2024\n" +"Last-Translator: Yao Selom SAKA (HISP WCA) , 2024\n" "Language-Team: French (https://app.transifex.com/hisp-uio/teams/100509/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -173,15 +174,9 @@ msgstr "Télécharger" msgid "Hide" msgstr "Masquer" -msgid "All" +msgid "all" msgstr "Tout" -msgid "{{total}} of {{axisMaxNumberOfItems}} selected" -msgstr "{{total}} de {{axisMaxNumberOfItems}} choisis" - -msgid "{{total}} selected" -msgstr "{{total}} choisis" - msgid "{{dimensionName}} is locked to {{axisName}} for {{visTypeName}}" msgstr "" "{{dimensionName}} est verrouillé sur {{axisName}} pour {{visTypeName}}" @@ -486,6 +481,9 @@ msgstr "Après dernier" msgid "Before first and after last" msgstr "Avant premier et après dernier " +msgid "All" +msgstr "Tout" + msgid "Hide empty rows" msgstr "Masquer lignes vides" @@ -760,7 +758,7 @@ msgstr "" "données utilisent des couches de carte distinctes." msgid "Category option combination" -msgstr "" +msgstr "Combinaison d'option de catégorie" msgid "Absolute deviation" msgstr "" @@ -778,7 +776,7 @@ msgid "" msgstr "" msgid "Median" -msgstr "" +msgstr "Médian" msgid "" "The middle value in a dataset when the values are arranged in ascending or " @@ -797,7 +795,7 @@ msgid "" msgstr "" msgid "Z-score" -msgstr "" +msgstr "Z-score" msgid "" "A measure of how many standard deviations a data point is from the mean of a" @@ -806,7 +804,7 @@ msgid "" msgstr "" msgid "Mean" -msgstr "" +msgstr "Moyenne" msgid "Average of the value over time." msgstr "" diff --git a/i18n/id.po b/i18n/id.po index fa8721527d..12b49292a7 100644 --- a/i18n/id.po +++ b/i18n/id.po @@ -169,14 +169,8 @@ msgstr "Unduh" msgid "Hide" msgstr "Sembunyikan" -msgid "All" -msgstr "Semua" - -msgid "{{total}} of {{axisMaxNumberOfItems}} selected" -msgstr "{{total}} dari {{axisMaxNumberOfItems}} terpilih" - -msgid "{{total}} selected" -msgstr "{{total}} terpilih" +msgid "all" +msgstr "" msgid "{{dimensionName}} is locked to {{axisName}} for {{visTypeName}}" msgstr "{{dimensionName}} dikunci sebagai {{axisName}} untuk {{visTypeName}}" @@ -479,6 +473,9 @@ msgstr "Setelah terakhir" msgid "Before first and after last" msgstr "Sebelum yang pertama dan sesudah yang terakhir" +msgid "All" +msgstr "Semua" + msgid "Hide empty rows" msgstr "Sembunyikan baris kosong" diff --git a/i18n/lo.po b/i18n/lo.po index c62069921a..d17419ce1a 100644 --- a/i18n/lo.po +++ b/i18n/lo.po @@ -21,7 +21,7 @@ msgstr "" "Plural-Forms: nplurals=1; plural=0;\n" msgid "All items" -msgstr "" +msgstr "ລາຍການທັງໝົດ" msgid "Rename successful" msgstr "" @@ -79,16 +79,16 @@ msgid "Horizontal" msgstr "" msgid "Main dimensions" -msgstr "" +msgstr "ຕົວຕອງທີ່ສຳຄັນ" msgid "Other dimensions" msgstr "" msgid "Your dimensions" -msgstr "" +msgstr "ຂະໜາດຂອງທ່ານ" msgid "Filter dimensions" -msgstr "" +msgstr "ຂະໜາດຕົວຕອງ" msgid "Data value set" msgstr "ການຕັ້ງຄ່າຂໍ້ມູນ" @@ -162,13 +162,7 @@ msgstr "ດາວໂຫຼດ" msgid "Hide" msgstr "ເຊື່ອງ" -msgid "All" -msgstr "ທັງໝົດ" - -msgid "{{total}} of {{axisMaxNumberOfItems}} selected" -msgstr "" - -msgid "{{total}} selected" +msgid "all" msgstr "" msgid "{{dimensionName}} is locked to {{axisName}} for {{visTypeName}}" @@ -465,6 +459,9 @@ msgstr "ຫຼັງຈາກສຸດທ້າຍ" msgid "Before first and after last" msgstr "ກ່ອນໜ້າທໍາອິດ ແລະ ຫຼັງຈາກສຸດທ້າຍ" +msgid "All" +msgstr "ທັງໝົດ" + msgid "Hide empty rows" msgstr "ເຊື່ອງແຖວທີ່ເປົ່າຫວ່າງ" @@ -628,7 +625,7 @@ msgid "Visualization type" msgstr "" msgid "Axis {{axisId}}" -msgstr "" +msgstr "ແກນ {{axisId}}" msgid "Series is empty" msgstr "" @@ -1024,7 +1021,7 @@ msgid "Last 5 years" msgstr "5 ປີຍ້ອນຫຼັງ" msgid "User organisation unit" -msgstr "" +msgstr "ຫນ່ວຍງານອົງການຈັດຕັ້ງຂອງຜູ້ໃຊ້" msgid "User sub-units" msgstr "ຫົວໜ່ວຍຍ່ອຍຂອງທ່ານ" diff --git a/i18n/nb.po b/i18n/nb.po index 21b97f3ebf..d829252a13 100644 --- a/i18n/nb.po +++ b/i18n/nb.po @@ -2,15 +2,15 @@ # Translators: # Caroline Hesthagen Holen , 2022 # Jan Henrik Øverland, 2024 -# Karoline Tufte Lien , 2024 # Martin , 2024 +# Karoline Tufte Lien , 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" "POT-Creation-Date: 2024-03-01T08:28:43.727Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Martin , 2024\n" +"Last-Translator: Karoline Tufte Lien , 2024\n" "Language-Team: Norwegian Bokmål (https://app.transifex.com/hisp-uio/teams/100509/nb/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -166,14 +166,8 @@ msgstr "Last ned" msgid "Hide" msgstr "Skjul" -msgid "All" -msgstr "Alt" - -msgid "{{total}} of {{axisMaxNumberOfItems}} selected" -msgstr "{{total}} av {{axisMaxNumberOfItems}} valgt" - -msgid "{{total}} selected" -msgstr "{{total}} valgt" +msgid "all" +msgstr "alle" msgid "{{dimensionName}} is locked to {{axisName}} for {{visTypeName}}" msgstr "{{dimensionName}} er låst til {{axisName}} for {{visTypeName}}" @@ -476,6 +470,9 @@ msgstr "Etter siste" msgid "Before first and after last" msgstr "Før første og etter siste" +msgid "All" +msgstr "Alt" + msgid "Hide empty rows" msgstr "Skjul tomme rader" diff --git a/i18n/nl.po b/i18n/nl.po index ea2778502c..972e4491a7 100644 --- a/i18n/nl.po +++ b/i18n/nl.po @@ -2,17 +2,17 @@ # Translators: # Cherise Beek , 2021 # Yury Rogachev , 2021 -# Charel van den Elsen, 2023 # Enzo Nicolas Rossi , 2023 # Philip Larsen Donnelly, 2024 # Rica Zamora Duchateau, 2024 +# Charel van den Elsen, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" "POT-Creation-Date: 2024-03-01T08:28:43.727Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Rica Zamora Duchateau, 2024\n" +"Last-Translator: Charel van den Elsen, 2024\n" "Language-Team: Dutch (https://app.transifex.com/hisp-uio/teams/100509/nl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -169,14 +169,8 @@ msgstr "Download" msgid "Hide" msgstr "Verbergen" -msgid "All" -msgstr "Alle" - -msgid "{{total}} of {{axisMaxNumberOfItems}} selected" -msgstr "{{total}} van {{axisMaxNumberOfItems}} geselecteerd" - -msgid "{{total}} selected" -msgstr "{{total}} geselecteerd" +msgid "all" +msgstr "alle" msgid "{{dimensionName}} is locked to {{axisName}} for {{visTypeName}}" msgstr "" @@ -475,6 +469,9 @@ msgstr "" msgid "Before first and after last" msgstr "Voor eerste en na laatste" +msgid "All" +msgstr "Alle" + msgid "Hide empty rows" msgstr "Verberg lege rijen" diff --git a/package.json b/package.json index d0f56dd4f2..e4182d76fa 100644 --- a/package.json +++ b/package.json @@ -40,12 +40,12 @@ "typescript": "^4.8.4" }, "dependencies": { - "@dhis2/analytics": "^26.6.8", + "@dhis2/analytics": "^26.6.10", "@dhis2/app-runtime": "^3.7.0", "@dhis2/app-runtime-adapter-d2": "^1.1.0", "@dhis2/app-service-datastore": "^1.0.0-beta.3", "@dhis2/d2-i18n": "^1.1.0", - "@dhis2/ui": "^9.2.0", + "@dhis2/ui": "^9.4.4", "@krakenjs/post-robot": "^11.0.0", "d2": "^31.9.1", "decode-uri-component": "^0.2.2", diff --git a/src/components/DimensionsPanel/Dialogs/DialogManager.js b/src/components/DimensionsPanel/Dialogs/DialogManager.js index e3844578d0..a7f59bbea4 100644 --- a/src/components/DimensionsPanel/Dialogs/DialogManager.js +++ b/src/components/DimensionsPanel/Dialogs/DialogManager.js @@ -234,7 +234,7 @@ export class DialogManager extends Component { // The OU content is persisted as mounted in order // to cache the org unit tree data - renderPersistedContent = (dimensionProps) => { + renderPersistedContent = (dimensionProps, displayNameProperty) => { const { ouIds, metadata, parentGraphMap, dialogId } = this.props if (this.state.ouMounted) { @@ -253,6 +253,7 @@ export class DialogManager extends Component { roots={this.props.rootOrgUnits.map( (rootOrgUnit) => rootOrgUnit.id )} + displayNameProp={displayNameProperty} {...dimensionProps} /> @@ -455,7 +456,10 @@ export class DialogManager extends Component { return ( - {this.renderPersistedContent(dimensionProps)} + {this.renderPersistedContent( + dimensionProps, + displayNameProperty + )} {dialogId && dynamicContent()} ) diff --git a/src/components/DimensionsPanel/styles/DndDimensionList.module.css b/src/components/DimensionsPanel/styles/DndDimensionList.module.css index 5a107a22bd..37f4d0b203 100644 --- a/src/components/DimensionsPanel/styles/DndDimensionList.module.css +++ b/src/components/DimensionsPanel/styles/DndDimensionList.module.css @@ -19,6 +19,9 @@ .list { margin: 0; padding: 0; + display: flex; + flex-direction: column; + gap: 4px; } .header { diff --git a/src/components/IconButton/styles/IconButton.module.css b/src/components/IconButton/styles/IconButton.module.css index dcb3430030..413b863096 100644 --- a/src/components/IconButton/styles/IconButton.module.css +++ b/src/components/IconButton/styles/IconButton.module.css @@ -9,10 +9,10 @@ cursor: pointer; padding: 0; vertical-align: middle; - border-radius: 2px; - width: 22px; - height: 22px; - margin: '0px 0px 0px 2px'; + border-radius: 0; + width: 20px; + height: 20px; + margin: 0px 0px 0px 2px; color: var(--colors-grey700); } @@ -21,6 +21,6 @@ } .iconButton:hover { - background-color: rgba(0, 0, 0, 0.12); + background-color: rgba(0, 0, 0, 0.09); color: var(--colors-grey900); } diff --git a/src/components/Layout/Chip.js b/src/components/Layout/Chip.js index 411dca6523..2954de4473 100644 --- a/src/components/Layout/Chip.js +++ b/src/components/Layout/Chip.js @@ -18,7 +18,9 @@ import { ALL_DYNAMIC_DIMENSION_ITEMS, } from '@dhis2/analytics' import i18n from '@dhis2/d2-i18n' -import { Tooltip, IconLock16, IconWarningFilled16 } from '@dhis2/ui' +import { Tooltip, IconWarning16 } from '@dhis2/ui' +import { colors } from '@dhis2/ui-constants' +import cx from 'classnames' import PropTypes from 'prop-types' import React from 'react' import { connect } from 'react-redux' @@ -27,7 +29,7 @@ import { setDataTransfer } from '../../modules/dnd.js' import { sGetDimensions } from '../../reducers/dimensions.js' import { sGetMetadata } from '../../reducers/metadata.js' import { sGetUiType } from '../../reducers/ui.js' -import { styles } from './styles/Chip.style.js' +import styles from './styles/Chip.module.css' import { default as TooltipContent } from './TooltipContent.js' const Chip = ({ @@ -48,19 +50,31 @@ const Chip = ({ const LockIconWrapper = (
- + + +
) const WarningIconWrapper = (
- +
) @@ -90,6 +104,18 @@ const Chip = ({ const isSplitAxis = type === VIS_TYPE_SCATTER && dimensionId === DIMENSION_ID_DATA + let chipLabelSuffix + + if (items.length > 0) { + if (items.includes(ALL_DYNAMIC_DIMENSION_ITEMS)) { + chipLabelSuffix = i18n.t('all') + } else if (isSplitAxis) { + chipLabelSuffix = i18n.t(metadata[items[0]]?.name || null) + } else { + chipLabelSuffix = items.length + } + } + const handleClick = () => { if (!getPredefinedDimensionProp(dimensionId, DIMENSION_PROP_NO_ITEMS)) { onClick() @@ -100,48 +126,12 @@ const Chip = ({ setDataTransfer(event, axisId) } - const getWrapperStyles = () => ({ - ...styles.chipWrapper, - ...(!getPredefinedDimensionProp(dimensionId, DIMENSION_PROP_NO_ITEMS) && - !items.length - ? styles.chipEmpty - : {}), - }) - - const renderChipLabelSuffix = () => { - const numberOfItems = items.length - let itemsLabel - if (items.includes(ALL_DYNAMIC_DIMENSION_ITEMS)) { - itemsLabel = i18n.t('All') - } else if ( - !!getMaxNumberOfItems() && - numberOfItems > getMaxNumberOfItems() - ) { - itemsLabel = i18n.t( - `{{total}} of {{axisMaxNumberOfItems}} selected`, - { - total: numberOfItems, - axisMaxNumberOfItems: getMaxNumberOfItems(), - } - ) - } else { - if (isSplitAxis) { - itemsLabel = i18n.t(metadata[items[0]]?.name || '') - } else { - itemsLabel = i18n.t('{{total}} selected', { - total: numberOfItems, - }) - } - } - return items.length > 0 ? `: ${itemsLabel}` : '' - } - const renderChipIcon = () => { const Icon = getPredefinedDimensionProp(dimensionId, 'icon') return Icon ? ( - + ) : ( - + ) } @@ -174,21 +164,32 @@ const Chip = ({ const renderChipContent = () => ( <> -
{renderChipIcon()}
- +
{renderChipIcon()}
+ {dimensionName} - - {renderChipLabelSuffix()} - + {chipLabelSuffix && ( + + {chipLabelSuffix} + + )} {hasWarning && WarningIconWrapper} - {isLocked && LockIconWrapper} ) return (
{renderChipContent()}
)} - {contextMenu &&
{contextMenu}
} + {contextMenu && ( +
{contextMenu}
+ )} + {isLocked && ( + + {LockIconWrapper} + + )} ) } diff --git a/src/components/Layout/DefaultLayout/styles/DefaultAxis.module.css b/src/components/Layout/DefaultLayout/styles/DefaultAxis.module.css index 8b517bd680..2f3ba96073 100644 --- a/src/components/Layout/DefaultLayout/styles/DefaultAxis.module.css +++ b/src/components/Layout/DefaultLayout/styles/DefaultAxis.module.css @@ -4,7 +4,7 @@ align-items: flex-start; align-content: flex-start; flex-wrap: wrap; - min-height: 32px; + min-height: 26px; } .content > div { diff --git a/src/components/Layout/styles/Chip.module.css b/src/components/Layout/styles/Chip.module.css new file mode 100644 index 0000000000..822d94dc10 --- /dev/null +++ b/src/components/Layout/styles/Chip.module.css @@ -0,0 +1,119 @@ +.chip { + max-width: 400px; + display: flex; + align-items: center; + background-color: var(--colors-teal100); + padding: 0; + font-size: 13px; + line-height: 15px; + font-weight: 400; + color: var(--colors-teal900); + cursor: pointer; + user-select: none; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin: 2px; + border-radius: 2px; + border: 1px solid var(--colors-teal200); +} + +.chip:hover { + background: #cdeae8; + border: 1px solid #93c4bf; + box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); +} + +.chipEmpty { + background-color: var(--colors-grey100); + border-color: var(--colors-grey400); + color: var(--colors-grey900); +} + +.chipEmpty:hover { + background-color: var(--colors-grey200); + border-color: var(--colors-grey400); +} + +.fixedDimensionIcon { + padding: 0 4px; +} + +.leftIconWrapper { + padding: 0 4px; + display: flex; + align-items: center; +} + +.warningIconWrapper { + padding-left: 2px; + height: 20px; + display: flex; + align-items: center; +} + +.lockIconWrapper { + background: #cbe7e5; + height: 20px; + padding: 0 2px 0 3px; + margin: 0 0 0 2px; + display: flex; + align-items: center; + justify-content: center; +} + +.lockIconWrapper svg path { + fill: var(--colors-teal900); +} + +.chipEmpty .lockIconWrapper { + background: var(--colors-grey300); +} + +.chipEmpty .lockIconWrapper svg path { + fill: var(--colors-grey800); +} + +.label { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.chipLeft { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: flex; + border-top-right-radius: 0px; + border-bottom-right-radius: 0px; + align-items: center; + min-height: 20px; + margin: 0 2px 0 0; +} + +.chipRight { + padding-left: 0px; + border-top-left-radius: 0px; + border-bottom-left-radius: 0px; + display: flex; + height: 20px; +} + +.chipRight + .lockIconWrapper { + margin: 0; +} + +.suffix { + font-family: monospace; + font-size: 11px; + background: #c5e4e3; + border-radius: 2px; + margin-left: 4px; + min-width: 12px; + text-align: center; + padding: 2px 2px 1px 2px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} diff --git a/src/components/Layout/styles/Chip.style.js b/src/components/Layout/styles/Chip.style.js deleted file mode 100644 index 39342a44ca..0000000000 --- a/src/components/Layout/styles/Chip.style.js +++ /dev/null @@ -1,67 +0,0 @@ -import { colors } from '@dhis2/ui' -import * as layoutStyle from './style.js' - -const baseChip = { - padding: layoutStyle.CHIP_PADDING, - fontSize: layoutStyle.CHIP_FONT_SIZE, - fontWeight: layoutStyle.CHIP_FONT_WEIGHT, - color: layoutStyle.CHIP_COLOR, - cursor: 'pointer', - minHeight: 24, - userSelect: 'none', -} - -export const styles = { - chipWrapper: { - display: 'flex', - margin: layoutStyle.CHIP_MARGIN, - backgroundColor: layoutStyle.CHIP_BACKGROUND_COLOR, - borderRadius: layoutStyle.CHIP_BORDER_RADIUS, - alignItems: 'center', - maxWidth: '400px', - }, - chip: { - ...baseChip, - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - }, - chipEmpty: { - backgroundColor: colors.grey300, - }, - fixedDimensionIcon: { - paddingRight: '6px', - }, - leftIconWrapper: { - paddingRight: '6px', - display: 'flex', - alignItems: 'center', - }, - rightIconWrapper: { - paddingLeft: '6px', - marginTop: '2px', - }, - label: { - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - }, -} - -styles.chipLeft = { - ...baseChip, - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - display: 'flex', - borderTopRightRadius: '0px', - borderBottomRightRadius: '0px', - alignItems: 'center', -} - -styles.chipRight = { - ...baseChip, - paddingLeft: '0px', - borderTopLeftRadius: '0px', - borderBottomLeftRadius: '0px', -} diff --git a/src/components/Layout/styles/style.js b/src/components/Layout/styles/style.js index d91df2a50b..ff4a2f6257 100644 --- a/src/components/Layout/styles/style.js +++ b/src/components/Layout/styles/style.js @@ -1,22 +1,12 @@ -import { colors, theme } from '@dhis2/ui' +import { colors } from '@dhis2/ui' // Layout -export const LAYOUT_HEIGHT = '78px' +export const LAYOUT_HEIGHT = '70px' // Axis -export const AXIS_PADDING = '4px 4px 2px 6px' +export const AXIS_PADDING = '4px 4px 4px 6px' export const AXIS_LABEL_PADDING = '2px 0px 0px 4px' export const AXIS_BORDER_COLOR = colors.grey300 export const AXIS_BORDER_STYLE = 'solid' export const AXIS_BORDER_WIDTH = '0px 0px 1px 1px' export const AXIS_BACKGROUND_COLOR = colors.white - -// Chip -export const CHIP_COLOR = colors.grey900 -export const CHIP_FONT_WEIGHT = 400 -export const CHIP_FONT_SIZE = '14px' -export const CHIP_BACKGROUND_COLOR = theme.secondary200 -export const CHIP_HEIGHT = '14px' -export const CHIP_MARGIN = '4px' -export const CHIP_PADDING = '1px 2px 1px 6px' -export const CHIP_BORDER_RADIUS = '2px' diff --git a/src/components/VisualizationPlugin/VisualizationPlugin.js b/src/components/VisualizationPlugin/VisualizationPlugin.js index eded3ef3bc..ef87dbac82 100644 --- a/src/components/VisualizationPlugin/VisualizationPlugin.js +++ b/src/components/VisualizationPlugin/VisualizationPlugin.js @@ -43,7 +43,7 @@ export const VisualizationPlugin = ({ onDrill, }) => { const engine = useDataEngine() - const [visualization, setVisualization] = useState(undefined) + const [visualization, setVisualization] = useState(null) const [ouLevels, setOuLevels] = useState(undefined) const [fetchResult, setFetchResult] = useState(null) const [contextualMenuRef, setContextualMenuRef] = useState(undefined) @@ -173,6 +173,7 @@ export const VisualizationPlugin = ({ useEffect(() => { setFetchResult(null) + setVisualization(null) // filter out disabled options const disabledOptions = getDisabledOptions({ @@ -186,8 +187,6 @@ export const VisualizationPlugin = ({ (option) => delete filteredVisualization[option] ) - setVisualization(filteredVisualization) - const doFetchAll = async () => { const { responses, extraOptions } = await doFetchData( filteredVisualization, @@ -254,6 +253,7 @@ export const VisualizationPlugin = ({ extraOptions, }) setShowLegendKey(filteredVisualization.legend?.showKey) + onLoadingComplete() } @@ -264,7 +264,15 @@ export const VisualizationPlugin = ({ /* eslint-disable-next-line react-hooks/exhaustive-deps */ }, [originalVisualization, filters, forDashboard]) - if (!fetchResult || !ouLevels) { + useEffect(() => { + if (fetchResult?.visualization && ouLevels) { + setVisualization( + convertOuLevelsToUids(ouLevels, fetchResult.visualization) + ) + } + }, [fetchResult?.visualization, ouLevels]) + + if (!fetchResult || !visualization || !ouLevels) { return null } @@ -406,10 +414,7 @@ export const VisualizationPlugin = ({ ) { return (