From bff69ab9cec7685adad823197de8508e9ae83636 Mon Sep 17 00:00:00 2001 From: Edoardo Sabadelli Date: Thu, 14 Dec 2023 16:20:32 +0100 Subject: [PATCH 01/13] feat: cumulative values in PT (DHIS2-5497) (#2746) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(deps): bump tar from 4.4.13 to 4.4.19 (#1946) Bumps [tar](https://github.com/npm/node-tar) from 4.4.13 to 4.4.19. - [Release notes](https://github.com/npm/node-tar/releases) - [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/npm/node-tar/compare/v4.4.13...v4.4.19) --- updated-dependencies: - dependency-name: tar dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: support non-gregorian fixed periods (#2233) * feat: support non-gregorian fixed periods * test: fix test failing due to change in single value behaviour --------- Co-authored-by: Jan Henrik Øverland * fix: dependency updates (#2243) * fix: update cli-app-scripts and analytics deps * chore: configure continuous delivery workflows (#2254) There are 4 workflows: verify PR (dhis2-verify-app.yml) -- build, lint, test, e2e-prod verify commit to dev (dhis2-verify-app.yml) -- build, lint, test, e2e-prod, report-failure-to-slack verify commit to master (dhis2-verify-app.yml) -- build, lint, test, e2e-prod, release, report-failure-to-slack nightly (nightly.yml) -- e2e-dev, report-failure In addition: * e2e-prod and e2e-dev are reusable workflows and are called from dhis2-verify-app and nightly. * removed uses: c-hive/gha-yarn-cache@v1 since it is deprecated and setup-node handles that work. * updated action versions and node versions * cypress test version tagging for features and bugs has been added (copied from line-list) * feat: single value background color change based upon legend (DHIS2-13702) (#2223) * feat: implement data icon option for SV visualization (DHIS2-10496) (#2236) * fix: do not pass a boolean for icons when saving If the option is not set, and thus its value is the same as the default (false) remove it from the current object to avoid sending a boolean value that the backend does not expect. This was causing any save to return 500. * fix: hide icon from visualization when option is toggled This didn't work before because the icons option was removed from the options object, but when the current object was merged with the new options, it retained the original value. * fix: fetch data element icon and pass it in extraOptions This is needed for SV visualizations when the "Show data item icon" options is checked and an icon is assigned to the dx dimension in the maintenance app. In that case the icon's SVG is fetched from the API and passed to the visualization API in the extraOptions object. The SVG generator embeds the icon in the SVG so it appears on the side of the value. * chore: manually bump deps (#2543) * chore: upgrade cypress to v12 and adjust project to it * chore: remove videos * chore: switch test server to debug (test.e2e is broken/slow) * chore: revert the test server changes and move to a separate PR * test: refactor clickCheckbox to check/uncheckCheckbox * test: add helper functions for totals options * test: add tests for cumulativeValues option in PT * refactor: allow non toggleable select to be disabled * refactor: allow checkbox options to be disabled * feat: disable option when cumulativeValues is checked in PT DHiS2-15728 * refactor: add helper text when used in PT DHIS2-15727 * feat: disabled options based on cumulativeValues in PT DHIS2-15728 * chore: update pot file * feat: handle disabled option in Redux store * refactor: revert changes to option components * feat: handle disabled and helpText props for disabled options * refactor: avoid extra prop and detect visType internally * feat: remove disabled before passing object to visualization generator * refactor: remove unnecessary code * refactor: streamlined code * fix: set disabledOptions on AO loading and vis type switching * fix: fix bug which cause current to loose props * chore: update pot file * refactor: rename variable for clarity * chore: fix rebase conflict resolutions * fix: avoid crash when visualization object is empty (ie. New) * refactor: allow disabled to be passed as prop * refactor: allow legend related option to be disabled * feat: disable legend option when cumulative values is enabled * refactor: add styles for titles of disabled sections * chore: regenerate pot file * fix: avoid visualization flashing when changing options Move the filtering of disabled options in the plugin, which is needed anyway to have the visualization looking the same also in dashboards. * refactor: simplify code for passing displayProperty * refactor: avoid involuntary changes to current in Redux store We still need to clone the object, and use it also for the various checks on options. * test: enhance tests, options and sorting * test: use current year instead of hardcode it * chore: use alpha version of analytics * docs: add PT to the list of vis types for cumulative values * chore: fix linting error * chore(analytics): add support for cumulative values --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mozafar Co-authored-by: Jan Henrik Øverland Co-authored-by: Jen Jones Arnesen Co-authored-by: Martin Co-authored-by: HendrikThePendric --- cypress/elements/common.js | 9 +- cypress/elements/optionsModal/index.js | 8 +- cypress/elements/optionsModal/lines.js | 8 +- cypress/elements/optionsModal/outliers.js | 4 +- cypress/elements/optionsModal/totals.js | 100 ++++++++ cypress/elements/pivotTable.js | 7 + .../options/cumulativeValues.cy.js | 221 ++++++++++++++++++ cypress/integration/options/fontStyles.cy.js | 8 +- cypress/integration/options/icon.cy.js | 6 +- cypress/integration/options/lines.cy.js | 4 +- cypress/integration/visTypes/scatter.cy.js | 4 +- docs/data-visualizer.md | 2 +- i18n/en.pot | 13 +- package.json | 2 +- src/actions/ui.js | 21 ++ src/components/Visualization/Visualization.js | 16 +- .../Options/CheckboxBaseOption.js | 20 +- .../Options/ColSubTotals.js | 1 + .../VisualizationOptions/Options/ColTotals.js | 1 + .../Options/CumulativeValues.js | 29 ++- .../VisualizationOptions/Options/Legend.js | 36 ++- .../Options/LegendDisplayStrategy.js | 7 +- .../Options/LegendDisplayStyle.js | 8 +- .../VisualizationOptions/Options/LegendSet.js | 7 +- .../Options/NumberType.js | 1 + .../Options/RadioBaseOption.js | 9 +- .../Options/RowSubTotals.js | 1 + .../VisualizationOptions/Options/RowTotals.js | 1 + .../Options/SelectBaseOption.js | 11 +- .../Options/ShowLegendKey.js | 8 +- .../Options/TextBaseOption.js | 6 +- .../VisualizationOptionsManager.js | 6 + .../styles/VisualizationOptions.style.js | 6 + .../VisualizationPlugin.js | 78 ++++--- src/modules/disabledOptions.js | 34 +++ src/modules/options/config.js | 3 + src/modules/options/pivotTableConfig.js | 9 +- .../options/sections/templates/totals.js | 5 +- src/modules/ui.js | 68 ++++-- src/reducers/ui.js | 12 + yarn.lock | 8 +- 41 files changed, 680 insertions(+), 128 deletions(-) create mode 100644 cypress/elements/optionsModal/totals.js create mode 100644 cypress/integration/options/cumulativeValues.cy.js create mode 100644 src/modules/disabledOptions.js diff --git a/cypress/elements/common.js b/cypress/elements/common.js index 7b245a202f..2ff7ba0562 100644 --- a/cypress/elements/common.js +++ b/cypress/elements/common.js @@ -12,9 +12,16 @@ const loadingEl = 'dhis2-uicore-circularloader' export const expectAppToNotBeLoading = () => cy.getBySel(loadingEl, { timeout: 15000 }).should('not.exist') -export const clickCheckbox = (target) => +export const checkCheckbox = (target) => cy.getBySel(target).click().find('[type="checkbox"]').should('be.checked') +export const uncheckCheckbox = (target) => + cy + .getBySel(target) + .click() + .find('[type="checkbox"]') + .should('not.be.checked') + export const typeInput = (target, text) => cy.getBySel(target).find('input').type(text) diff --git a/cypress/elements/optionsModal/index.js b/cypress/elements/optionsModal/index.js index 1f8f7f6bdd..b1a74c55fa 100644 --- a/cypress/elements/optionsModal/index.js +++ b/cypress/elements/optionsModal/index.js @@ -51,19 +51,19 @@ export { } from './axes.js' export { - clickTrendLineCheckbox, + checkTrendLineCheckbox, selectTrendLineType, - clickTargetLineCheckbox, + checkTargetLineCheckbox, setTargetLineValue, setTargetLineLabel, - clickBaseLineCheckbox, + checkBaseLineCheckbox, setBaseLineLabel, setBaseLineValue, } from './lines.js' export { setCustomSubtitle } from './subtitle.js' -export { clickOutliersCheckbox } from './outliers.js' +export { checkOutliersCheckbox } from './outliers.js' export { setItemToAxis, setItemToType } from './series.js' diff --git a/cypress/elements/optionsModal/lines.js b/cypress/elements/optionsModal/lines.js index d7368fa505..6402fa1534 100644 --- a/cypress/elements/optionsModal/lines.js +++ b/cypress/elements/optionsModal/lines.js @@ -1,4 +1,4 @@ -import { clickCheckbox, typeInput } from '../common.js' +import { checkCheckbox, typeInput } from '../common.js' const trendLineCheckboxEl = 'option-trend-line-checkbox' const trendLineSelectEl = 'option-trend-line-select' @@ -10,14 +10,14 @@ const baseLineCheckboxEl = 'option-base-line-checkbox' const baseLineValueInputEl = 'option-base-line-value-input' const baseLineLabelInputEl = 'option-base-line-label-input' -export const clickTrendLineCheckbox = () => clickCheckbox(trendLineCheckboxEl) +export const checkTrendLineCheckbox = () => checkCheckbox(trendLineCheckboxEl) export const selectTrendLineType = (optionName) => { cy.getBySel(trendLineSelectEl).findBySel('dhis2-uicore-select').click() cy.getBySel(trendLineSelectOptionEl).contains(optionName).click() } -export const clickTargetLineCheckbox = () => clickCheckbox(targetLineCheckboxEl) +export const checkTargetLineCheckbox = () => checkCheckbox(targetLineCheckboxEl) export const setTargetLineValue = (text) => typeInput(targetLineValueInputEl, text) @@ -25,7 +25,7 @@ export const setTargetLineValue = (text) => export const setTargetLineLabel = (text) => typeInput(targetLineLabelInputEl, text) -export const clickBaseLineCheckbox = () => clickCheckbox(baseLineCheckboxEl) +export const checkBaseLineCheckbox = () => checkCheckbox(baseLineCheckboxEl) export const setBaseLineValue = (text) => typeInput(baseLineValueInputEl, text) diff --git a/cypress/elements/optionsModal/outliers.js b/cypress/elements/optionsModal/outliers.js index 93db0ea56b..678a4e2e18 100644 --- a/cypress/elements/optionsModal/outliers.js +++ b/cypress/elements/optionsModal/outliers.js @@ -1,5 +1,5 @@ -import { clickCheckbox } from '../common.js' +import { checkCheckbox } from '../common.js' const outliersCheckboxEl = 'option-outliers-enabled-checkbox' -export const clickOutliersCheckbox = () => clickCheckbox(outliersCheckboxEl) +export const checkOutliersCheckbox = () => checkCheckbox(outliersCheckboxEl) diff --git a/cypress/elements/optionsModal/totals.js b/cypress/elements/optionsModal/totals.js new file mode 100644 index 0000000000..bbe931be43 --- /dev/null +++ b/cypress/elements/optionsModal/totals.js @@ -0,0 +1,100 @@ +export const colTotalsOptionEl = 'option-col-totals' +const colSubTotalsOptionEl = 'option-col-subtotals' +const rowTotalsOptionEl = 'option-row-totals' +const rowSubTotalsOptionEl = 'option-row-subtotals' + +export const expectColumnsTotalsToBeDisabled = () => + cy + .getBySel(colTotalsOptionEl) + .find('[type="checkbox"]') + .should('be.disabled') + +export const expectColumnsTotalsToBeEnabled = () => + cy + .getBySel(colTotalsOptionEl) + .find('[type="checkbox"]') + .should('not.be.disabled') + +export const expectColumnsTotalsToBeChecked = () => + cy + .getBySel(colTotalsOptionEl) + .find('[type="checkbox"]') + .should('be.checked') + +export const expectColumnsTotalsToBeUnchecked = () => + cy + .getBySel(colTotalsOptionEl) + .find('[type="checkbox"]') + .should('not.be.checked') + +export const expectColumnsSubTotalsToBeDisabled = () => + cy + .getBySel(colSubTotalsOptionEl) + .find('[type="checkbox"]') + .should('be.disabled') + +export const expectColumnsSubTotalsToBeEnabled = () => + cy + .getBySel(colSubTotalsOptionEl) + .find('[type="checkbox"]') + .should('be.enabled') + +export const expectColumnsSubTotalsToBeChecked = () => + cy + .getBySel(colSubTotalsOptionEl) + .find('[type="checkbox"]') + .should('be.checked') + +export const expectColumnsSubTotalsToBeUnchecked = () => + cy + .getBySel(colSubTotalsOptionEl) + .find('[type="checkbox"]') + .should('not.be.checked') + +export const expectRowsTotalsToBeDisabled = () => + cy + .getBySel(rowTotalsOptionEl) + .find('[type="checkbox"]') + .should('be.disabled') + +export const expectRowsTotalsToBeEnabled = () => + cy + .getBySel(rowTotalsOptionEl) + .find('[type="checkbox"]') + .should('be.enabled') + +export const expectRowsTotalsToBeChecked = () => + cy + .getBySel(rowTotalsOptionEl) + .find('[type="checkbox"]') + .should('be.checked') + +export const expectRowsTotalsToBeUnchecked = () => + cy + .getBySel(rowTotalsOptionEl) + .find('[type="checkbox"]') + .should('not.be.checked') + +export const expectRowsSubTotalsToBeDisabled = () => + cy + .getBySel(rowSubTotalsOptionEl) + .find('[type="checkbox"]') + .should('be.disabled') + +export const expectRowsSubTotalsToBeEnabled = () => + cy + .getBySel(rowSubTotalsOptionEl) + .find('[type="checkbox"]') + .should('be.enabled') + +export const expectRowsSubTotalsToBeChecked = () => + cy + .getBySel(rowSubTotalsOptionEl) + .find('[type="checkbox"]') + .should('be.checked') + +export const expectRowsSubTotalsToBeUnchecked = () => + cy + .getBySel(rowSubTotalsOptionEl) + .find('[type="checkbox"]') + .should('not.be.checked') diff --git a/cypress/elements/pivotTable.js b/cypress/elements/pivotTable.js index e92d64a714..3b41d3982d 100644 --- a/cypress/elements/pivotTable.js +++ b/cypress/elements/pivotTable.js @@ -1,7 +1,14 @@ const valueCellEl = 'visualization-value-cell' +const headerCellEl = 'visualization-column-header' export const clickTableValueCell = (index) => cy.getBySel(valueCellEl).eq(index).click() export const expectTableValueCellsToHaveLength = (length) => cy.getBySel(valueCellEl).should('have.length', length) + +export const expectTableValueCellToContainValue = (index, value) => + cy.getBySel(valueCellEl).eq(index).contains(value) + +export const clickTableHeaderCell = (name) => + cy.getBySel(headerCellEl).contains(name).click() diff --git a/cypress/integration/options/cumulativeValues.cy.js b/cypress/integration/options/cumulativeValues.cy.js new file mode 100644 index 0000000000..0334f46e7e --- /dev/null +++ b/cypress/integration/options/cumulativeValues.cy.js @@ -0,0 +1,221 @@ +import { + AXIS_ID_COLUMNS, + AXIS_ID_ROWS, + DIMENSION_ID_DATA, + DIMENSION_ID_PERIOD, + VIS_TYPE_PIVOT_TABLE, + visTypeDisplayNames, +} from '@dhis2/analytics' +import { + clickNewCalculationButton, + clickSaveButton, + inputCalculationLabel, + selectOperatorFromListByDoubleClick, + typeInNumberField, +} from '../../elements/calculationsModal.js' +import { checkCheckbox, uncheckCheckbox } from '../../elements/common.js' +import { + clickDimensionModalHideButton, + clickDimensionModalUpdateButton, + selectDataElements, + selectFixedPeriods, + unselectAllItemsByButton, +} from '../../elements/dimensionModal/index.js' +import { openDimension } from '../../elements/dimensionsPanel.js' +import { clickContextMenuMove, openContextMenu } from '../../elements/layout.js' +import { openOptionsModal } from '../../elements/menuBar.js' +import { + OPTIONS_TAB_DATA, + OPTIONS_TAB_LEGEND, + clickOptionsModalHideButton, + clickOptionsModalUpdateButton, + clickOptionsTab, +} from '../../elements/optionsModal/index.js' +import { + colTotalsOptionEl, + expectColumnsTotalsToBeChecked, + expectColumnsTotalsToBeDisabled, + expectColumnsSubTotalsToBeDisabled, + expectRowsTotalsToBeDisabled, + expectRowsSubTotalsToBeDisabled, + expectColumnsTotalsToBeEnabled, + expectColumnsSubTotalsToBeEnabled, + expectRowsTotalsToBeEnabled, + expectRowsSubTotalsToBeEnabled, +} from '../../elements/optionsModal/totals.js' +import { + expectTableValueCellToContainValue, + clickTableHeaderCell, +} from '../../elements/pivotTable.js' +import { goToStartPage } from '../../elements/startScreen.js' +import { changeVisType } from '../../elements/visualizationTypeSelector.js' +import { TEST_DATA_ELEMENTS } from '../../utils/data.js' + +const cumulativeValuesOptionEl = 'option-cumulative-values' + +describe('Options - Cumulative values', () => { + describe('Interaction with other options (only for PT)', () => { + beforeEach(() => { + goToStartPage() + changeVisType(visTypeDisplayNames[VIS_TYPE_PIVOT_TABLE]) + }) + + it('disables/enables Totals, Number type and Legend options when cumulativeValues is checked/unchecked', () => { + openOptionsModal(OPTIONS_TAB_DATA) + checkCheckbox(cumulativeValuesOptionEl) + + // Totals + expectColumnsTotalsToBeDisabled() + expectColumnsSubTotalsToBeDisabled() + expectRowsTotalsToBeDisabled() + expectRowsSubTotalsToBeDisabled() + + // Number type + cy.getBySel('option-number-type-select') + .should('contain', 'Not supported when using cumulative values') + .find('[data-test="dhis2-uicore-select-input"]') + .should('have.class', 'disabled') + + // Legend + clickOptionsTab(OPTIONS_TAB_LEGEND) + cy.getBySel('option-legend') + .should('contain', 'Not supported when using cumulative values') + .find('[type="checkbox"]') + .should('be.disabled') + + clickOptionsTab(OPTIONS_TAB_DATA) + uncheckCheckbox(cumulativeValuesOptionEl) + + // Totals + expectColumnsTotalsToBeEnabled() + expectColumnsSubTotalsToBeEnabled() + expectRowsTotalsToBeEnabled() + expectRowsSubTotalsToBeEnabled() + + // Number type + cy.getBySel('option-number-type-select') + .should( + 'not.contain', + 'Not supported when using cumulative values' + ) + .find('[data-test="dhis2-uicore-select-input"]') + .should('not.have.class', 'disabled') + + // Legend + clickOptionsTab(OPTIONS_TAB_LEGEND) + cy.getBySel('option-legend') + .should( + 'not.contain', + 'Not supported when using cumulative values' + ) + .find('[type="checkbox"]') + .should('not.be.disabled') + + clickOptionsModalHideButton() + }) + + it('disables/enables a total option preserving its state', () => { + openOptionsModal(OPTIONS_TAB_DATA) + checkCheckbox(colTotalsOptionEl) + + expectColumnsTotalsToBeChecked() + + checkCheckbox(cumulativeValuesOptionEl) + + expectColumnsTotalsToBeDisabled() + expectColumnsTotalsToBeChecked() + + uncheckCheckbox(cumulativeValuesOptionEl) + + expectColumnsTotalsToBeEnabled() + expectColumnsTotalsToBeChecked() + + clickOptionsModalHideButton() + }) + }) + + describe('Applying cumulativeValues: Pivot table', () => { + beforeEach(() => { + goToStartPage() + changeVisType(visTypeDisplayNames[VIS_TYPE_PIVOT_TABLE]) + }) + + it('correctly shows the cumulative values', () => { + openContextMenu(DIMENSION_ID_DATA) + clickContextMenuMove(DIMENSION_ID_DATA, AXIS_ID_ROWS) + openContextMenu(DIMENSION_ID_PERIOD) + clickContextMenuMove(DIMENSION_ID_PERIOD, AXIS_ID_COLUMNS) + + // create a calculation to facilitate testing the cumulative values + openDimension(DIMENSION_ID_DATA) + clickNewCalculationButton() + selectOperatorFromListByDoubleClick('Number') + typeInNumberField(1, 1) + inputCalculationLabel('test data for cumulativeValues') + clickSaveButton() + + clickDimensionModalUpdateButton() + + openOptionsModal(OPTIONS_TAB_DATA) + checkCheckbox(cumulativeValuesOptionEl) + clickOptionsModalUpdateButton() + + Array.from({ length: 12 }, (_, i) => i).forEach((i) => + expectTableValueCellToContainValue(i, i + 1) + ) + }) + + it('correctly sort a column with cumulative values', () => { + openContextMenu(DIMENSION_ID_DATA) + clickContextMenuMove(DIMENSION_ID_DATA, AXIS_ID_ROWS) + openContextMenu(DIMENSION_ID_PERIOD) + clickContextMenuMove(DIMENSION_ID_PERIOD, AXIS_ID_COLUMNS) + + const year = new Date().getFullYear().toString() + + openDimension(DIMENSION_ID_PERIOD) + unselectAllItemsByButton() + selectFixedPeriods( + [`October ${year}`, `November ${year}`, `December ${year}`], + 'Monthly' + ) + clickDimensionModalHideButton() + + // create a calculation to facilitate testing the cumulative values + openDimension(DIMENSION_ID_DATA) + clickNewCalculationButton() + selectOperatorFromListByDoubleClick('Number') + typeInNumberField(1, 6000) + inputCalculationLabel( + 'test data for sorting cumulative values sorting' + ) + clickSaveButton() + + selectDataElements([TEST_DATA_ELEMENTS[4].name]) + + clickDimensionModalUpdateButton() + + // sort before cumulative + expectTableValueCellToContainValue(2, '6 000') + expectTableValueCellToContainValue(5, '5 266') + + clickTableHeaderCell(`December ${year}`) + + expectTableValueCellToContainValue(2, '5 266') + expectTableValueCellToContainValue(5, '6 000') + + // sort after cumulative + openOptionsModal(OPTIONS_TAB_DATA) + checkCheckbox(cumulativeValuesOptionEl) + clickOptionsModalUpdateButton() + + expectTableValueCellToContainValue(2, '18 000') + expectTableValueCellToContainValue(5, '18 488') + + clickTableHeaderCell(`December ${year}`) + + expectTableValueCellToContainValue(2, '18 000') + expectTableValueCellToContainValue(5, '18 488') + }) + }) +}) diff --git a/cypress/integration/options/fontStyles.cy.js b/cypress/integration/options/fontStyles.cy.js index 15408d334f..2cbee29b55 100644 --- a/cypress/integration/options/fontStyles.cy.js +++ b/cypress/integration/options/fontStyles.cy.js @@ -20,10 +20,10 @@ import { OPTIONS_TAB_STYLE, OPTIONS_TAB_DATA, OPTIONS_TAB_AXES, - clickTargetLineCheckbox, + checkTargetLineCheckbox, setTargetLineValue, setTargetLineLabel, - clickBaseLineCheckbox, + checkBaseLineCheckbox, setBaseLineLabel, setBaseLineValue, setAxisTitleText, @@ -221,7 +221,7 @@ describe('Options - Font styles', () => { }) it('sets target line', () => { cy.log(`Test value: ${TEST_VALUE}`) - clickTargetLineCheckbox() + checkTargetLineCheckbox() setTargetLineLabel(TEST_LABEL) setTargetLineValue(TEST_VALUE) }) @@ -280,7 +280,7 @@ describe('Options - Font styles', () => { }) it('sets base line', () => { cy.log(`Test value: ${TEST_VALUE}`) - clickBaseLineCheckbox() + checkBaseLineCheckbox() setBaseLineLabel(TEST_LABEL) setBaseLineValue(TEST_VALUE) }) diff --git a/cypress/integration/options/icon.cy.js b/cypress/integration/options/icon.cy.js index 541e586eac..ea90015372 100644 --- a/cypress/integration/options/icon.cy.js +++ b/cypress/integration/options/icon.cy.js @@ -9,7 +9,7 @@ import { visTypeDisplayNames, } from '@dhis2/analytics' import { expectVisualizationToBeVisible } from '../../elements/chart.js' -import { clickCheckbox } from '../../elements/common.js' +import { checkCheckbox } from '../../elements/common.js' import { expectSelectableDataItemsAmountToBeLeast, switchDataTypeTo, @@ -98,7 +98,7 @@ describe('Icon', () => { it(`icon shows when option is enabled for ${type}`, () => { // enable the icon openOptionsModal(OPTIONS_TAB_STYLE) - clickCheckbox('option-show-data-item-icon') + checkCheckbox('option-show-data-item-icon') clickOptionsModalHideButton() // find the data item @@ -129,7 +129,7 @@ describe('Icon', () => { it.skip('icon gets correct color when a legend is in use', () => { // enable the icon openOptionsModal(OPTIONS_TAB_STYLE) - clickCheckbox('option-show-data-item-icon') + checkCheckbox('option-show-data-item-icon') // enable the legend clickOptionsTab(OPTIONS_TAB_LEGEND) diff --git a/cypress/integration/options/lines.cy.js b/cypress/integration/options/lines.cy.js index ea8fef0357..3a332aeb84 100644 --- a/cypress/integration/options/lines.cy.js +++ b/cypress/integration/options/lines.cy.js @@ -11,7 +11,7 @@ import { openDimension } from '../../elements/dimensionsPanel.js' import { openOptionsModal } from '../../elements/menuBar.js' import { clickOptionsModalUpdateButton, - clickTrendLineCheckbox, + checkTrendLineCheckbox, OPTIONS_TAB_DATA, selectTrendLineType, } from '../../elements/optionsModal/index.js' @@ -54,7 +54,7 @@ describe('Options - Lines', () => { }) if (index === 0) { it('enables trendline', () => { - clickTrendLineCheckbox() + checkTrendLineCheckbox() }) } it('selects trendline type', () => { diff --git a/cypress/integration/visTypes/scatter.cy.js b/cypress/integration/visTypes/scatter.cy.js index 6fba12f094..d35eba4e66 100644 --- a/cypress/integration/visTypes/scatter.cy.js +++ b/cypress/integration/visTypes/scatter.cy.js @@ -41,7 +41,7 @@ import { } from '../../elements/menuBar.js' import { clickOptionsModalUpdateButton, - clickOutliersCheckbox, + checkOutliersCheckbox, OPTIONS_TAB_AXES, OPTIONS_TAB_OUTLIERS, setAxisRangeMaxValue, @@ -180,7 +180,7 @@ describe('using a Scatter chart', () => { }) it('Options -> Outliers -> enables outliers', () => { openOptionsModal(OPTIONS_TAB_OUTLIERS) - clickOutliersCheckbox() + checkOutliersCheckbox() // TODO: Set more outlier options clickOptionsModalUpdateButton() expectVisualizationToBeVisible(VIS_TYPE_SCATTER) diff --git a/docs/data-visualizer.md b/docs/data-visualizer.md index 2978d41418..3dc516c7a4 100644 --- a/docs/data-visualizer.md +++ b/docs/data-visualizer.md @@ -165,7 +165,7 @@ The display of a visualization can be changed by enabling/disabling and configur | Base line | Displays a horizontal line at the given domain value. Useful for example when you want to visualize how your performance has evolved since the beginning of a process. | | Column sub-totals | Displays sub-totals in a Pivot table for each dimension.
If you only select one dimension, sub-totals will be hidden for those columns. This is because the values will be equal to the sub-totals. | | Column totals | Displays total values in a Pivot table for each column, as well as a total for all values in the table. | -| Cumulative values | Displays cumulative values in Column, Stacked column, Bar, Stacked bar, Line and Area visualizations | +| Cumulative values | Displays cumulative values in Column, Stacked column, Bar, Stacked bar, Line, Area and Pivot Table visualizations | | Custom sort order | Controls the sort order of the values. | | Dimension labels | Shows the dimension names as part of a Pivot table. | | Hide empty categories | Hides the category items with no data from the visualization.
**Before first**: hides missing values only before the first value
**After last**: hides missing values only after the last value
**Before first and after last**: hides missing values only before the first value and after the last value
**All**: hides all missing values
This is useful for example when you create Column and Bar visualizations. | diff --git a/i18n/en.pot b/i18n/en.pot index 8e76313004..ec2d6dac34 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-06T13:41:14.540Z\n" -"PO-Revision-Date: 2023-09-06T13:41:14.540Z\n" +"POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" +"PO-Revision-Date: 2023-11-13T12:11:28.959Z\n" msgid "All items" msgstr "All items" @@ -381,6 +381,9 @@ msgstr "Include cumulative" msgid "Cumulative values" msgstr "Cumulative values" +msgid "Accumulate cell values along rows" +msgstr "Accumulate cell values along rows" + msgid "Show data item icon" msgstr "Show data item icon" @@ -709,6 +712,9 @@ msgstr "Open as Map" msgid "Visually plot data on a world map. Data elements use separate map layers." msgstr "Visually plot data on a world map. Data elements use separate map layers." +msgid "Not supported when using cumulative values" +msgstr "Not supported when using cumulative values" + msgid "No data available" msgstr "No data available" @@ -1002,6 +1008,9 @@ msgstr "Lines" msgid "Totals" msgstr "Totals" +msgid "Totals are not supported when using cumulative values" +msgstr "Totals are not supported when using cumulative values" + msgid "Vertical (y) axis {{axisId}}" msgstr "Vertical (y) axis {{axisId}}" diff --git a/package.json b/package.json index 1427db228d..7901abb430 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "start-server-and-test": "^2.0.0" }, "dependencies": { - "@dhis2/analytics": "^26.1.6", + "@dhis2/analytics": "^26.2.0", "@dhis2/app-runtime": "^3.7.0", "@dhis2/app-runtime-adapter-d2": "^1.1.0", "@dhis2/app-service-datastore": "^1.0.0-beta.3", diff --git a/src/actions/ui.js b/src/actions/ui.js index fe3e3fcdfe..df704580f7 100644 --- a/src/actions/ui.js +++ b/src/actions/ui.js @@ -1,8 +1,10 @@ +import { getDisabledOptions } from '../modules/disabledOptions.js' import { SET_UI, CLEAR_UI, SET_UI_FROM_VISUALIZATION, SET_UI_TYPE, + SET_UI_DISABLED_OPTIONS, SET_UI_OPTIONS, SET_UI_LAYOUT, ADD_UI_LAYOUT_DIMENSIONS, @@ -21,6 +23,8 @@ import { UPDATE_UI_SERIES_ITEM, SET_UI_OPTION, SET_UI_OPTION_FONT_STYLE, + sGetUiType, + sGetUiOptions, } from '../reducers/ui.js' export const acSetUi = (value) => ({ @@ -43,6 +47,11 @@ export const acSetUiType = (value) => ({ value, }) +export const acSetUiDisabledOptions = (value) => ({ + type: SET_UI_DISABLED_OPTIONS, + value, +}) + export const acSetUiOptions = (value) => ({ type: SET_UI_OPTIONS, value, @@ -129,3 +138,15 @@ export const acSetUiRightSidebarOpen = () => ({ export const acClearSeriesType = () => ({ type: CLEAR_SERIES_TYPE, }) + +export const tSetUiOptionAndDisabledOptions = + (option) => (dispatch, getState) => { + dispatch(acSetUiOption(option)) + + const visType = sGetUiType(getState()) + const options = sGetUiOptions(getState()) + + dispatch( + acSetUiDisabledOptions(getDisabledOptions({ visType, options })) + ) + } diff --git a/src/components/Visualization/Visualization.js b/src/components/Visualization/Visualization.js index 2d67a2bef0..0b67d8a528 100644 --- a/src/components/Visualization/Visualization.js +++ b/src/components/Visualization/Visualization.js @@ -3,7 +3,6 @@ import debounce from 'lodash-es/debounce' import PropTypes from 'prop-types' import React, { Component, Fragment } from 'react' import { connect } from 'react-redux' -import { createSelector } from 'reselect' import { acSetChart } from '../../actions/chart.js' import { tSetCurrentFromUi } from '../../actions/current.js' import { acSetLoadError, acSetPluginLoading } from '../../actions/loader.js' @@ -183,7 +182,7 @@ export class UnconnectedVisualization extends Component { render() { const { visualization, - userSettings, + displayProperty, error, isLoading, onLoadingComplete, @@ -208,7 +207,7 @@ export class UnconnectedVisualization extends Component { onError={this.onError} onDrill={this.onDrill} style={styles.chartCanvas} - displayProperty={userSettings.displayProperty} + displayProperty={displayProperty} /> ) @@ -218,6 +217,7 @@ export class UnconnectedVisualization extends Component { UnconnectedVisualization.propTypes = { addMetadata: PropTypes.func, addParentGraphMap: PropTypes.func, + displayProperty: PropTypes.string, error: PropTypes.object, isLoading: PropTypes.bool, rightSidebarOpen: PropTypes.bool, @@ -225,24 +225,16 @@ UnconnectedVisualization.propTypes = { setCurrent: PropTypes.func, setLoadError: PropTypes.func, setUiItems: PropTypes.func, - userSettings: PropTypes.object, visualization: PropTypes.object, onLoadingComplete: PropTypes.func, } -export const userSettingsSelector = createSelector( - [sGetSettingsDisplayProperty], - (displayProperty) => ({ - displayProperty, - }) -) - const mapStateToProps = (state) => ({ visualization: sGetCurrent(state), rightSidebarOpen: sGetUiRightSidebarOpen(state), error: sGetLoadError(state), isLoading: sGetIsPluginLoading(state), - userSettings: userSettingsSelector(state), + displayProperty: sGetSettingsDisplayProperty(state), }) const mapDispatchToProps = (dispatch) => ({ diff --git a/src/components/VisualizationOptions/Options/CheckboxBaseOption.js b/src/components/VisualizationOptions/Options/CheckboxBaseOption.js index 829dcdf651..4e7ba0e712 100644 --- a/src/components/VisualizationOptions/Options/CheckboxBaseOption.js +++ b/src/components/VisualizationOptions/Options/CheckboxBaseOption.js @@ -2,8 +2,8 @@ import { CheckboxField } from '@dhis2/ui' import PropTypes from 'prop-types' import React from 'react' import { connect } from 'react-redux' -import { acSetUiOption } from '../../../actions/ui.js' -import { sGetUiOption } from '../../../reducers/ui.js' +import { tSetUiOptionAndDisabledOptions } from '../../../actions/ui.js' +import { sGetUiOption, sGetUiDisabledOption } from '../../../reducers/ui.js' import { tabSectionOption, tabSectionOptionToggleable, @@ -19,6 +19,7 @@ export const UnconnectedCheckboxBaseOption = ({ inverted, fontStyleKey, dataTest, + disabled, }) => (
onChange(inverted ? !checked : checked)} dense dataTest={dataTest} + disabled={disabled} /> {((!inverted && value) || (inverted && !value)) && fontStyleKey ? (
@@ -43,6 +45,7 @@ export const UnconnectedCheckboxBaseOption = ({ UnconnectedCheckboxBaseOption.propTypes = { dataTest: PropTypes.string, + disabled: PropTypes.bool, fontStyleKey: PropTypes.string, helpText: PropTypes.string, inverted: PropTypes.bool, @@ -53,18 +56,25 @@ UnconnectedCheckboxBaseOption.propTypes = { } const mapStateToProps = (state, ownProps) => ({ + disabled: Boolean( + sGetUiDisabledOption(state, ownProps.option) ?? ownProps.disabled + ), + helpText: + sGetUiDisabledOption(state, ownProps.option)?.helpText || + ownProps.helpText, value: sGetUiOption(state, ownProps.option) || false, }) const mapDispatchToProps = (dispatch, ownProps) => ({ - onChange: (value) => + onChange: (value) => { dispatch( - acSetUiOption({ + tSetUiOptionAndDisabledOptions({ optionId: ownProps.option.id || ownProps.option.name, axisId: ownProps.option.axisId, value, }) - ), + ) + }, }) export const CheckboxBaseOption = connect( diff --git a/src/components/VisualizationOptions/Options/ColSubTotals.js b/src/components/VisualizationOptions/Options/ColSubTotals.js index bcb36c01d7..1d5e922278 100644 --- a/src/components/VisualizationOptions/Options/ColSubTotals.js +++ b/src/components/VisualizationOptions/Options/ColSubTotals.js @@ -8,6 +8,7 @@ const ColSubTotals = () => ( option={{ name: 'colSubTotals', }} + dataTest="option-col-subtotals" /> ) diff --git a/src/components/VisualizationOptions/Options/ColTotals.js b/src/components/VisualizationOptions/Options/ColTotals.js index c67b8650e2..322a1dcfbd 100644 --- a/src/components/VisualizationOptions/Options/ColTotals.js +++ b/src/components/VisualizationOptions/Options/ColTotals.js @@ -8,6 +8,7 @@ const ColTotals = () => ( option={{ name: 'colTotals', }} + dataTest="option-col-totals" /> ) diff --git a/src/components/VisualizationOptions/Options/CumulativeValues.js b/src/components/VisualizationOptions/Options/CumulativeValues.js index b4cb4fa7ac..630cc17e90 100644 --- a/src/components/VisualizationOptions/Options/CumulativeValues.js +++ b/src/components/VisualizationOptions/Options/CumulativeValues.js @@ -1,14 +1,27 @@ +import { VIS_TYPE_PIVOT_TABLE } from '@dhis2/analytics' import i18n from '@dhis2/d2-i18n' import React from 'react' +import { useSelector } from 'react-redux' +import { sGetUiType } from '../../../reducers/ui.js' import { CheckboxBaseOption } from './CheckboxBaseOption.js' -const CumulativeValues = () => ( - -) +const CumulativeValues = () => { + const visType = useSelector(sGetUiType) + + return ( + + ) +} export default CumulativeValues diff --git a/src/components/VisualizationOptions/Options/Legend.js b/src/components/VisualizationOptions/Options/Legend.js index 30e4825be1..03923b2979 100644 --- a/src/components/VisualizationOptions/Options/Legend.js +++ b/src/components/VisualizationOptions/Options/Legend.js @@ -4,7 +4,7 @@ import { LEGEND_DISPLAY_STYLE_FILL, } from '@dhis2/analytics' import i18n from '@dhis2/d2-i18n' -import { Checkbox, FieldSet, Legend as UiCoreLegend } from '@dhis2/ui' +import { Checkbox, FieldSet, Help, Legend as UiCoreLegend } from '@dhis2/ui' import cx from 'classnames' import PropTypes from 'prop-types' import React, { useState } from 'react' @@ -15,22 +15,27 @@ import { OPTION_LEGEND_DISPLAY_STYLE, OPTION_LEGEND_SET, } from '../../../modules/options.js' -import { sGetUiOption } from '../../../reducers/ui.js' +import { sGetUiOption, sGetUiDisabledOption } from '../../../reducers/ui.js' import { tabSectionOptionToggleable, tabSectionOption, tabSectionTitle, + tabSectionTitleDisabled, tabSectionTitleMargin, } from '../styles/VisualizationOptions.style.js' import LegendDisplayStrategy from './LegendDisplayStrategy.js' import LegendDisplayStyle from './LegendDisplayStyle.js' import ShowLegendKey from './ShowLegendKey.js' +const optionName = 'legend' + const Legend = ({ legendSet, legendDisplayStrategy, onChange, + helpText, hideStyleOptions, + disabled, }) => { const [legendEnabled, setLegendEnabled] = useState( !( @@ -69,9 +74,11 @@ const Legend = ({ } return ( -
+
{i18n.t('Legend style')}
- +
@@ -104,33 +115,44 @@ const Legend = ({ className={cx(tabSectionTitle.className, { [tabSectionTitleMargin.className]: hideStyleOptions, + [tabSectionTitleDisabled.className]: + disabled, })} > {i18n.t('Legend type')}
- +
- +
) : null} + {helpText && ( + + {helpText} + + )}
) } Legend.propTypes = { onChange: PropTypes.func.isRequired, + disabled: PropTypes.bool, + helpText: PropTypes.string, hideStyleOptions: PropTypes.bool, legendDisplayStrategy: PropTypes.string, legendSet: PropTypes.object, } const mapStateToProps = (state) => ({ + disabled: Boolean(sGetUiDisabledOption(state, { name: optionName })), + helpText: sGetUiDisabledOption(state, { name: optionName })?.helpText, legendSet: sGetUiOption(state, { id: OPTION_LEGEND_SET }), legendDisplayStrategy: sGetUiOption(state, { id: OPTION_LEGEND_DISPLAY_STRATEGY, diff --git a/src/components/VisualizationOptions/Options/LegendDisplayStrategy.js b/src/components/VisualizationOptions/Options/LegendDisplayStrategy.js index 44ee0b5a33..8f3d16ce0c 100644 --- a/src/components/VisualizationOptions/Options/LegendDisplayStrategy.js +++ b/src/components/VisualizationOptions/Options/LegendDisplayStrategy.js @@ -13,13 +13,14 @@ import { sGetUiOption } from '../../../reducers/ui.js' import { tabSectionOptionToggleable } from '../styles/VisualizationOptions.style.js' import LegendSet from './LegendSet.js' -const LegendDisplayStrategy = ({ value, onChange }) => ( +const LegendDisplayStrategy = ({ value, onChange, disabled }) => ( ( key={LEGEND_DISPLAY_STRATEGY_FIXED} label={i18n.t('Select a legend')} value={LEGEND_DISPLAY_STRATEGY_FIXED} + disabled={disabled} checked={value === LEGEND_DISPLAY_STRATEGY_FIXED} onChange={onChange} dense @@ -37,7 +39,7 @@ const LegendDisplayStrategy = ({ value, onChange }) => ( {value === LEGEND_DISPLAY_STRATEGY_FIXED ? (
- +
) : null}
@@ -46,6 +48,7 @@ const LegendDisplayStrategy = ({ value, onChange }) => ( LegendDisplayStrategy.propTypes = { value: PropTypes.string.isRequired, onChange: PropTypes.func.isRequired, + disabled: PropTypes.bool, } const mapStateToProps = (state) => ({ diff --git a/src/components/VisualizationOptions/Options/LegendDisplayStyle.js b/src/components/VisualizationOptions/Options/LegendDisplayStyle.js index bd7699f782..819fdaa481 100644 --- a/src/components/VisualizationOptions/Options/LegendDisplayStyle.js +++ b/src/components/VisualizationOptions/Options/LegendDisplayStyle.js @@ -3,11 +3,12 @@ import { LEGEND_DISPLAY_STYLE_TEXT, } from '@dhis2/analytics' import i18n from '@dhis2/d2-i18n' +import PropTypes from 'prop-types' import React from 'react' import { OPTION_LEGEND_DISPLAY_STYLE } from '../../../modules/options.js' import { default as RadioBaseOption } from './RadioBaseOption.js' -const LegendDisplayStyle = () => ( +const LegendDisplayStyle = ({ disabled }) => ( ( }, ], }} + disabled={disabled} dataTest={'legend-display-style'} /> ) +LegendDisplayStyle.propTypes = { + disabled: PropTypes.bool, +} + export default LegendDisplayStyle diff --git a/src/components/VisualizationOptions/Options/LegendSet.js b/src/components/VisualizationOptions/Options/LegendSet.js index 2fa17a0ec7..307c62afe2 100644 --- a/src/components/VisualizationOptions/Options/LegendSet.js +++ b/src/components/VisualizationOptions/Options/LegendSet.js @@ -29,10 +29,12 @@ const LegendSetSelect = ({ onFocus, onChange, dataTest, + disabled, }) => ( { +const LegendSet = ({ value, onChange, disabled, dataTest }) => { const engine = useDataEngine() const [options, setOptions] = useState([]) @@ -103,6 +106,7 @@ const LegendSet = ({ value, onChange, dataTest }) => { return ( { LegendSet.propTypes = { onChange: PropTypes.func.isRequired, dataTest: PropTypes.string, + disabled: PropTypes.bool, value: PropTypes.object, } diff --git a/src/components/VisualizationOptions/Options/NumberType.js b/src/components/VisualizationOptions/Options/NumberType.js index b69ee1047b..82e24c1800 100644 --- a/src/components/VisualizationOptions/Options/NumberType.js +++ b/src/components/VisualizationOptions/Options/NumberType.js @@ -6,6 +6,7 @@ const NumberType = () => ( ({ + disabled: Boolean( + sGetUiDisabledOption(state, ownProps.option) ?? ownProps.disabled + ), value: ownProps.option.id ? sGetUiOption(state, { id: ownProps.option.id }) : sGetUiOptions(state)[ownProps.option.name], diff --git a/src/components/VisualizationOptions/Options/RowSubTotals.js b/src/components/VisualizationOptions/Options/RowSubTotals.js index 38c1c53f46..f886ec6ee8 100644 --- a/src/components/VisualizationOptions/Options/RowSubTotals.js +++ b/src/components/VisualizationOptions/Options/RowSubTotals.js @@ -8,6 +8,7 @@ const RowSubTotals = () => ( option={{ name: 'rowSubTotals', }} + dataTest="option-row-subtotals" /> ) diff --git a/src/components/VisualizationOptions/Options/RowTotals.js b/src/components/VisualizationOptions/Options/RowTotals.js index ddf7028871..5daa6f2597 100644 --- a/src/components/VisualizationOptions/Options/RowTotals.js +++ b/src/components/VisualizationOptions/Options/RowTotals.js @@ -8,6 +8,7 @@ const RowTotals = () => ( option={{ name: 'rowTotals', }} + dataTest="option-row-totals" /> ) diff --git a/src/components/VisualizationOptions/Options/SelectBaseOption.js b/src/components/VisualizationOptions/Options/SelectBaseOption.js index af8420046e..d6d6c94abb 100644 --- a/src/components/VisualizationOptions/Options/SelectBaseOption.js +++ b/src/components/VisualizationOptions/Options/SelectBaseOption.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types' import React, { useState } from 'react' import { connect } from 'react-redux' import { acSetUiOptions } from '../../../actions/ui.js' -import { sGetUiOptions } from '../../../reducers/ui.js' +import { sGetUiOption, sGetUiDisabledOption } from '../../../reducers/ui.js' import { tabSectionOption, tabSectionOptionToggleable, @@ -41,7 +41,7 @@ export const UnconnectedSelectBaseOption = ({ dataTest={`${dataTest}-checkbox`} /> ) : null} - {(!toggleable || checked) && !disabled ? ( + {!toggleable || checked ? (
{option.items.map(({ value, label }) => ( ({ - value: sGetUiOptions(state)[ownProps.option.name], + disabled: Boolean(sGetUiDisabledOption(state, ownProps.option)), + helpText: + sGetUiDisabledOption(state, ownProps.option)?.helpText || + ownProps.helpText, + value: sGetUiOption(state, ownProps.option), }) const mapDispatchToProps = (dispatch, ownProps) => ({ diff --git a/src/components/VisualizationOptions/Options/ShowLegendKey.js b/src/components/VisualizationOptions/Options/ShowLegendKey.js index a5211b697e..bb999582fb 100644 --- a/src/components/VisualizationOptions/Options/ShowLegendKey.js +++ b/src/components/VisualizationOptions/Options/ShowLegendKey.js @@ -1,16 +1,22 @@ import i18n from '@dhis2/d2-i18n' +import PropTypes from 'prop-types' import React from 'react' import { OPTION_SHOW_LEGEND_KEY } from '../../../modules/options.js' import { CheckboxBaseOption } from './CheckboxBaseOption.js' -const ShowLegendKey = () => ( +const ShowLegendKey = ({ disabled }) => ( ) +ShowLegendKey.propTypes = { + disabled: PropTypes.bool, +} + export default ShowLegendKey diff --git a/src/components/VisualizationOptions/Options/TextBaseOption.js b/src/components/VisualizationOptions/Options/TextBaseOption.js index 2fac2d5aaf..ac4c4795d7 100644 --- a/src/components/VisualizationOptions/Options/TextBaseOption.js +++ b/src/components/VisualizationOptions/Options/TextBaseOption.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types' import React from 'react' import { connect } from 'react-redux' import { acSetUiOption } from '../../../actions/ui.js' -import { sGetUiOption } from '../../../reducers/ui.js' +import { sGetUiOption, sGetUiDisabledOption } from '../../../reducers/ui.js' import { tabSectionOption, tabSectionOptionToggleable, @@ -112,6 +112,10 @@ UnconnectedTextBaseOption.propTypes = { } const mapStateToProps = (state, ownProps) => ({ + disabled: Boolean(sGetUiDisabledOption(state, ownProps.option)), + helpText: + sGetUiDisabledOption(state, ownProps.option)?.helpText || + ownProps.helpText, value: sGetUiOption(state, ownProps.option) || '', checked: sGetUiOption(state, { diff --git a/src/components/VisualizationOptions/VisualizationOptionsManager.js b/src/components/VisualizationOptions/VisualizationOptionsManager.js index 3bc6608ef0..bae0102753 100644 --- a/src/components/VisualizationOptions/VisualizationOptionsManager.js +++ b/src/components/VisualizationOptions/VisualizationOptionsManager.js @@ -8,6 +8,7 @@ import { HoverMenuList, HoverMenuListItem, VisualizationOptions, + VIS_TYPE_PIVOT_TABLE, } from '@dhis2/analytics' import i18n from '@dhis2/d2-i18n' import PropTypes from 'prop-types' @@ -28,6 +29,7 @@ const VisualizationOptionsManager = ({ rowDimensionItems, columns, series, + cumulativeValues, }) => { const [selectedOptionConfigKey, setSelectedOptionConfigKey] = useState(null) const onOptionsUpdate = (handler) => { @@ -54,6 +56,8 @@ const VisualizationOptionsManager = ({ : [0], hasDimensionItemsInColumns: Boolean(columnDimensionItems.length), hasDimensionItemsInRows: Boolean(rowDimensionItems.length), + hasCumulativeValuesInPt: + visualizationType === VIS_TYPE_PIVOT_TABLE && cumulativeValues, }) return ( @@ -94,6 +98,7 @@ VisualizationOptionsManager.propTypes = { visualizationType: PropTypes.string.isRequired, columnDimensionItems: PropTypes.array, columns: PropTypes.array, + cumulativeValues: PropTypes.bool, rowDimensionItems: PropTypes.array, series: PropTypes.array, } @@ -104,6 +109,7 @@ const mapStateToProps = (state) => ({ rowDimensionItems: sGetDimensionItemsByAxis(state, AXIS_ID_ROWS), series: sGetUiOptions(state).series, columns: sGetUiLayout(state).columns, + cumulativeValues: sGetUiOptions(state).cumulativeValues, }) export default connect(mapStateToProps)(VisualizationOptionsManager) diff --git a/src/components/VisualizationOptions/styles/VisualizationOptions.style.js b/src/components/VisualizationOptions/styles/VisualizationOptions.style.js index 897f544d1a..bedbf42c60 100644 --- a/src/components/VisualizationOptions/styles/VisualizationOptions.style.js +++ b/src/components/VisualizationOptions/styles/VisualizationOptions.style.js @@ -42,6 +42,12 @@ export const tabSectionTitle = css.resolve` } ` +export const tabSectionTitleDisabled = css.resolve` + span { + color: ${colors.grey600}; + } +` + export const tabSectionTitleMargin = css.resolve` span { margin-top: ${spacers.dp8}; diff --git a/src/components/VisualizationPlugin/VisualizationPlugin.js b/src/components/VisualizationPlugin/VisualizationPlugin.js index ec8d731b46..5875e674ae 100644 --- a/src/components/VisualizationPlugin/VisualizationPlugin.js +++ b/src/components/VisualizationPlugin/VisualizationPlugin.js @@ -13,17 +13,20 @@ import { import { useDataEngine } from '@dhis2/app-runtime' import { Button, IconLegend24, Layer } from '@dhis2/ui' import cx from 'classnames' +import cloneDeep from 'lodash-es/cloneDeep' import PropTypes from 'prop-types' import React, { useEffect, useState, useCallback } from 'react' import { apiFetchLegendSets } from '../../api/legendSets.js' +import { getDisabledOptions } from '../../modules/disabledOptions.js' import { fetchData } from '../../modules/fetchData.js' +import { getOptionsFromVisualization } from '../../modules/options.js' import ChartPlugin from './ChartPlugin.js' import ContextualMenu from './ContextualMenu.js' import PivotPlugin from './PivotPlugin.js' import styles from './styles/VisualizationPlugin.module.css' export const VisualizationPlugin = ({ - visualization, + visualization: originalVisualization, displayProperty, filters, forDashboard, @@ -36,6 +39,7 @@ export const VisualizationPlugin = ({ onDrill, }) => { const engine = useDataEngine() + const [visualization, setVisualization] = useState(undefined) const [ouLevels, setOuLevels] = useState(undefined) const [fetchResult, setFetchResult] = useState(null) const [contextualMenuRef, setContextualMenuRef] = useState(undefined) @@ -121,28 +125,24 @@ export const VisualizationPlugin = ({ onDrill(args) } - const doFetchData = useCallback(async () => { - const result = await fetchData({ - dataEngine: engine, - visualization, - filters, - forDashboard, - displayProperty, - }) + const doFetchData = useCallback( + async (visualization, filters, forDashboard) => { + const result = await fetchData({ + dataEngine: engine, + visualization, + filters, + forDashboard, + displayProperty, + }) - if (result.responses.length) { - onResponsesReceived(result.responses) - } + if (result.responses.length) { + onResponsesReceived(result.responses) + } - return result - }, [ - engine, - filters, - forDashboard, - displayProperty, - onResponsesReceived, - visualization, - ]) + return result + }, + [engine, displayProperty, onResponsesReceived] + ) const doFetchLegendSets = useCallback( async (legendSetIds) => { @@ -170,9 +170,23 @@ export const VisualizationPlugin = ({ useEffect(() => { setFetchResult(null) + // filter out disabled options + const disabledOptions = getDisabledOptions({ + visType: originalVisualization.type, + options: getOptionsFromVisualization(originalVisualization), + }) + + const filteredVisualization = cloneDeep(originalVisualization) + + Object.keys(disabledOptions).forEach( + (option) => delete filteredVisualization[option] + ) + + setVisualization(filteredVisualization) + const doFetchAll = async () => { const { responses, extraOptions } = await doFetchData( - visualization, + filteredVisualization, filters, forDashboard ) @@ -186,7 +200,7 @@ export const VisualizationPlugin = ({ // DHIS2-10496: show icon on the side of the single value if an icon is assigned in Maintenance app and // the "Show data item icon" option is set in DV options if ( - Boolean(visualization.icons?.length) && + Boolean(filteredVisualization.icons?.length) && dxIds[0] && responses[0].metaData.items[dxIds[0]]?.style?.icon ) { @@ -207,10 +221,10 @@ export const VisualizationPlugin = ({ const legendSetIds = [] - switch (visualization.legend?.strategy) { + switch (filteredVisualization.legend?.strategy) { case LEGEND_DISPLAY_STRATEGY_FIXED: - if (visualization.legend?.set?.id) { - legendSetIds.push(visualization.legend.set.id) + if (filteredVisualization.legend?.set?.id) { + legendSetIds.push(filteredVisualization.legend.set.id) } break case LEGEND_DISPLAY_STRATEGY_BY_DATA_ITEM: { @@ -230,12 +244,12 @@ export const VisualizationPlugin = ({ const legendSets = await doFetchLegendSets(legendSetIds) setFetchResult({ - visualization, + visualization: filteredVisualization, legendSets, responses, extraOptions, }) - setShowLegendKey(visualization.legend?.showKey) + setShowLegendKey(filteredVisualization.legend?.showKey) onLoadingComplete() } @@ -244,7 +258,7 @@ export const VisualizationPlugin = ({ }) /* eslint-disable-next-line react-hooks/exhaustive-deps */ - }, [visualization, filters, forDashboard]) + }, [originalVisualization, filters, forDashboard]) if (!fetchResult || !ouLevels) { return null @@ -257,7 +271,7 @@ export const VisualizationPlugin = ({ : null let legendSets = [] - switch (visualization.legend?.strategy) { + switch (fetchResult.visualization.legend?.strategy) { case LEGEND_DISPLAY_STRATEGY_BY_DATA_ITEM: { if ( @@ -281,7 +295,9 @@ export const VisualizationPlugin = ({ legendSet: item.legendSet, })) - const unsupportedDimensions = (visualization.series || []) + const unsupportedDimensions = ( + fetchResult.visualization.series || [] + ) .filter((serie) => serie.type === VIS_TYPE_LINE) .map((item) => item.dimensionItem) diff --git a/src/modules/disabledOptions.js b/src/modules/disabledOptions.js new file mode 100644 index 0000000000..495c3b6603 --- /dev/null +++ b/src/modules/disabledOptions.js @@ -0,0 +1,34 @@ +import { VIS_TYPE_PIVOT_TABLE } from '@dhis2/analytics' +import i18n from '@dhis2/d2-i18n' + +export const getDisabledOptions = ({ visType, options }) => { + const disabledOptions = {} + + for (const [option, value] of Object.entries(options)) { + switch (option) { + case 'cumulativeValues': { + const helpText = i18n.t( + 'Not supported when using cumulative values' + ) + + // when checked, disabled totals and numberType options + if (visType === VIS_TYPE_PIVOT_TABLE && value) { + disabledOptions.colTotals = {} + disabledOptions.colSubTotals = {} + disabledOptions.rowTotals = {} + disabledOptions.rowSubTotals = {} + disabledOptions.numberType = { + helpText, + } + disabledOptions.legend = { + helpText, + } + } + + break + } + } + } + + return disabledOptions +} diff --git a/src/modules/options/config.js b/src/modules/options/config.js index 194e291094..b55b1c9d23 100644 --- a/src/modules/options/config.js +++ b/src/modules/options/config.js @@ -20,6 +20,7 @@ import singleValueConfig from './singleValueConfig.js' export const getOptionsByType = ({ type, + hasCumulativeValuesInPt, hasDimensionItemsInColumns, hasDimensionItemsInRows, hasDisabledSections, @@ -33,6 +34,7 @@ export const getOptionsByType = ({ const isVertical = isVerticalType(type) const defaultProps = { + hasCumulativeValuesInPt, hasDisabledSections, isStacked, isColumnBased, @@ -52,6 +54,7 @@ export const getOptionsByType = ({ return singleValueConfig() case VIS_TYPE_PIVOT_TABLE: return pivotTableConfig({ + hasCumulativeValuesInPt, hasDimensionItemsInColumns, hasDimensionItemsInRows, }) diff --git a/src/modules/options/pivotTableConfig.js b/src/modules/options/pivotTableConfig.js index 6e95b5ff87..3b36cfe3d8 100644 --- a/src/modules/options/pivotTableConfig.js +++ b/src/modules/options/pivotTableConfig.js @@ -7,6 +7,7 @@ import ColSubTotals from '../../components/VisualizationOptions/Options/ColSubTo import ColTotals from '../../components/VisualizationOptions/Options/ColTotals.js' import CompletedOnly from '../../components/VisualizationOptions/Options/CompletedOnly.js' import Cumulative from '../../components/VisualizationOptions/Options/Cumulative.js' +import CumulativeValues from '../../components/VisualizationOptions/Options/CumulativeValues.js' import DigitGroupSeparator from '../../components/VisualizationOptions/Options/DigitGroupSeparator.js' import DisplayDensity from '../../components/VisualizationOptions/Options/DisplayDensity.js' import FixColumnHeaders from '../../components/VisualizationOptions/Options/FixColumnHeaders.js' @@ -37,15 +38,21 @@ import getLimitValuesTab from './tabs/limitValues.js' import getSeriesTab from './tabs/series.js' import getStyleTab from './tabs/style.js' -export default ({ hasDimensionItemsInColumns, hasDimensionItemsInRows }) => [ +export default ({ + hasCumulativeValuesInPt, + hasDimensionItemsInColumns, + hasDimensionItemsInRows, +}) => [ getDataTab([ getDisplayTemplate({ content: React.Children.toArray([ + , , , ]), }), getTotalsTemplate({ + hasCumulativeValuesInPt, content: React.Children.toArray([ , , diff --git a/src/modules/options/sections/templates/totals.js b/src/modules/options/sections/templates/totals.js index 30484f3d2e..0d8d6fad07 100644 --- a/src/modules/options/sections/templates/totals.js +++ b/src/modules/options/sections/templates/totals.js @@ -1,7 +1,10 @@ import i18n from '@dhis2/d2-i18n' -export default ({ content }) => ({ +export default ({ hasCumulativeValuesInPt, content }) => ({ key: 'data-totals', label: i18n.t('Totals'), + helpText: hasCumulativeValuesInPt + ? i18n.t('Totals are not supported when using cumulative values') + : null, content, }) diff --git a/src/modules/ui.js b/src/modules/ui.js index 3af241b280..39b6cc6f10 100644 --- a/src/modules/ui.js +++ b/src/modules/ui.js @@ -14,6 +14,7 @@ import { VIS_TYPE_GAUGE, VIS_TYPE_SINGLE_VALUE, } from '@dhis2/analytics' +import { getDisabledOptions } from './disabledOptions.js' import { BASE_FIELD_YEARLY_SERIES } from './fields/baseFields.js' import { getInverseLayout } from './layout.js' import { getOptionsFromVisualization } from './options.js' @@ -25,24 +26,30 @@ export const ITEM_ATTRIBUTE_VERTICAL = 'VERTICAL' export const ITEM_ATTRIBUTE_HORIZONTAL = 'HORIZONTAL' // Transform from backend model to store.ui format -export const getUiFromVisualization = (vis, currentState = {}) => ({ - ...currentState, - type: vis.type || defaultVisType, - options: getOptionsFromVisualization(vis), - layout: layoutGetAxisIdDimensionIdsObject(vis), - itemsByDimension: layoutGetDimensionIdItemIdsObject(vis), - parentGraphMap: - vis.parentGraphMap || - getParentGraphMapFromVisualization(vis) || - currentState.parentGraphMap, - yearOverYearSeries: - isYearOverYear(vis.type) && vis[BASE_FIELD_YEARLY_SERIES] - ? vis[BASE_FIELD_YEARLY_SERIES] - : currentState.yearOverYearSeries, - yearOverYearCategory: isYearOverYear(vis.type) - ? vis.rows[0].items.map((item) => item.id) - : currentState.yearOverYearCategory, -}) +export const getUiFromVisualization = (vis, currentState = {}) => { + const visType = vis.type || defaultVisType + const options = getOptionsFromVisualization(vis) + + return { + ...currentState, + type: visType, + options, + disabledOptions: getDisabledOptions({ visType, options }), + layout: layoutGetAxisIdDimensionIdsObject(vis), + itemsByDimension: layoutGetDimensionIdItemIdsObject(vis), + parentGraphMap: + vis.parentGraphMap || + getParentGraphMapFromVisualization(vis) || + currentState.parentGraphMap, + yearOverYearSeries: + isYearOverYear(vis.type) && vis[BASE_FIELD_YEARLY_SERIES] + ? vis[BASE_FIELD_YEARLY_SERIES] + : currentState.yearOverYearSeries, + yearOverYearCategory: isYearOverYear(vis.type) + ? vis.rows[0].items.map((item) => item.id) + : currentState.yearOverYearCategory, + } +} // Transform from store.ui to default format const defaultUiAdapter = (ui) => ({ @@ -100,20 +107,35 @@ const scatterUiAdapter = (ui) => { } export const getAdaptedUiByType = (ui) => { + let adaptedUi + switch (ui.type) { case VIS_TYPE_YEAR_OVER_YEAR_LINE: case VIS_TYPE_YEAR_OVER_YEAR_COLUMN: { - return yearOverYearUiAdapter(ui) + adaptedUi = yearOverYearUiAdapter(ui) + break } case VIS_TYPE_PIVOT_TABLE: - return ui + adaptedUi = ui + break case VIS_TYPE_GAUGE: case VIS_TYPE_SINGLE_VALUE: - return singleValueUiAdapter(ui) + adaptedUi = singleValueUiAdapter(ui) + break case VIS_TYPE_SCATTER: - return scatterUiAdapter(ui) + adaptedUi = scatterUiAdapter(ui) + break default: - return defaultUiAdapter(ui) + adaptedUi = defaultUiAdapter(ui) + break + } + + return { + ...adaptedUi, + disabledOptions: getDisabledOptions({ + visType: adaptedUi.type, + options: adaptedUi.options, + }), } } diff --git a/src/reducers/ui.js b/src/reducers/ui.js index db5726fd84..d6c7113e53 100644 --- a/src/reducers/ui.js +++ b/src/reducers/ui.js @@ -59,6 +59,7 @@ import { export const SET_UI = 'SET_UI' export const SET_UI_FROM_VISUALIZATION = 'SET_UI_FROM_VISUALIZATION' export const SET_UI_TYPE = 'SET_UI_TYPE' +export const SET_UI_DISABLED_OPTIONS = 'SET_UI_DISABLED_OPTIONS' export const SET_UI_OPTIONS = 'SET_UI_OPTIONS' export const SET_UI_OPTION = 'SET_UI_OPTION' export const SET_UI_OPTION_FONT_STYLE = 'SET_UI_OPTION_FONT_STYLE' @@ -82,6 +83,7 @@ export const UPDATE_UI_SERIES_ITEM = 'UPDATE_UI_SERIES_ITEM' export const DEFAULT_UI = { type: defaultVisType, options: getOptionsForUi(), + disabledOptions: {}, layout: { columns: [DIMENSION_ID_DATA], rows: [DIMENSION_ID_PERIOD], @@ -150,6 +152,12 @@ export default (state = DEFAULT_UI, action) => { type: action.value, } } + case SET_UI_DISABLED_OPTIONS: { + return { + ...state, + disabledOptions: action.value, + } + } case SET_UI_OPTIONS: { return { ...state, @@ -665,6 +673,7 @@ export default (state = DEFAULT_UI, action) => { export const sGetUi = (state) => state.ui export const sGetUiType = (state) => sGetUi(state).type +export const sGetUiDisabledOptions = (state) => sGetUi(state).disabledOptions export const sGetUiOptions = (state) => sGetUi(state).options export const sGetUiLayout = (state) => sGetUi(state).layout export const sGetUiLayoutRows = (state) => sGetUi(state).layout.rows @@ -728,6 +737,9 @@ export const sGetAxisSetup = (state) => { : [] } +export const sGetUiDisabledOption = (state, option) => + sGetUiDisabledOptions(state)[option.name] + export const sGetUiOption = (state, option) => { const options = sGetUi(state).options const [axisType, axisIndex] = (option.axisId || '').split('_') diff --git a/yarn.lock b/yarn.lock index 943e3ca86f..f36c919257 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2028,10 +2028,10 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2/analytics@^26.1.6": - version "26.1.6" - resolved "https://registry.yarnpkg.com/@dhis2/analytics/-/analytics-26.1.6.tgz#d76d7aa40c4538fae6afbf7d8d5e7cfbad81efb5" - integrity sha512-XIoe2/mUjIlxzMrmA1iVeSH3zydZG7LC1LqZJQK8TBrneC7IrLhVFka+0zaGvTyA/2P35c5xFxSie7gKC8h8Og== +"@dhis2/analytics@^26.2.0": + version "26.2.0" + resolved "https://registry.yarnpkg.com/@dhis2/analytics/-/analytics-26.2.0.tgz#36a7f258ac96ddab90f4001e62257e2cc64f202e" + integrity sha512-YcJu6EHnor6pbHmwXKYumLRVy/9TxuLtBDv9JIzjt9/APZa8kbak6sT2/53pnWDnbUjzDwR8EV1UIz24vAX+ig== dependencies: "@dhis2/d2-ui-rich-text" "^7.4.1" "@dhis2/multi-calendar-dates" "1.0.0" From 4f3669dd329ce8c44aed0fc87b647860421124e2 Mon Sep 17 00:00:00 2001 From: "@dhis2-bot" Date: Thu, 14 Dec 2023 18:31:23 +0000 Subject: [PATCH 02/13] chore(release): cut 100.4.0 [skip release] # [100.4.0](https://github.com/dhis2/data-visualizer-app/compare/v100.3.1...v100.4.0) (2023-12-14) ### Bug Fixes * **translations:** sync translations from transifex (dev) ([65441f2](https://github.com/dhis2/data-visualizer-app/commit/65441f2b454a6e0f74567b8b7107cad63d594a04)) * **translations:** sync translations from transifex (dev) ([005be59](https://github.com/dhis2/data-visualizer-app/commit/005be599f7dd4382f0086861d74064aedc49ac29)) ### Features * cumulative values in PT (DHIS2-5497) ([#2746](https://github.com/dhis2/data-visualizer-app/issues/2746)) ([bff69ab](https://github.com/dhis2/data-visualizer-app/commit/bff69ab9cec7685adad823197de8508e9ae83636)), closes [#1946](https://github.com/dhis2/data-visualizer-app/issues/1946) --- CHANGELOG.md | 13 +++++++++++++ package.json | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cecf7e271a..e90033b62c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# [100.4.0](https://github.com/dhis2/data-visualizer-app/compare/v100.3.1...v100.4.0) (2023-12-14) + + +### Bug Fixes + +* **translations:** sync translations from transifex (dev) ([65441f2](https://github.com/dhis2/data-visualizer-app/commit/65441f2b454a6e0f74567b8b7107cad63d594a04)) +* **translations:** sync translations from transifex (dev) ([005be59](https://github.com/dhis2/data-visualizer-app/commit/005be599f7dd4382f0086861d74064aedc49ac29)) + + +### Features + +* cumulative values in PT (DHIS2-5497) ([#2746](https://github.com/dhis2/data-visualizer-app/issues/2746)) ([bff69ab](https://github.com/dhis2/data-visualizer-app/commit/bff69ab9cec7685adad823197de8508e9ae83636)), closes [#1946](https://github.com/dhis2/data-visualizer-app/issues/1946) + ## [100.3.1](https://github.com/dhis2/data-visualizer-app/compare/v100.3.0...v100.3.1) (2023-11-08) diff --git a/package.json b/package.json index 7901abb430..93959761c3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "data-visualizer-app", - "version": "100.3.1", + "version": "100.4.0", "description": "DHIS2 Data Visualizer", "license": "BSD-3-Clause", "private": true, From fb706cf763c1329d418b22eae59ae13c52a2d783 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 15 Dec 2023 13:00:39 +0100 Subject: [PATCH 03/13] chore: bump Cypress to version 13 (#2935) * chore: bump Cypress to version 13 * chore: yarn deduplicate * chore: bump node to version 18 * chore: video changes from https://github.com/dhis2/line-listing-app/pull/472 * test: don't expect subtitle to be visible by default * test: hover over list to prevent it from being stale * test: refactor data.cy.js to independent test cases --- .github/workflows/dhis2-preview-pr.yml | 2 +- cypress.config.js | 27 +- cypress/integration/dimensions/data.cy.js | 566 ++++++++++--------- cypress/integration/options/fontStyles.cy.js | 1 - package.json | 2 +- yarn.lock | 105 ++-- 6 files changed, 372 insertions(+), 331 deletions(-) diff --git a/.github/workflows/dhis2-preview-pr.yml b/.github/workflows/dhis2-preview-pr.yml index 27a52daf87..e5bf971cce 100644 --- a/.github/workflows/dhis2-preview-pr.yml +++ b/.github/workflows/dhis2-preview-pr.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 14.x + node-version: 18.x - uses: c-hive/gha-yarn-cache@v1 - run: yarn install --frozen-lockfile diff --git a/cypress.config.js b/cypress.config.js index 6ce2dc5d61..12810c2f08 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -1,3 +1,4 @@ +const fs = require('fs') const { chromeAllowXSiteCookies } = require('@dhis2/cypress-plugins') const { defineConfig } = require('cypress') const { @@ -8,6 +9,28 @@ async function setupNodeEvents(on, config) { chromeAllowXSiteCookies(on, config) excludeByVersionTags(on, config) + // Delete videos for passing tests + on('after:spec', (spec, results) => { + try { + if (results && results.video) { + // Do we have failures for any retry attempts? + const failures = results.tests.some((test) => + test.attempts.some((attempt) => attempt.state === 'failed') + ) + if (!failures) { + // delete the video if the spec passed and no tests retried + fs.unlinkSync(results.video) + } + } + } catch (error) { + if (error.code === 'ENOENT') { + console.log('Video already deleted') + } else { + throw error + } + } + }) + if (!config.env.dhis2InstanceVersion) { throw new Error( 'dhis2InstanceVersion is missing. Check the README for more information.' @@ -59,10 +82,6 @@ module.exports = defineConfig({ testIsolation: false, // Record video video: true, - /* Only compress and upload videos for failures. - * This will save execution time and reduce the risk - * out-of-memory issues on the CI machine */ - videoUploadOnPasses: false, // Enabled to reduce the risk of out-of-memory issues experimentalMemoryManagement: true, // Set to a low number to reduce the risk of out-of-memory issues diff --git a/cypress/integration/dimensions/data.cy.js b/cypress/integration/dimensions/data.cy.js index 90c245e4c9..98932758fa 100644 --- a/cypress/integration/dimensions/data.cy.js +++ b/cypress/integration/dimensions/data.cy.js @@ -48,159 +48,162 @@ const PAGE_SIZE = 50 const DATA_ITEMS_URL = '**/dataItems*' describe('Data dimension', () => { - describe('initial state', () => { - it('navigates to the start page', () => { - goToStartPage() - }) - it('opens the data dimension modal', () => { - cy.intercept('GET', DATA_ITEMS_URL).as('request') - openDimension(DIMENSION_ID_DATA) - cy.wait('@request').then(({ request, response }) => { - expect(request.url).to.contain('page=1') - expect(response.statusCode).to.eq(200) - expect(response.body.dataItems.length).to.eq(PAGE_SIZE) - }) - expectDataDimensionModalToBeVisible() - }) - it('modal has a title', () => { - expectDimensionModalToContain('Data') - }) - it('no items are selected', () => { - expectNoDataItemsToBeSelected() - }) - it("data type is 'All'", () => { - expectDataTypeToBe('All') - }) - it('group select is not visible', () => { - expectGroupSelectToNotBeVisible() + it('has correct initial state', () => { + // navigates to the start page + goToStartPage() + + // opens the data dimension modal + cy.intercept('GET', DATA_ITEMS_URL).as('request') + openDimension(DIMENSION_ID_DATA) + cy.wait('@request').then(({ request, response }) => { + expect(request.url).to.contain('page=1') + expect(response.statusCode).to.eq(200) + expect(response.body.dataItems.length).to.eq(PAGE_SIZE) }) + expectDataDimensionModalToBeVisible() + + // modal has a title + expectDimensionModalToContain('Data') + + // no items are selected + expectNoDataItemsToBeSelected() + + // data type is 'All' + expectDataTypeToBe('All') + + // group select is not visible + expectGroupSelectToNotBeVisible() + + // an item can be selected by double click const firstPageItemName = TEST_INDICATORS[0].name - it('an item can be selected by double click', () => { - selectItemByDoubleClick(firstPageItemName) - expectItemToBeSelected(firstPageItemName) - }) - it('an item can be unselected by double click', () => { - unselectItemByDoubleClick(firstPageItemName) - expectNoDataItemsToBeSelected() - }) - it('an item can be selected by button', () => { - selectItemByButton(firstPageItemName) - expectItemToBeSelected(firstPageItemName) - }) - it('an item can be unselected by button', () => { - unselectItemByButton(firstPageItemName) - expectNoDataItemsToBeSelected() - }) + selectItemByDoubleClick(firstPageItemName) + expectItemToBeSelected(firstPageItemName) + + // an item can be unselected by double click + unselectItemByDoubleClick(firstPageItemName) + expectNoDataItemsToBeSelected() + + // an item can be selected by button + selectItemByButton(firstPageItemName) + expectItemToBeSelected(firstPageItemName) + + // an item can be unselected by button + unselectItemByButton(firstPageItemName) + expectNoDataItemsToBeSelected() }) - describe('selecting all and fetching more', () => { + it('can select all and fetch more', () => { + goToStartPage() + openDimension(DIMENSION_ID_DATA) + expectDataDimensionModalToBeVisible() + expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE) + const secondPageItemName = 'BCG doses' - it('all items can be selected', () => { - cy.intercept('GET', DATA_ITEMS_URL).as('request') - selectAllItemsByButton() - expectSelectedItemsAmountToBeLeast(PAGE_SIZE) - cy.wait('@request').then(({ request, response }) => { - expect(request.url).to.contain('page=2') - expect(response.statusCode).to.eq(200) - expect(response.body.dataItems.length).to.eq(PAGE_SIZE) - }) - expectSourceToNotBeLoading() + // all items can be selected + cy.intercept('GET', DATA_ITEMS_URL).as('request') + selectAllItemsByButton() + expectSelectedItemsAmountToBeLeast(PAGE_SIZE) + cy.wait('@request').then(({ request, response }) => { + expect(request.url).to.contain('page=2') + expect(response.statusCode).to.eq(200) + expect(response.body.dataItems.length).to.eq(PAGE_SIZE) }) - it('more items are fetched', () => { - expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE) - expectItemToBeSelectable(secondPageItemName) - }) - it('all items can be unselected', () => { - unselectAllItemsByButton() - expectNoDataItemsToBeSelected() - }) - it('more items are fetched when scrolling down', () => { - cy.intercept('GET', DATA_ITEMS_URL).as('request') - scrollSourceToBottom() - cy.wait('@request').then(({ request, response }) => { - expect(request.url).to.contain('page=3') - expect(response.statusCode).to.eq(200) - expect(response.body.dataItems.length).to.eq(PAGE_SIZE) - }) - expectSourceToNotBeLoading() - expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE * 3) + expectSourceToNotBeLoading() + + // more items are fetched + expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE) + expectItemToBeSelectable(secondPageItemName) + + // all items can be unselected + unselectAllItemsByButton() + expectNoDataItemsToBeSelected() + + // more items are fetched when scrolling down + cy.intercept('GET', DATA_ITEMS_URL).as('request') + scrollSourceToBottom() + cy.wait('@request').then(({ request, response }) => { + expect(request.url).to.contain('page=3') + expect(response.statusCode).to.eq(200) + expect(response.body.dataItems.length).to.eq(PAGE_SIZE) }) + expectSourceToNotBeLoading() + expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE * 3) }) - describe('global search', () => { + it('can use global search', () => { + goToStartPage() + openDimension(DIMENSION_ID_DATA) + expectDataDimensionModalToBeVisible() + expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE) + const testSearchTerm = 'Dispenser' // Use a data element for the third step to work - it('receives a search term', () => { - cy.intercept('GET', DATA_ITEMS_URL).as('request') - inputSearchTerm(testSearchTerm) - cy.wait('@request').then(({ request, response }) => { - expect(request.url).to.contain('page=1') - expect(request.url).to.contain(testSearchTerm) - expect(response.statusCode).to.eq(200) - expect(response.body.dataItems.length).to.eq(1) - }) - expectSourceToNotBeLoading() + // receives a search term + cy.intercept('GET', DATA_ITEMS_URL).as('request') + inputSearchTerm(testSearchTerm) + cy.wait('@request').then(({ request, response }) => { + expect(request.url).to.contain('page=1') + expect(request.url).to.contain(testSearchTerm) + expect(response.statusCode).to.eq(200) + expect(response.body.dataItems.length).to.eq(1) }) + expectSourceToNotBeLoading() + // TODO: Test that the search is only called once, i.e. debounce works - it('search result is displayed', () => { - expectSelectableDataItemsAmountToBe(1) - expectItemToBeSelectable(testSearchTerm) + // search result is displayed + expectSelectableDataItemsAmountToBe(1) + expectItemToBeSelectable(testSearchTerm) + + // search result is maintained when switching data type + cy.intercept('GET', '**/dataElements*').as('dataElements') + switchDataTypeTo('Data elements') + cy.wait('@dataElements').then(({ request, response }) => { + expect(request.url).to.contain('page=1') + expect(response.statusCode).to.eq(200) + expect(response.body.dataElements.length).to.be.eq(1) }) - it('search result is maintained when switching data type', () => { - cy.intercept('GET', '**/dataElements*').as('dataElements') - switchDataTypeTo('Data elements') - cy.wait('@dataElements').then(({ request, response }) => { - expect(request.url).to.contain('page=1') - expect(response.statusCode).to.eq(200) - expect(response.body.dataElements.length).to.be.eq(1) - }) - expectSourceToNotBeLoading() - expectSelectableDataItemsAmountToBe(1) - expectItemToBeSelectable(testSearchTerm) - clearSearchTerm() - cy.wait('@dataElements').then(({ request, response }) => { - expect(request.url).to.contain('page=1') - expect(response.statusCode).to.eq(200) - expect(response.body.dataElements.length).to.be.eq(PAGE_SIZE) - }) - expectSourceToNotBeLoading() - cy.intercept('GET', DATA_ITEMS_URL).as('dataItems') - switchDataTypeToAll() - cy.wait('@dataItems').then(({ request, response }) => { - expect(request.url).to.contain('page=1') - expect(response.statusCode).to.eq(200) - expect(response.body.dataItems.length).to.eq(PAGE_SIZE) - }) - expectSourceToNotBeLoading() - expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE) + expectSourceToNotBeLoading() + expectSelectableDataItemsAmountToBe(1) + expectItemToBeSelectable(testSearchTerm) + clearSearchTerm() + cy.wait('@dataElements').then(({ request, response }) => { + expect(request.url).to.contain('page=1') + expect(response.statusCode).to.eq(200) + expect(response.body.dataElements.length).to.be.eq(PAGE_SIZE) }) - it('search displays a correct error message', () => { - const testSearchTermWithNoMatch = 'nomatch' - cy.intercept('GET', DATA_ITEMS_URL).as('request') - inputSearchTerm(testSearchTermWithNoMatch) - cy.wait('@request').then(({ request, response }) => { - expect(request.url).to.contain('page=1') - expect(request.url).to.contain(testSearchTermWithNoMatch) - expect(response.statusCode).to.eq(200) - expect(response.body.dataItems.length).to.eq(0) - }) - expectSourceToNotBeLoading() - expectEmptySourceMessageToBe( - `Nothing found for "${testSearchTermWithNoMatch}"` - ) + expectSourceToNotBeLoading() + cy.intercept('GET', DATA_ITEMS_URL).as('dataItems') + switchDataTypeToAll() + cy.wait('@dataItems').then(({ request, response }) => { + expect(request.url).to.contain('page=1') + expect(response.statusCode).to.eq(200) + expect(response.body.dataItems.length).to.eq(PAGE_SIZE) }) - it('search result can be cleared', () => { - cy.intercept('GET', DATA_ITEMS_URL).as('request') - clearSearchTerm() - cy.wait('@request').then(({ request, response }) => { - expect(request.url).to.contain('page=1') - expect(response.statusCode).to.eq(200) - expect(response.body.dataItems.length).to.be.eq(PAGE_SIZE) - }) - expectSourceToNotBeLoading() - expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE) + expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE) + expectSourceToNotBeLoading() + + // search displays a correct error message + const testSearchTermWithNoMatch = 'nomatch' + cy.intercept('GET', DATA_ITEMS_URL).as('requestNoMatch') + inputSearchTerm(testSearchTermWithNoMatch) + cy.wait('@requestNoMatch').then(({ request, response }) => { + expect(request.url).to.contain('page=1') + expect(request.url).to.contain(testSearchTermWithNoMatch) + expect(response.statusCode).to.eq(200) + expect(response.body.dataItems.length).to.eq(0) }) - it('modal is closed', () => { - clickDimensionModalHideButton() - expectDimensionModalToNotBeVisible() + expectSourceToNotBeLoading() + expectEmptySourceMessageToBe( + `Nothing found for "${testSearchTermWithNoMatch}"` + ) + + // search result can be cleared + cy.intercept('GET', DATA_ITEMS_URL).as('requestClear') + clearSearchTerm() + cy.wait('@requestClear').then(({ request, response }) => { + expect(request.url).to.contain('page=1') + expect(response.statusCode).to.eq(200) + expect(response.body.dataItems.length).to.be.eq(PAGE_SIZE) }) + expectSourceToNotBeLoading() + expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE) }) const testDataTypes = [ { @@ -272,89 +275,92 @@ describe('Data dimension', () => { }, ] testDataTypes.forEach((testDataType) => { - describe(`${testDataType.name}`, () => { - it('opens the data dimension modal', () => { - openDimension(DIMENSION_ID_DATA) - expectDataDimensionModalToBeVisible() + it(`displays ${testDataType.name} correctly`, () => { + // opens the data dimension modal + goToStartPage() + openDimension(DIMENSION_ID_DATA) + expectDataDimensionModalToBeVisible() + + // switches to type + cy.log(`type: ${testDataType.name}`) + cy.intercept('GET', testDataType.endpoint.requestUrl).as('request') + switchDataTypeTo(testDataType.name) + cy.wait('@request').then(({ response }) => { + expect(response.statusCode).to.eq(200) + expect( + response.body[testDataType.endpoint.responseBody].length + ).to.be.least(1) }) - it(`switches to ${testDataType.name}`, () => { + expectSourceToNotBeLoading() + expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE) + + // group select is visible + expectGroupSelectToBeVisible() + expectGroupSelectToBe(testDataType.defaultGroup.name) + + if (testDataType.endpoint.hasMultiplePages) { + // more items are fetched when scrolling down cy.intercept('GET', testDataType.endpoint.requestUrl).as( 'request' ) - switchDataTypeTo(testDataType.name) - cy.wait('@request').then(({ response }) => { + scrollSourceToBottom() + cy.wait('@request').then(({ request, response }) => { + expect(request.url).to.contain('page=2') expect(response.statusCode).to.eq(200) expect( response.body[testDataType.endpoint.responseBody].length ).to.be.least(1) }) expectSourceToNotBeLoading() - expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE) - }) - it('group select is visible', () => { - expectGroupSelectToBeVisible() - expectGroupSelectToBe(testDataType.defaultGroup.name) - }) - if (testDataType.endpoint.hasMultiplePages) { - it('more items are fetched when scrolling down', () => { - cy.intercept('GET', testDataType.endpoint.requestUrl).as( - 'request' - ) - scrollSourceToBottom() - cy.wait('@request').then(({ request, response }) => { - expect(request.url).to.contain('page=2') - expect(response.statusCode).to.eq(200) - expect( - response.body[testDataType.endpoint.responseBody] - .length - ).to.be.least(1) - }) - expectSourceToNotBeLoading() - expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE + 1) - }) + expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE + 1) } - it('an item can be selected', () => { - expectDataItemsToBeInSource([testDataType.testItem.name]) - selectItemByDoubleClick(testDataType.testItem.name) - expectItemToBeSelected(testDataType.testItem.name) - }) - it(`group can be changed to "${testDataType.testGroup.name}"`, () => { - cy.intercept('GET', testDataType.endpoint.requestUrl).as( - 'request' - ) - switchGroupTo(testDataType.testGroup.name) - cy.wait('@request').then(({ request, response }) => { - expect(request.url).to.contain('page=1') - expect(response.statusCode).to.eq(200) - }) - expectSourceToNotBeLoading() - expectGroupSelectToBe(testDataType.testGroup.name) - expectSelectableDataItemsAmountToBe( - testDataType.testGroup.itemAmount - ) - expectItemToBeSelected(testDataType.testItem.name) - }) - it('the first item can be selected', () => { - selectFirstDataItem() - expectSelectedItemsAmountToBe(2) - expectSelectableDataItemsAmountToBe( - testDataType.testGroup.itemAmount - 1 - ) + // an item can be selected + expectDataItemsToBeInSource([testDataType.testItem.name]) + selectItemByDoubleClick(testDataType.testItem.name) + expectItemToBeSelected(testDataType.testItem.name) + + // group can be changed + cy.log(`group: ${testDataType.testGroup.name}`) + cy.intercept('GET', testDataType.endpoint.requestUrl).as( + 'requestWithGroup' + ) + switchGroupTo(testDataType.testGroup.name) + cy.wait('@requestWithGroup').then(({ request, response }) => { + expect(request.url).to.contain('page=1') + expect(response.statusCode).to.eq(200) }) + expectSourceToNotBeLoading() + expectGroupSelectToBe(testDataType.testGroup.name) + cy.getBySel('data-dimension-transfer-sourceoptions').trigger( + 'mouseover' + ) // seems to trigger Cypress to refretch the list and update the result so it's no longer stale + expectSelectableDataItemsAmountToBe( + testDataType.testGroup.itemAmount + ) + expectItemToBeSelected(testDataType.testItem.name) + + // the first item can be selected + selectFirstDataItem() + expectSelectedItemsAmountToBe(2) + expectSelectableDataItemsAmountToBe( + testDataType.testGroup.itemAmount - 1 + ) + if (['Data elements', 'Data sets'].includes(testDataType.name)) { - it('sub group select is visible', () => { - expectSubGroupSelectToBeVisible() - expectSubGroupSelectToBe(testDataType.defaultSubGroup.name) - }) + // sub group select is visible + expectSubGroupSelectToBeVisible() + expectSubGroupSelectToBe(testDataType.defaultSubGroup.name) - it(`sub group can be changed to "${testDataType.testSubGroup.name}"`, () => { - cy.intercept( - 'GET', - testDataType.testSubGroup.endpoint?.requestUrl || - testDataType.endpoint.requestUrl - ).as('request') - switchSubGroupTo(testDataType.testSubGroup.name) - cy.wait('@request').then(({ request, response }) => { + // sub group can be changed + cy.log(`sub group: ${testDataType.testSubGroup.name}`) + cy.intercept( + 'GET', + testDataType.testSubGroup.endpoint?.requestUrl || + testDataType.endpoint.requestUrl + ).as('requestWithSubGroup') + switchSubGroupTo(testDataType.testSubGroup.name) + cy.wait('@requestWithSubGroup').then( + ({ request, response }) => { expect(request.url).to.contain('page=1') expect(response.statusCode).to.eq(200) expect( @@ -364,92 +370,94 @@ describe('Data dimension', () => { testDataType.endpoint.responseBody ].length ).to.be.eq(testDataType.testSubGroup.itemAmount) - }) - expectSourceToNotBeLoading() - expectSubGroupSelectToBe(testDataType.testSubGroup.name) - expectSelectableDataItemsAmountToBe( - testDataType.testSubGroup.itemAmount - ) - expectItemToBeSelected(testDataType.testItem.name) - }) - it(`sub group can be changed back to "${testDataType.defaultSubGroup.name}"`, () => { - cy.intercept('GET', testDataType.endpoint.requestUrl).as( - 'request' - ) - switchSubGroupTo(testDataType.defaultSubGroup.name) - cy.wait('@request').then(({ request, response }) => { - expect(request.url).to.contain('page=1') - expect(response.statusCode).to.eq(200) - }) - expectSourceToNotBeLoading() - expectSubGroupSelectToBe(testDataType.defaultSubGroup.name) - expectSelectableDataItemsAmountToBe( - testDataType.testGroup.itemAmount - 1 - ) - expectItemToBeSelected(testDataType.testItem.name) - }) - } - it('search displays a correct error message', () => { - const testSearchTermWithNoMatch = 'nomatch' + } + ) + expectSourceToNotBeLoading() + expectSubGroupSelectToBe(testDataType.testSubGroup.name) + expectSelectableDataItemsAmountToBe( + testDataType.testSubGroup.itemAmount + ) + expectItemToBeSelected(testDataType.testItem.name) + + // sub group can be changed back to default + cy.log( + `default sub group: ${testDataType.defaultSubGroup.name}` + ) cy.intercept('GET', testDataType.endpoint.requestUrl).as( 'request' ) - inputSearchTerm(testSearchTermWithNoMatch) + switchSubGroupTo(testDataType.defaultSubGroup.name) cy.wait('@request').then(({ request, response }) => { expect(request.url).to.contain('page=1') - expect(request.url).to.contain(testSearchTermWithNoMatch) expect(response.statusCode).to.eq(200) - expect( - response.body[testDataType.endpoint.responseBody].length - ).to.eq(0) }) expectSourceToNotBeLoading() - expectEmptySourceMessageToBe( - `No ${testDataType.name.toLowerCase()} found for "${testSearchTermWithNoMatch}"` - ) - }) - it('selection and filter can be reset', () => { - unselectAllItemsByButton() - expectNoDataItemsToBeSelected() - cy.intercept('GET', testDataType.endpoint.requestUrl).as( - testDataType.endpoint.requestUrl - ) - clearSearchTerm() - cy.wait(`@${testDataType.endpoint.requestUrl}`).then( - ({ request, response }) => { - expect(request.url).to.contain('page=1') - expect(response.statusCode).to.eq(200) - } - ) - expectSourceToNotBeLoading() + expectSubGroupSelectToBe(testDataType.defaultSubGroup.name) expectSelectableDataItemsAmountToBe( - testDataType.testGroup.itemAmount - ) - switchGroupToAll() - cy.wait(`@${testDataType.endpoint.requestUrl}`).then( - ({ request, response }) => { - expect(request.url).to.contain('page=1') - expect(response.statusCode).to.eq(200) - } + testDataType.testGroup.itemAmount - 1 ) - expectSourceToNotBeLoading() - expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE) - if (testDataType.endpoint.requestUrl !== DATA_ITEMS_URL) { - cy.intercept('GET', DATA_ITEMS_URL).as('**/dataItems*') + expectItemToBeSelected(testDataType.testItem.name) + } + // search displays a correct error message + const testSearchTermWithNoMatch = 'nomatch' + cy.intercept('GET', testDataType.endpoint.requestUrl).as( + 'requestNoMatch' + ) + inputSearchTerm(testSearchTermWithNoMatch) + cy.wait('@requestNoMatch').then(({ request, response }) => { + expect(request.url).to.contain('page=1') + expect(request.url).to.contain(testSearchTermWithNoMatch) + expect(response.statusCode).to.eq(200) + expect( + response.body[testDataType.endpoint.responseBody].length + ).to.eq(0) + }) + expectSourceToNotBeLoading() + expectEmptySourceMessageToBe( + `No ${testDataType.name.toLowerCase()} found for "${testSearchTermWithNoMatch}"` + ) + + // selection and filter can be reset + unselectAllItemsByButton() + expectNoDataItemsToBeSelected() + cy.intercept('GET', testDataType.endpoint.requestUrl).as( + testDataType.endpoint.requestUrl + ) + clearSearchTerm() + cy.wait(`@${testDataType.endpoint.requestUrl}`).then( + ({ request, response }) => { + expect(request.url).to.contain('page=1') + expect(response.statusCode).to.eq(200) } - switchDataTypeToAll() - cy.wait('@**/dataItems*').then(({ request, response }) => { + ) + expectSourceToNotBeLoading() + expectSelectableDataItemsAmountToBe( + testDataType.testGroup.itemAmount + ) + switchGroupToAll() + cy.wait(`@${testDataType.endpoint.requestUrl}`).then( + ({ request, response }) => { expect(request.url).to.contain('page=1') expect(response.statusCode).to.eq(200) - expect(response.body.dataItems.length).to.eq(PAGE_SIZE) - }) - expectSourceToNotBeLoading() - expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE) - }) - it('modal is closed', () => { - clickDimensionModalHideButton() - expectDimensionModalToNotBeVisible() + } + ) + expectSourceToNotBeLoading() + expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE) + if (testDataType.endpoint.requestUrl !== DATA_ITEMS_URL) { + cy.intercept('GET', DATA_ITEMS_URL).as('**/dataItems*') + } + switchDataTypeToAll() + cy.wait('@**/dataItems*').then(({ request, response }) => { + expect(request.url).to.contain('page=1') + expect(response.statusCode).to.eq(200) + expect(response.body.dataItems.length).to.eq(PAGE_SIZE) }) + expectSourceToNotBeLoading() + expectSelectableDataItemsAmountToBeLeast(PAGE_SIZE) + + // modal is closed + clickDimensionModalHideButton() + expectDimensionModalToNotBeVisible() }) }) }) diff --git a/cypress/integration/options/fontStyles.cy.js b/cypress/integration/options/fontStyles.cy.js index 2cbee29b55..366f519265 100644 --- a/cypress/integration/options/fontStyles.cy.js +++ b/cypress/integration/options/fontStyles.cy.js @@ -168,7 +168,6 @@ describe('Options - Font styles', () => { const TEST_SUBTITLE_TEXT = 'S' it('has default value', () => { - expectChartSubtitleToBeVisible() expectWindowConfigSubtitleToBeValue(CONFIG_DEFAULT_SUBTITLE) }) it('opens Options -> Style', () => { diff --git a/package.json b/package.json index 93959761c3..4226931bb1 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@dhis2/cypress-plugins": "^10.0.2", "@reportportal/agent-js-cypress": "git+https://github.com/dhis2/agent-js-cypress.git#develop", "@reportportal/agent-js-jest": "^5.0.6", - "cypress": "^12.16.0", + "cypress": "^13.6.1", "cypress-tags": "^1.1.2", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.7", diff --git a/yarn.lock b/yarn.lock index f36c919257..ac4840ccc5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1416,10 +1416,10 @@ through2 "^2.0.0" watchify "^4.0.0" -"@cypress/request@^2.88.10": - version "2.88.11" - resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.11.tgz#5a4c7399bc2d7e7ed56e92ce5acb620c8b187047" - integrity sha512-M83/wfQ1EkspjkE2lNWNV5ui2Cv7UCv1swW1DqljahbzLVWltcsexQh8jYtuS/vzFXP+HySntGM83ZXA9fn17w== +"@cypress/request@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.1.tgz#72d7d5425236a2413bd3d8bb66d02d9dc3168960" + integrity sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -1434,9 +1434,9 @@ json-stringify-safe "~5.0.1" mime-types "~2.1.19" performance-now "^2.1.0" - qs "~6.10.3" + qs "6.10.4" safe-buffer "^5.1.2" - tough-cookie "~2.5.0" + tough-cookie "^4.1.3" tunnel-agent "^0.6.0" uuid "^8.3.2" @@ -2985,19 +2985,7 @@ "@reportportal/client-javascript" "^5.0.12" strip-ansi "^6.0.1" -"@reportportal/client-javascript@^5.0.12": - version "5.0.12" - resolved "https://registry.yarnpkg.com/@reportportal/client-javascript/-/client-javascript-5.0.12.tgz#b6d9254014545ca56599c05105854ea814561f1e" - integrity sha512-ECLvuDLV7KyKs0wG9Sis3ZqHOq9VMg3fywm1VDegd5HGDKC1hoXxFKfz6ngPY8FZ5O1nt1UJvgEs47shtPHQCg== - dependencies: - axios "^0.27.2" - axios-retry "^3.4.0" - glob "^7.2.3" - ini "^2.0.0" - uniqid "^5.4.0" - uuid "^9.0.0" - -"@reportportal/client-javascript@^5.0.14": +"@reportportal/client-javascript@^5.0.12", "@reportportal/client-javascript@^5.0.14": version "5.0.14" resolved "https://registry.yarnpkg.com/@reportportal/client-javascript/-/client-javascript-5.0.14.tgz#48a40f2f33129d74bb7e451aaf25d44742e26fcc" integrity sha512-4ge9ddOB1rFlzqI6j43qCw0cyjQOloiPChA1EFyF3dcV2BXHzGbh8S3SNhwxibvlQtV6piU8e0W9CLN4UWXvSA== @@ -3432,10 +3420,12 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== -"@types/node@*", "@types/node@^14.14.31": - version "14.18.32" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.32.tgz#8074f7106731f1a12ba993fe8bad86ee73905014" - integrity sha512-Y6S38pFr04yb13qqHf8uk1nHE3lXgQ30WZbv1mLliV9pt0NjvqdWttLcrOYLnXbOafknVYRHZGoMSpR9UwfYow== +"@types/node@*", "@types/node@^18.17.5": + version "18.19.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.3.tgz#e4723c4cb385641d61b983f6fe0b716abd5f8fc0" + integrity sha512-k5fggr14DwAytoA/t8rPrIz++lXK7/DqckthCmoZOKNsEbJkId4Z//BqgApXBUGrGddrigYa1oqheo/7YmW4rg== + dependencies: + undici-types "~5.26.4" "@types/parse-json@^4.0.0": version "4.0.0" @@ -6220,14 +6210,14 @@ cypress-tags@^1.1.2: boolean-parser "0.0.2" through "^2.3.8" -cypress@^12.16.0: - version "12.16.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.16.0.tgz#d0dcd0725a96497f4c60cf54742242259847924c" - integrity sha512-mwv1YNe48hm0LVaPgofEhGCtLwNIQEjmj2dJXnAkY1b4n/NE9OtgPph4TyS+tOtYp5CKtRmDvBzWseUXQTjbTg== +cypress@^13.6.1: + version "13.6.1" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.6.1.tgz#c5f714f08551666ed3ac1fa95718eabb23a416df" + integrity sha512-k1Wl5PQcA/4UoTffYKKaxA0FJKwg8yenYNYRzLt11CUR0Kln+h7Udne6mdU1cUIdXBDTVZWtmiUjzqGs7/pEpw== dependencies: - "@cypress/request" "^2.88.10" + "@cypress/request" "^3.0.0" "@cypress/xvfb" "^1.2.4" - "@types/node" "^14.14.31" + "@types/node" "^18.17.5" "@types/sinonjs__fake-timers" "8.1.1" "@types/sizzle" "^2.3.2" arch "^2.2.0" @@ -6260,9 +6250,10 @@ cypress@^12.16.0: minimist "^1.2.8" ospath "^1.2.2" pretty-bytes "^5.6.0" + process "^0.11.10" proxy-from-env "1.0.0" request-progress "^3.0.0" - semver "^7.3.2" + semver "^7.5.3" supports-color "^8.1.1" tmp "~0.2.1" untildify "^4.0.0" @@ -12866,7 +12857,7 @@ process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -process@~0.11.0: +process@^0.11.10, process@~0.11.0: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== @@ -13003,10 +12994,10 @@ qs@6.10.3: dependencies: side-channel "^1.0.4" -qs@~6.10.3: - version "6.10.5" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.5.tgz#974715920a80ff6a262264acd2c7e6c2a53282b4" - integrity sha512-O5RlPh0VFtR78y79rgcgKK4wbAI0C5zGVLztOIdpWX6ep368q5Hv6XRxDvXuZ9q3C6v+e3n8UfZZJw7IIG27eQ== +qs@6.10.4: + version "6.10.4" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.4.tgz#6a3003755add91c0ec9eacdc5f878b034e73f9e7" + integrity sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g== dependencies: side-channel "^1.0.4" @@ -13035,6 +13026,11 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + quick-lru@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" @@ -14070,10 +14066,10 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.2.1, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== +semver@^7.2.1, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" @@ -15263,14 +15259,15 @@ tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" -tough-cookie@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" - integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== +tough-cookie@^4.0.0, tough-cookie@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== dependencies: psl "^1.1.33" punycode "^2.1.1" - universalify "^0.1.2" + universalify "^0.2.0" + url-parse "^1.5.3" tr46@^1.0.1: version "1.0.1" @@ -15469,6 +15466,11 @@ undeclared-identifiers@^1.1.2: simple-concat "^1.0.0" xtend "^4.0.1" +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" @@ -15529,11 +15531,16 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" -universalify@^0.1.0, universalify@^0.1.2: +universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + universalify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" @@ -15620,6 +15627,14 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + url@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" From cc3fcd1cc61c6b08f84cb8954cbde3955499aa6a Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 4 Jan 2024 15:09:17 +0100 Subject: [PATCH 04/13] fix: add custom errors for analytics requests (#2874) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jan Henrik Øverland --- i18n/en.pot | 10 +++++++++ src/components/Visualization/Visualization.js | 8 +++++++ src/modules/error.js | 22 +++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/i18n/en.pot b/i18n/en.pot index ec2d6dac34..10985d66d0 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -845,6 +845,16 @@ msgstr "" msgid "Something went wrong" msgstr "Something went wrong" +msgid "" +"There's a problem with the generated analytics. Contact a system " +"administrator." +msgstr "" +"There's a problem with the generated analytics. Contact a system " +"administrator." + +msgid "There's a syntax problem with the analytics request." +msgstr "There's a syntax problem with the analytics request." + msgid "or" msgstr "or" diff --git a/src/components/Visualization/Visualization.js b/src/components/Visualization/Visualization.js index 0b67d8a528..c619c23760 100644 --- a/src/components/Visualization/Visualization.js +++ b/src/components/Visualization/Visualization.js @@ -19,6 +19,8 @@ import { NoOrgUnitResponseError, NoDataError, ValueTypeError, + AnalyticsGenerationError, + AnalyticsRequestError, } from '../../modules/error.js' import { removeLastPathSegment } from '../../modules/orgUnit.js' import { sGetCurrent } from '../../reducers/current.js' @@ -74,6 +76,12 @@ export class UnconnectedVisualization extends Component { } } break + case 'E7144': + error = new AnalyticsGenerationError() + break + case 'E7145': + error = new AnalyticsRequestError() + break default: error = response } diff --git a/src/modules/error.js b/src/modules/error.js index 7f61a71b5a..07160d1aa9 100644 --- a/src/modules/error.js +++ b/src/modules/error.js @@ -335,6 +335,28 @@ export class ValueTypeError extends VisualizationError { } } +export class AnalyticsGenerationError extends VisualizationError { + constructor() { + super( + GenericError, + i18n.t('Something went wrong'), + i18n.t( + "There's a problem with the generated analytics. Contact a system administrator." + ) + ) + } +} + +export class AnalyticsRequestError extends VisualizationError { + constructor() { + super( + GenericError, + i18n.t('Something went wrong'), + i18n.t("There's a syntax problem with the analytics request.") + ) + } +} + export const genericErrorTitle = i18n.t('Something went wrong') const getAvailableAxesDescription = (visType) => { From 2966187e5fa05208836b6d7bb42529e14ede0440 Mon Sep 17 00:00:00 2001 From: "@dhis2-bot" Date: Sun, 14 Jan 2024 02:40:44 +0100 Subject: [PATCH 05/13] fix(translations): sync translations from transifex (dev) Automatically merged. --- i18n/fr.po | 27 ++++++++++++++++++++++----- i18n/nb.po | 25 +++++++++++++++++++++---- i18n/zh.po | 11 ++++++++++- 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/i18n/fr.po b/i18n/fr.po index a75c7b3294..0a617a4f81 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -4,19 +4,19 @@ # Matthieu Pinard , 2019 # Karoline Tufte Lien , 2020 # Djibril Dione , 2020 -# Philip Larsen Donnelly, 2020 # Gabriela Rodriguez , 2020 # Viktor Varland , 2021 # Yayra Gomado , 2022 # Yao Selom SAKA (HISP WCA) , 2022 # Edem Kossi , 2023 +# Philip Larsen Donnelly, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-06T13:41:14.540Z\n" +"POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Edem Kossi , 2023\n" +"Last-Translator: Philip Larsen Donnelly, 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" @@ -56,7 +56,7 @@ msgid "Yes, leave" msgstr "Oui, quitter" msgid "Add to {{axisName}}" -msgstr "Ajouter à {{nomdel'axe}} " +msgstr "Ajouter à {{axisName}}" msgid "" "'{{visualizationType}}' is intended to show a single data item. Only the " @@ -400,6 +400,9 @@ msgstr "Inclure cumulatif" msgid "Cumulative values" msgstr "Valeurs cumulées" +msgid "Accumulate cell values along rows" +msgstr "" + msgid "Show data item icon" msgstr "" @@ -725,7 +728,7 @@ msgid "Change org unit" msgstr "Changer d'unité d'organisation" msgid "{{level}} level in {{orgunit}}" -msgstr "{niveau}} niveau dans {{unité d'org.}}" +msgstr "{{level}} niveau dans {{orgunit}}" msgid "Open as Map" msgstr "Ouvrir comme carte" @@ -736,6 +739,9 @@ msgstr "" "Tracez graphiquement les données sur une carte du monde. Les éléments de " "données utilisent des couches de carte distinctes." +msgid "Not supported when using cumulative values" +msgstr "" + msgid "No data available" msgstr "Pas de données disponibles" @@ -877,6 +883,14 @@ msgstr "" msgid "Something went wrong" msgstr "Quelque chose a mal tourné" +msgid "" +"There's a problem with the generated analytics. Contact a system " +"administrator." +msgstr "" + +msgid "There's a syntax problem with the analytics request." +msgstr "" + msgid "or" msgstr "ou" @@ -1048,6 +1062,9 @@ msgstr "Lignes" msgid "Totals" msgstr "Totaux" +msgid "Totals are not supported when using cumulative values" +msgstr "" + msgid "Vertical (y) axis {{axisId}}" msgstr "Axe vertical (y) {{axisId}}" diff --git a/i18n/nb.po b/i18n/nb.po index a822c90e46..d976bfed50 100644 --- a/i18n/nb.po +++ b/i18n/nb.po @@ -1,14 +1,14 @@ # # Translators: # Caroline Hesthagen Holen , 2022 -# Karoline Tufte Lien , 2023 +# Karoline Tufte Lien , 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-06T13:41:14.540Z\n" +"POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Karoline Tufte Lien , 2023\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" @@ -195,7 +195,7 @@ msgid "Only '{{- name}}' in use" msgstr "Bare '{{- name}}' er i bruk" msgid "Only '{{number}}' in use" -msgstr "Bare '{{nummer}}' er i bruk" +msgstr "Bare '{{number}}' er i bruk" msgid "All items are selected" msgstr "Alle elementer er valgt" @@ -389,6 +389,9 @@ msgstr "Inkluder kumulativ" msgid "Cumulative values" msgstr "Kumulative verdier" +msgid "Accumulate cell values along rows" +msgstr "" + msgid "Show data item icon" msgstr "" @@ -711,6 +714,9 @@ msgid "" msgstr "" "Vis data på et verdenskart visuelt. Dataelementer bruker separate kartlag." +msgid "Not supported when using cumulative values" +msgstr "" + msgid "No data available" msgstr "Ingen data tilgjengelig" @@ -842,6 +848,14 @@ msgstr "" msgid "Something went wrong" msgstr "Noe gikk galt" +msgid "" +"There's a problem with the generated analytics. Contact a system " +"administrator." +msgstr "" + +msgid "There's a syntax problem with the analytics request." +msgstr "" + msgid "or" msgstr "eller" @@ -1008,6 +1022,9 @@ msgstr "Linjer" msgid "Totals" msgstr "Totalsummer" +msgid "Totals are not supported when using cumulative values" +msgstr "" + msgid "Vertical (y) axis {{axisId}}" msgstr "" diff --git a/i18n/zh.po b/i18n/zh.po index 3e4ef431de..77ce15e6fc 100644 --- a/i18n/zh.po +++ b/i18n/zh.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-06T13:41:14.540Z\n" +"POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" "Last-Translator: easylin , 2023\n" "Language-Team: Chinese (https://app.transifex.com/hisp-uio/teams/100509/zh/)\n" @@ -379,6 +379,9 @@ msgstr "包括累计" msgid "Cumulative values" msgstr "汇总值" +msgid "Accumulate cell values along rows" +msgstr "沿行累积单元格的值" + msgid "Show data item icon" msgstr "显示数据项图标" @@ -694,6 +697,9 @@ msgid "" "Visually plot data on a world map. Data elements use separate map layers." msgstr "在世界地图上直观地绘制数据。数据元素使用单独的地图图层。" +msgid "Not supported when using cumulative values" +msgstr "使用累积值时不支持" + msgid "No data available" msgstr "没有值" @@ -976,6 +982,9 @@ msgstr "线" msgid "Totals" msgstr "总共" +msgid "Totals are not supported when using cumulative values" +msgstr "使用累积值时不支持总计" + msgid "Vertical (y) axis {{axisId}}" msgstr "垂直 (y) 轴 {{axisId}}" From 86d968dd89d7c0435a4fb25479a31c160f2a1d7a Mon Sep 17 00:00:00 2001 From: Adel Bensaad Date: Mon, 22 Jan 2024 13:06:41 +0000 Subject: [PATCH 06/13] chore: remove testRegex from jest config (#2956) Remove testRegex from Jest config to revert to default jest test file detection --- jest.config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index 08483cdd6b..ed1d53c0fd 100644 --- a/jest.config.js +++ b/jest.config.js @@ -5,7 +5,6 @@ module.exports = { setupFilesAfterEnv: ['./config/testSetup.js'], testRunner: 'jest-circus/runner', - testRegex: ['/src/modules/__tests__/.*.spec.js?$'], reporters: [ 'default', [ From 065c4b1b76c0bafe7dbef2bf5ebe9f1ef01cfcf2 Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Tue, 23 Jan 2024 09:42:23 +0100 Subject: [PATCH 07/13] chore: add slack bot release success message (#2965) --- .github/workflows/dhis2-verify-app.yml | 62 +++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dhis2-verify-app.yml b/.github/workflows/dhis2-verify-app.yml index 20a4e735fe..12946c819c 100644 --- a/.github/workflows/dhis2-verify-app.yml +++ b/.github/workflows/dhis2-verify-app.yml @@ -82,6 +82,7 @@ jobs: REPORTPORTAL_PROJECT: ${{ vars.REPORTPORTAL_PROJECT }} 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 secrets: @@ -133,13 +134,70 @@ jobs: if: | failure() && !cancelled() && - github.ref == 'refs/heads/master' + github.ref == 'refs/heads/master' && + contains(github.event.head_commit.message, 'chore(release)') steps: + - name: Checkout code + uses: actions/checkout@master + + - name: Extract version + id: extract_version + uses: Saionaro/extract-package-version@v1.2.1 + - name: Send failure message to analytics-internal-bot slack channel id: slack uses: slackapi/slack-github-action@v1.23.0 with: channel-id: ${{ secrets.SLACK_CHANNEL_ID }} - slack-message: ':small_red_triangle_down: Data-visualizer-app release ' + payload: | + { + "text": ":small_red_triangle_down: :data-visualizer-app: DV release ", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": ":small_red_triangle_down: :data-visualizer-app: DV release " + } + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} + + report-release-success: + runs-on: ubuntu-latest + needs: release + if: | + success() && + !cancelled() && + github.ref == 'refs/heads/master' && + contains(github.event.head_commit.message, 'chore(release)') + steps: + - name: Checkout code + uses: actions/checkout@master + + - name: Extract version + id: extract_version + uses: Saionaro/extract-package-version@v1.2.1 + + - name: Send success message to analytics-internal-bot slack channel + id: slack + uses: slackapi/slack-github-action@v1.23.0 + with: + channel-id: ${{ secrets.SLACK_CHANNEL_ID }} + payload: | + { + "text": ":large_green_circle: :data-visualizer-app: :tada: DV app release succeeded for version: ${{ steps.extract_version.outputs.version }}", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": ":large_green_circle: :data-visualizer-app: :tada: DV version ${{ steps.extract_version.outputs.version }} released " + } + } + ] + } env: SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} From 5872a5a20e6c04642d2e3e6ea88210999a805235 Mon Sep 17 00:00:00 2001 From: Hendrik de Graaf Date: Mon, 29 Jan 2024 11:07:31 +0100 Subject: [PATCH 08/13] feat: add class-names for push-analytics (#2966) * feat: add class-names for push-analytics * chore: apply consistent naming to push analytics class names * chore: upgrade @dhis2/analytics --- package.json | 2 +- src/components/DownloadMenu/GraphicsMenu.js | 1 + src/components/DownloadMenu/TableMenu.js | 1 + src/components/DownloadMenu/ToolbarDownloadDropdown.js | 6 +++++- yarn.lock | 8 ++++---- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 4226931bb1..2151d1306e 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "start-server-and-test": "^2.0.0" }, "dependencies": { - "@dhis2/analytics": "^26.2.0", + "@dhis2/analytics": "^26.3.0", "@dhis2/app-runtime": "^3.7.0", "@dhis2/app-runtime-adapter-d2": "^1.1.0", "@dhis2/app-service-datastore": "^1.0.0-beta.3", diff --git a/src/components/DownloadMenu/GraphicsMenu.js b/src/components/DownloadMenu/GraphicsMenu.js index 83537ebf52..e765a0dda5 100644 --- a/src/components/DownloadMenu/GraphicsMenu.js +++ b/src/components/DownloadMenu/GraphicsMenu.js @@ -27,6 +27,7 @@ export const GraphicsMenu = ({ hoverable, onDownload }) => { icon={} label={i18n.t('Image (.png)')} onClick={() => onDownload({ format: FILE_FORMAT_PNG })} + className="push-analytics-download-menu-item" /> { onDownload({ type: DOWNLOAD_TYPE_TABLE, diff --git a/src/components/DownloadMenu/ToolbarDownloadDropdown.js b/src/components/DownloadMenu/ToolbarDownloadDropdown.js index 5dcbae0948..3f0038764a 100644 --- a/src/components/DownloadMenu/ToolbarDownloadDropdown.js +++ b/src/components/DownloadMenu/ToolbarDownloadDropdown.js @@ -8,7 +8,11 @@ const ToolbarDownloadDropdown = () => { const { disabled, doDownloadData, doDownloadImage, visType } = useDownload() return ( - + Date: Wed, 14 Feb 2024 15:14:33 +0100 Subject: [PATCH 09/13] chore: include release notes in slackbot message (#2974) --- .github/workflows/dhis2-verify-app.yml | 40 ++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/.github/workflows/dhis2-verify-app.yml b/.github/workflows/dhis2-verify-app.yml index 12946c819c..c87c7cb631 100644 --- a/.github/workflows/dhis2-verify-app.yml +++ b/.github/workflows/dhis2-verify-app.yml @@ -99,7 +99,7 @@ jobs: if: | !github.event.push.repository.fork && github.actor != 'dependabot[bot]' && - github.event_name != 'pull_request' + github.ref == 'refs/heads/master' steps: - uses: actions/checkout@v3 with: @@ -151,13 +151,13 @@ jobs: channel-id: ${{ secrets.SLACK_CHANNEL_ID }} payload: | { - "text": ":small_red_triangle_down: :data-visualizer-app: DV release ", + "text": ":small_red_triangle_down: :data-visualizer-app: Data Visualizer release ", "blocks": [ { "type": "section", "text": { "type": "mrkdwn", - "text": ":small_red_triangle_down: :data-visualizer-app: DV release " + "text": ":small_red_triangle_down: :data-visualizer-app: Data Visualizer release " } } ] @@ -188,13 +188,41 @@ jobs: channel-id: ${{ secrets.SLACK_CHANNEL_ID }} payload: | { - "text": ":large_green_circle: :data-visualizer-app: :tada: DV app release succeeded for version: ${{ steps.extract_version.outputs.version }}", + "text": "Data Visualizer app release ${{ steps.extract_version.outputs.version }} succeeded", "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": ":large_green_circle: :data-visualizer-app: Data Visualizer version ${{ steps.extract_version.outputs.version }} released :tada:", + "emoji": true + } + }, + { + "type": "divider" + }, { "type": "section", "text": { - "type": "mrkdwn", - "text": ":large_green_circle: :data-visualizer-app: :tada: DV version ${{ steps.extract_version.outputs.version }} released " + "type": "mrkdwn", + "text": "*Release Notes*" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": ${{ toJSON(github.event.head_commit.message) }} + } + }, + { + "type": "divider" + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "Link to " } } ] From 1156a51bf3864e22a5e6ed595722c4680f5d0333 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 26 Feb 2024 10:04:56 +0000 Subject: [PATCH 10/13] test: adapt tests to new database (#2975) --- cypress/elements/dimensionsPanel.js | 1 + cypress/integration/dimensions/data.cy.js | 2 +- cypress/integration/dimensions/dynamic.cy.js | 26 +++++++++----------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/cypress/elements/dimensionsPanel.js b/cypress/elements/dimensionsPanel.js index e795e99d11..73b2e09d01 100644 --- a/cypress/elements/dimensionsPanel.js +++ b/cypress/elements/dimensionsPanel.js @@ -75,6 +75,7 @@ export const expectDimensionToNotHaveSelectedStyle = (dimensionId) => export const expectRecommendedIconToBeVisible = (dimensionId) => cy .getBySel(getDimensionButtonById(dimensionId)) + .scrollIntoView() .findBySel(recommendedIconEl) .should('have.length', 1) .and('be.visible') diff --git a/cypress/integration/dimensions/data.cy.js b/cypress/integration/dimensions/data.cy.js index 98932758fa..8b8244eba8 100644 --- a/cypress/integration/dimensions/data.cy.js +++ b/cypress/integration/dimensions/data.cy.js @@ -254,7 +254,7 @@ describe('Data dimension', () => { { name: 'Event data items', testGroup: { name: 'Information Campaign', itemAmount: 6 }, - testItem: { name: 'Diagnosis (ICD-10)' }, + testItem: { name: 'E2E TE program 1 First name' }, defaultGroup: { name: 'All programs' }, endpoint: { hasMultiplePages: true, diff --git a/cypress/integration/dimensions/dynamic.cy.js b/cypress/integration/dimensions/dynamic.cy.js index 44e722fbc6..d632357538 100644 --- a/cypress/integration/dimensions/dynamic.cy.js +++ b/cypress/integration/dimensions/dynamic.cy.js @@ -20,11 +20,8 @@ import { expectDimensionToHaveItemAmount, } from '../../elements/layout.js' import { goToStartPage } from '../../elements/startScreen.js' -import { TEST_DATA_ELEMENTS } from '../../utils/data.js' -import { getRandomArrayItem } from '../../utils/random.js' import { expectWindowConfigSeriesToHaveLength } from '../../utils/window.js' -const TEST_DATA_ELEMENT_NAME = getRandomArrayItem(TEST_DATA_ELEMENTS).name const TEST_DYNAMIC_DIMENSION = { id: 'J5jldMd8OHv', name: 'Facility type', @@ -32,15 +29,16 @@ const TEST_DYNAMIC_DIMENSION = { } describe(`Dynamic dimension - ${TEST_DYNAMIC_DIMENSION.name}`, () => { - it('navigates to the start page and adds a data item', () => { + it('can add and remove items, which persist after saving', () => { + cy.log('navigates to the start page and adds a data item') goToStartPage() openDimension(DIMENSION_ID_DATA) - selectDataElements([TEST_DATA_ELEMENT_NAME]) + selectDataElements(['ANC 2nd visit']) clickDimensionModalUpdateButton() expectVisualizationToBeVisible(VIS_TYPE_COLUMN) - }) - const TEST_ITEM = 'Hospital' - it('adds an item manually', () => { + + const TEST_ITEM = 'Hospital' + cy.log('adds an item manually') openDimension(TEST_DYNAMIC_DIMENSION.id) expectDimensionModalToBeVisible(TEST_DYNAMIC_DIMENSION.id) expectManualSelectionToBeChecked() @@ -49,8 +47,8 @@ describe(`Dynamic dimension - ${TEST_DYNAMIC_DIMENSION.name}`, () => { expectVisualizationToBeVisible(VIS_TYPE_COLUMN) expectWindowConfigSeriesToHaveLength(1) expectDimensionToHaveItemAmount(TEST_DYNAMIC_DIMENSION.id, 1) - }) - it('adds all items automatically', () => { + + cy.log('adds all items automatically') openDimension(TEST_DYNAMIC_DIMENSION.id) expectDimensionModalToBeVisible(TEST_DYNAMIC_DIMENSION.id) expectManualSelectionToBeChecked() @@ -60,8 +58,8 @@ describe(`Dynamic dimension - ${TEST_DYNAMIC_DIMENSION.name}`, () => { expectVisualizationToBeVisible(VIS_TYPE_COLUMN) expectWindowConfigSeriesToHaveLength(TEST_DYNAMIC_DIMENSION.itemAmount) expectDimensionToHaveAllItemsSelected(TEST_DYNAMIC_DIMENSION.id) - }) - it('switches back to manual and previous item is presisted', () => { + + cy.log('switches back to manual and previous item is persisted') openDimension(TEST_DYNAMIC_DIMENSION.id) expectDimensionModalToBeVisible(TEST_DYNAMIC_DIMENSION.id) expectAutomaticSelectionToBeChecked() @@ -72,8 +70,8 @@ describe(`Dynamic dimension - ${TEST_DYNAMIC_DIMENSION.name}`, () => { expectVisualizationToBeVisible(VIS_TYPE_COLUMN) expectWindowConfigSeriesToHaveLength(1) expectDimensionToHaveItemAmount(TEST_DYNAMIC_DIMENSION.id, 1) - }) - it('switches back to automatic, saving, the selection is presisted', () => { + + cy.log('switches back to automatic, saving, the selection is persisted') openDimension(TEST_DYNAMIC_DIMENSION.id) expectDimensionModalToBeVisible(TEST_DYNAMIC_DIMENSION.id) expectManualSelectionToBeChecked() From 459c53c1b1d655596a7ccdaac99731b433ce5307 Mon Sep 17 00:00:00 2001 From: "@dhis2-bot" Date: Tue, 27 Feb 2024 02:31:55 +0100 Subject: [PATCH 11/13] fix(translations): sync translations from transifex (dev) Automatically merged. --- i18n/sv.po | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/i18n/sv.po b/i18n/sv.po index da42086d6e..fa9ba47fb5 100644 --- a/i18n/sv.po +++ b/i18n/sv.po @@ -2,13 +2,14 @@ # Translators: # Philip Larsen Donnelly, 2020 # Viktor Varland , 2021 +# Jason Pickering , 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-05-30T12:32:59.044Z\n" +"POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Viktor Varland , 2021\n" +"Last-Translator: Jason Pickering , 2024\n" "Language-Team: Swedish (https://app.transifex.com/hisp-uio/teams/100509/sv/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -375,6 +376,9 @@ msgstr "" msgid "Cumulative values" msgstr "" +msgid "Accumulate cell values along rows" +msgstr "" + msgid "Show data item icon" msgstr "" @@ -474,10 +478,10 @@ msgstr "" msgid "Legend type" msgstr "Typ av symbolförklaring" -msgid "Use pre-defined legend per data item" +msgid "Use pre-defined legend by data item" msgstr "" -msgid "Select a single legend for the entire visualization" +msgid "Select a legend" msgstr "" msgid "Legend changes background color" @@ -690,6 +694,9 @@ msgid "" "Visually plot data on a world map. Data elements use separate map layers." msgstr "" +msgid "Not supported when using cumulative values" +msgstr "" + msgid "No data available" msgstr "" @@ -743,7 +750,7 @@ msgid "" msgstr "" msgid "No data selected" -msgstr "" +msgstr "Ingen data har valts" msgid "" "{{visualizationType}} must have at least one data item or data element group" @@ -808,6 +815,14 @@ msgstr "" msgid "Something went wrong" msgstr "" +msgid "" +"There's a problem with the generated analytics. Contact a system " +"administrator." +msgstr "" + +msgid "There's a syntax problem with the analytics request." +msgstr "" + msgid "or" msgstr "" @@ -970,6 +985,9 @@ msgstr "" msgid "Totals" msgstr "Totals" +msgid "Totals are not supported when using cumulative values" +msgstr "" + msgid "Vertical (y) axis {{axisId}}" msgstr "" From 476e8a8e0190731e81a32c00d7c620bc7c196b4e Mon Sep 17 00:00:00 2001 From: "@dhis2-bot" Date: Wed, 28 Feb 2024 13:03:38 +0100 Subject: [PATCH 12/13] fix(translations): sync translations from transifex (dev) Automatically merged. --- i18n/ar.po | 25 +++++++++++++++++++---- i18n/cs.po | 28 +++++++++++++++++++++----- i18n/es.po | 38 +++++++++++++++++++++++++++-------- i18n/es_419.po | 29 +++++++++++++++++++++------ i18n/id.po | 26 ++++++++++++++++++++---- i18n/nb.po | 5 +++-- i18n/nl.po | 28 +++++++++++++++++++++----- i18n/pt.po | 49 +++++++++++++++++++++++++++++++--------------- i18n/pt_BR.po | 40 ++++++++++++++++++++++++++----------- i18n/uz_UZ_Cyrl.po | 30 +++++++++++++++++++++++----- i18n/uz_UZ_Latn.po | 26 ++++++++++++++++++++---- i18n/vi.po | 49 +++++++++++++++++++++++++++++++--------------- i18n/zh.po | 16 +++++++++++---- i18n/zh_CN.po | 24 ++++++++++++++++++++--- 14 files changed, 320 insertions(+), 93 deletions(-) diff --git a/i18n/ar.po b/i18n/ar.po index 16b77191f5..a22426cb11 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: 2023-09-06T13:41:14.540Z\n" +"POT-Creation-Date: 2023-11-13T12:11:28.959Z\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" @@ -194,7 +194,7 @@ msgid "Only '{{- name}}' in use" msgstr "قيد الإستخدام '{{- name}}' فقط" msgid "Only '{{number}}' in use" -msgstr "قيد الإستخدام '{{- name}}' فقط" +msgstr "قيد الإستخدام '{{number}}' فقط" msgid "All items are selected" msgstr "" @@ -386,6 +386,9 @@ msgstr "تضمين التراكم" msgid "Cumulative values" msgstr "قيم تراكمية" +msgid "Accumulate cell values along rows" +msgstr "" + msgid "Show data item icon" msgstr "" @@ -705,6 +708,9 @@ msgstr "" "رسم البيانات بصريًا على خريطة العالم. تستخدم عناصر البيانات طبقات خريطة " "منفصلة." +msgid "Not supported when using cumulative values" +msgstr "" + msgid "No data available" msgstr "لا توجد بيانات متاحة" @@ -835,6 +841,14 @@ msgstr "" msgid "Something went wrong" msgstr "هناك خطأ ما" +msgid "" +"There's a problem with the generated analytics. Contact a system " +"administrator." +msgstr "" + +msgid "There's a syntax problem with the analytics request." +msgstr "" + msgid "or" msgstr "أو" @@ -997,6 +1011,9 @@ msgstr "الخطوط" msgid "Totals" msgstr "الإجمالي" +msgid "Totals are not supported when using cumulative values" +msgstr "" + msgid "Vertical (y) axis {{axisId}}" msgstr "" diff --git a/i18n/cs.po b/i18n/cs.po index 00fb9f303f..ba438bd71a 100644 --- a/i18n/cs.po +++ b/i18n/cs.po @@ -1,13 +1,14 @@ # # Translators: # Jiří Podhorecký, 2022 +# Philip Larsen Donnelly, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-06T13:41:14.540Z\n" +"POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Jiří Podhorecký, 2022\n" +"Last-Translator: Philip Larsen Donnelly, 2024\n" "Language-Team: Czech (https://app.transifex.com/hisp-uio/teams/100509/cs/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -387,6 +388,9 @@ msgstr "Zahrnout kumulativní" msgid "Cumulative values" msgstr "Kumulativní hodnoty" +msgid "Accumulate cell values along rows" +msgstr "" + msgid "Show data item icon" msgstr "" @@ -718,6 +722,9 @@ msgstr "" "Vizuálně vykreslete data na mapě světa. Datové prvky používají samostatné " "mapové vrstvy." +msgid "Not supported when using cumulative values" +msgstr "" + msgid "No data available" msgstr "Údaje nejsou k dispozici" @@ -735,14 +742,14 @@ msgid "Add at least one item to {{axisName}}." msgstr "Přidat alespoň jednu položku do {{axisName}}." msgid "{{columnsAxisName}} and {{rowsAxisName}} are empty" -msgstr "{{columnsAxisName}} a {{linesAxisName}} jsou prázdné" +msgstr "{{columnsAxisName}} a {{rowsAxisName}} jsou prázdné" msgid "" "Add at least one item to {{columnsAxisName}} or {{rowsAxisName}} to create a" " {{visualizationType}}." msgstr "" -"Přidejte alespoň jednu položku do {{columnsAxisName}} nebo {{linesAxisName}}" -" a vytvořte {{visualizationType}}." +"Přidejte alespoň jednu položku do {{columnsAxisName}} nebo {{rowsAxisName}} " +"a vytvořte {{visualizationType}}." msgid "No period selected" msgstr "Není vybráno žádné období" @@ -851,6 +858,14 @@ msgstr "" msgid "Something went wrong" msgstr "Něco se pokazilo" +msgid "" +"There's a problem with the generated analytics. Contact a system " +"administrator." +msgstr "" + +msgid "There's a syntax problem with the analytics request." +msgstr "" + msgid "or" msgstr "nebo" @@ -1017,6 +1032,9 @@ msgstr "Linie" msgid "Totals" msgstr "Součty" +msgid "Totals are not supported when using cumulative values" +msgstr "" + msgid "Vertical (y) axis {{axisId}}" msgstr "Svislá osa (y) {{axisId}}" diff --git a/i18n/es.po b/i18n/es.po index 484d4d4474..f79de3b039 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,21 +1,21 @@ # # Translators: # ericbp , 2020 -# Philip Larsen Donnelly, 2020 # Gabriela Rodriguez , 2020 # Pablo Pajuelo Cabezas , 2020 # Prabhjot Singh, 2021 # Viktor Varland , 2021 # Carlos Tejo Alonso, 2022 -# Enzo Nicolas Rossi , 2023 # Janeth Cruz, 2023 +# Enzo Nicolas Rossi , 2024 +# Philip Larsen Donnelly, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-06T13:41:14.540Z\n" +"POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Janeth Cruz, 2023\n" +"Last-Translator: Philip Larsen Donnelly, 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" @@ -33,7 +33,7 @@ msgid "Untitled {{visualizationType}} visualization, {{date}}" msgstr "Visualización {{visualizationType}} sin título, {{date}}" msgid "\"{{- deletedObject}}\" successfully deleted." -msgstr "\"{{- deleteObject}}\" se eliminó con éxito." +msgstr "\"{{- deletedObject}}\" se eliminó con éxito." msgid "You have unsaved changes." msgstr "Tiene cambios sin guardar." @@ -55,7 +55,7 @@ msgid "Yes, leave" msgstr "Si, salir" msgid "Add to {{axisName}}" -msgstr "Añadir a {{Nombredeleje}}" +msgstr "Añadir a {{axisName}}" msgid "" "'{{visualizationType}}' is intended to show a single data item. Only the " @@ -328,6 +328,8 @@ msgid "" "Number of axis tick steps, including the min and max. A value of 2 or lower " "will be ignored." msgstr "" +"Número de pasos de las marcas de los ejes, incluyendo el mínimo y el máximo." +" Se ignorará un valor de 2 o menor." msgid "Steps" msgstr "Pasos" @@ -395,6 +397,9 @@ msgstr "Incluir acumulado" msgid "Cumulative values" msgstr "Valores acumulativos" +msgid "Accumulate cell values along rows" +msgstr "Acumular valores de celdas a lo largo de las filas" + msgid "Show data item icon" msgstr "Mostrar el icono de elemento de datos" @@ -497,10 +502,10 @@ msgid "Legend type" msgstr "Tipo de leyenda" msgid "Use pre-defined legend by data item" -msgstr "" +msgstr "Utilizar leyenda predefinida por elemento de datos" msgid "Select a legend" -msgstr "" +msgstr "Selecciona una leyenda" msgid "Legend changes background color" msgstr "La leyenda cambia el color de fondo" @@ -729,6 +734,9 @@ msgstr "" "Trazar visualmente los datos en un mapa mundial. Los elementos de datos usan" " capas de mapa separadas." +msgid "Not supported when using cumulative values" +msgstr "No es compatible cuando se utilizan valores acumulativos." + msgid "No data available" msgstr "No hay datos disponibles" @@ -870,6 +878,16 @@ msgstr "" msgid "Something went wrong" msgstr "Algo ha ido mal" +msgid "" +"There's a problem with the generated analytics. Contact a system " +"administrator." +msgstr "" +"Hay un problema con los análisis generados. Contacta a un administrador del " +"sistema." + +msgid "There's a syntax problem with the analytics request." +msgstr "Hay un problema de sintaxis con la solicitud de análisis." + msgid "or" msgstr "o" @@ -1041,6 +1059,10 @@ msgstr "Líneas" msgid "Totals" msgstr "Totales" +msgid "Totals are not supported when using cumulative values" +msgstr "" +"Los totales no son compatibles cuando se utilizan valores acumulativos." + msgid "Vertical (y) axis {{axisId}}" msgstr "Eje vertical (y) {{axisId}}" diff --git a/i18n/es_419.po b/i18n/es_419.po index 0647dbf5cf..7e5c6a669e 100644 --- a/i18n/es_419.po +++ b/i18n/es_419.po @@ -1,14 +1,14 @@ # # Translators: # Jaime Bosque , 2022 -# Enzo Nicolas Rossi , 2023 +# Enzo Nicolas Rossi , 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-05-30T12:32:59.044Z\n" +"POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Enzo Nicolas Rossi , 2023\n" +"Last-Translator: Enzo Nicolas Rossi , 2024\n" "Language-Team: Spanish (Latin America) (https://app.transifex.com/hisp-uio/teams/100509/es_419/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -46,7 +46,7 @@ msgid "Yes, leave" msgstr "" msgid "Add to {{axisName}}" -msgstr "" +msgstr "Añadir a {{axisName}}" msgid "" "'{{visualizationType}}' is intended to show a single data item. Only the " @@ -375,6 +375,9 @@ msgstr "" msgid "Cumulative values" msgstr "" +msgid "Accumulate cell values along rows" +msgstr "" + msgid "Show data item icon" msgstr "" @@ -474,10 +477,10 @@ msgstr "" msgid "Legend type" msgstr "" -msgid "Use pre-defined legend per data item" +msgid "Use pre-defined legend by data item" msgstr "" -msgid "Select a single legend for the entire visualization" +msgid "Select a legend" msgstr "" msgid "Legend changes background color" @@ -690,6 +693,9 @@ msgid "" "Visually plot data on a world map. Data elements use separate map layers." msgstr "" +msgid "Not supported when using cumulative values" +msgstr "" + msgid "No data available" msgstr "" @@ -808,6 +814,14 @@ msgstr "" msgid "Something went wrong" msgstr "Algo ha ido mal" +msgid "" +"There's a problem with the generated analytics. Contact a system " +"administrator." +msgstr "" + +msgid "There's a syntax problem with the analytics request." +msgstr "" + msgid "or" msgstr "" @@ -970,6 +984,9 @@ msgstr "" msgid "Totals" msgstr "" +msgid "Totals are not supported when using cumulative values" +msgstr "" + msgid "Vertical (y) axis {{axisId}}" msgstr "" diff --git a/i18n/id.po b/i18n/id.po index 09b5352d52..431c590483 100644 --- a/i18n/id.po +++ b/i18n/id.po @@ -6,13 +6,14 @@ # Untoro Dwi Raharjo , 2021 # Aprisa Chrysantina , 2021 # Raja Fathurrahim, 2022 +# Philip Larsen Donnelly, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-06T13:41:14.540Z\n" +"POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Raja Fathurrahim, 2022\n" +"Last-Translator: Philip Larsen Donnelly, 2024\n" "Language-Team: Indonesian (https://app.transifex.com/hisp-uio/teams/100509/id/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -65,7 +66,7 @@ msgid "" "'{{visualiationType}}' is intended to show maximum {{maxNumber}} number of " "items. Only the first {{maxNumber}} items will be used and saved." msgstr "" -"'{{visualizationType}}' digunakan untuk menunjukkan maksimum {{maxNumber}} " +"'{{visualiationType}}' digunakan untuk menunjukkan maksimum {{maxNumber}} " "item. Hanya {{maxNumber}} item pertama yang akan digunakan dan disimpan." msgid "" @@ -393,6 +394,9 @@ msgstr "Masukkan kumulatif" msgid "Cumulative values" msgstr "Nilai Kumulatif" +msgid "Accumulate cell values along rows" +msgstr "" + msgid "Show data item icon" msgstr "" @@ -712,7 +716,7 @@ msgid "Change org unit" msgstr "Ubah unit organisasi" msgid "{{level}} level in {{orgunit}}" -msgstr "{{level}} level pada {{unit organisasi}}" +msgstr "{{level}} level pada {{orgunit}}" msgid "Open as Map" msgstr "Buka sebagai Peta" @@ -723,6 +727,9 @@ msgstr "" "Plot data secara visual pada peta dunia. Elemen data menggunakan lapisan " "peta terpisah." +msgid "Not supported when using cumulative values" +msgstr "" + msgid "No data available" msgstr "Tidak ada data yang tersedia" @@ -860,6 +867,14 @@ msgstr "" msgid "Something went wrong" msgstr "Terjadi kesalahan" +msgid "" +"There's a problem with the generated analytics. Contact a system " +"administrator." +msgstr "" + +msgid "There's a syntax problem with the analytics request." +msgstr "" + msgid "or" msgstr "atau" @@ -1025,6 +1040,9 @@ msgstr "Garis" msgid "Totals" msgstr "Total" +msgid "Totals are not supported when using cumulative values" +msgstr "" + msgid "Vertical (y) axis {{axisId}}" msgstr "Sumbu vertikal (y) {{axisId}}" diff --git a/i18n/nb.po b/i18n/nb.po index d976bfed50..25ba6d4f77 100644 --- a/i18n/nb.po +++ b/i18n/nb.po @@ -2,13 +2,14 @@ # Translators: # Caroline Hesthagen Holen , 2022 # Karoline Tufte Lien , 2024 +# Jan Henrik Øverland, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" "POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Karoline Tufte Lien , 2024\n" +"Last-Translator: Jan Henrik Øverland, 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" @@ -87,7 +88,7 @@ msgid "Your dimensions" msgstr "Dine dimensjoner" msgid "Filter dimensions" -msgstr "Filterdimensjoner" +msgstr "Filtrer dimensjoner" msgid "Data value set" msgstr "Dataverdisett" diff --git a/i18n/nl.po b/i18n/nl.po index 67ac49b507..6f36a47d9e 100644 --- a/i18n/nl.po +++ b/i18n/nl.po @@ -5,13 +5,14 @@ # Rica Zamora Duchateau, 2022 # Charel van den Elsen, 2023 # Enzo Nicolas Rossi , 2023 +# Philip Larsen Donnelly, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-06T13:41:14.540Z\n" +"POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Enzo Nicolas Rossi , 2023\n" +"Last-Translator: Philip Larsen Donnelly, 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" @@ -29,7 +30,7 @@ msgid "Untitled {{visualizationType}} visualization, {{date}}" msgstr "Ongetitelde {{visualizationType}} visualisatie, {{date}}" msgid "\"{{- deletedObject}}\" successfully deleted." -msgstr "\"{{- verwijderObject}}\" succesvol verwijderd." +msgstr "\"{{- deletedObject}}\" succesvol verwijderd." msgid "You have unsaved changes." msgstr "U heeft niet opgeslagen veranderingen." @@ -64,8 +65,8 @@ msgid "" "'{{visualiationType}}' is intended to show maximum {{maxNumber}} number of " "items. Only the first {{maxNumber}} items will be used and saved." msgstr "" -"'{{visualizationType}}' is bedoeld om het maximum {{maxNumber}} aantal items" -" te tonen. Alleen de eerste {{maxNumber}} items worden gebruikt en " +"'{{visualiationType}}' is bedoeld om het maximum {{maxNumber}} aantal items " +"te tonen. Alleen de eerste {{maxNumber}} items worden gebruikt en " "opgeslagen." msgid "" @@ -390,6 +391,9 @@ msgstr "" msgid "Cumulative values" msgstr "" +msgid "Accumulate cell values along rows" +msgstr "" + msgid "Show data item icon" msgstr "" @@ -708,6 +712,9 @@ msgid "" "Visually plot data on a world map. Data elements use separate map layers." msgstr "" +msgid "Not supported when using cumulative values" +msgstr "" + msgid "No data available" msgstr "Geen gegevens beschikbaar" @@ -831,6 +838,14 @@ msgstr "" msgid "Something went wrong" msgstr "Er is iets fout gegaan." +msgid "" +"There's a problem with the generated analytics. Contact a system " +"administrator." +msgstr "" + +msgid "There's a syntax problem with the analytics request." +msgstr "" + msgid "or" msgstr "of" @@ -993,6 +1008,9 @@ msgstr "" msgid "Totals" msgstr "Totalen" +msgid "Totals are not supported when using cumulative values" +msgstr "" + msgid "Vertical (y) axis {{axisId}}" msgstr "" diff --git a/i18n/pt.po b/i18n/pt.po index b7f044eb84..8b1f95aec3 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -1,19 +1,19 @@ # # Translators: -# Philip Larsen Donnelly, 2020 # Gabriela Rodriguez , 2020 # Viktor Varland , 2021 # Sheila André , 2021 # Fernando Jorge Bade, 2021 # Ge Joao , 2022 # Emilio Mosse, 2022 +# Philip Larsen Donnelly, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-06T13:41:14.540Z\n" +"POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Emilio Mosse, 2022\n" +"Last-Translator: Philip Larsen Donnelly, 2024\n" "Language-Team: Portuguese (https://app.transifex.com/hisp-uio/teams/100509/pt/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -28,7 +28,7 @@ msgid "Rename successful" msgstr "Renomeado com sucesso" msgid "Untitled {{visualizationType}} visualization, {{date}}" -msgstr "Sem título {{visualizaçãoTipo}} visualização, {{date}}" +msgstr "Sem título {{visualizationType}} visualização, {{date}}" msgid "\"{{- deletedObject}}\" successfully deleted." msgstr "" @@ -53,7 +53,7 @@ msgid "Yes, leave" msgstr "Sim, sair" msgid "Add to {{axisName}}" -msgstr "Adicionar à {{Nomedoeixo}}" +msgstr "Adicionar à {{axisName}}" msgid "" "'{{visualizationType}}' is intended to show a single data item. Only the " @@ -173,11 +173,11 @@ msgid "{{total}} of {{axisMaxNumberOfItems}} selected" msgstr "{{total}} of {{axisMaxNumberOfItems}} selected" msgid "{{total}} selected" -msgstr "{{Total}} selecionados " +msgstr "{{total}} selecionados " msgid "{{dimensionName}} is locked to {{axisName}} for {{visTypeName}}" msgstr "" -"{{dimension Name}} está bloqueado para {{axisName}} para {{visTypeName}}" +"{{dimensionName}} está bloqueado para {{axisName}} para {{visTypeName}}" msgid "Not available for Scatter" msgstr "Não disponível para Dispersão" @@ -198,10 +198,10 @@ msgid "None selected" msgstr "Nenhum selecionado" msgid "Only '{{- name}}' in use" -msgstr "Apenas '{{- nome}}' em uso" +msgstr "Apenas '{{- name}}' em uso" msgid "Only '{{number}}' in use" -msgstr "Somente '{{numero}}' em uso" +msgstr "Somente '{{number}}' em uso" msgid "All items are selected" msgstr "Todos os itens são selecionados" @@ -216,7 +216,7 @@ msgid "And 1 other..." msgstr "E mais 1 ..." msgid "And {{numberOfItems}} others..." -msgstr "E mais {{númerodeítens}} outros ..." +msgstr "E mais {{numberOfItems}} outros ..." msgid "Select a period" msgstr "Selecionar periodos " @@ -395,6 +395,9 @@ msgstr "Incluir cumulativos" msgid "Cumulative values" msgstr "Valores cumulativos " +msgid "Accumulate cell values along rows" +msgstr "" + msgid "Show data item icon" msgstr "" @@ -636,7 +639,7 @@ msgid "Visualization type" msgstr "Tipo de visualização" msgid "Axis {{axisId}}" -msgstr "Eixo {{eixoId}}" +msgstr "Eixo {{axisId}}" msgid "Series is empty" msgstr "A série está vazia." @@ -727,6 +730,9 @@ msgstr "" "Traçar visualmente dados num mapa do mundo. Os elementos de dados usam " "camadas de mapas separadas." +msgid "Not supported when using cumulative values" +msgstr "" + msgid "No data available" msgstr "Sem dados disponiveis " @@ -744,13 +750,13 @@ msgid "Add at least one item to {{axisName}}." msgstr "Adicione pelo menos um item ao {{axisName}}." msgid "{{columnsAxisName}} and {{rowsAxisName}} are empty" -msgstr "{{columnAxisName}} e {{linesAxisName}} estão vazias" +msgstr "{{columnsAxisName}} e {{rowsAxisName}} estão vazias" msgid "" "Add at least one item to {{columnsAxisName}} or {{rowsAxisName}} to create a" " {{visualizationType}}." msgstr "" -"Adicione pelo menos um item a {{columnAxisName}} ou {{linesAxisName}} para " +"Adicione pelo menos um item a {{columnsAxisName}} ou {{rowsAxisName}} para " "criar um {{visualizationType}}." msgid "No period selected" @@ -763,7 +769,7 @@ msgstr "" "{{axes}}. " msgid "Add organisation units to {{axisName}}." -msgstr "Adicione unidades de organização a {{Nome Eixo}}}" +msgstr "Adicione unidades de organização a {{axisName}}}" msgid "Vertical is empty" msgstr "Vertical está vazio" @@ -861,6 +867,14 @@ msgstr "" msgid "Something went wrong" msgstr "Ocorreu algo de errado" +msgid "" +"There's a problem with the generated analytics. Contact a system " +"administrator." +msgstr "" + +msgid "There's a syntax problem with the analytics request." +msgstr "" + msgid "or" msgstr "ou" @@ -1020,7 +1034,7 @@ msgid "Empty data" msgstr "Dados vazios" msgid "Horizontal (x) axis {{axisId}}" -msgstr "Horizontal (x) eixo {{eixoId}}" +msgstr "Horizontal (x) eixo {{axisId}}" msgid "Horizontal (x) axis" msgstr "Eixo Horizontal (x) " @@ -1031,8 +1045,11 @@ msgstr "Linhas " msgid "Totals" msgstr "Totais" +msgid "Totals are not supported when using cumulative values" +msgstr "" + msgid "Vertical (y) axis {{axisId}}" -msgstr "Eixo vertical (y) {{eixoId}}" +msgstr "Eixo vertical (y) {{axisId}}" msgid "Vertical (y) axis" msgstr "Eixo vertical (y)" diff --git a/i18n/pt_BR.po b/i18n/pt_BR.po index e8a673119f..94c9df5f98 100644 --- a/i18n/pt_BR.po +++ b/i18n/pt_BR.po @@ -2,13 +2,14 @@ # Translators: # Philip Larsen Donnelly, 2020 # Viktor Varland , 2020 +# Thiago Rocha, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-05-30T12:32:59.044Z\n" +"POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Viktor Varland , 2020\n" +"Last-Translator: Thiago Rocha, 2024\n" "Language-Team: Portuguese (Brazil) (https://app.transifex.com/hisp-uio/teams/100509/pt_BR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -40,10 +41,10 @@ msgid "" msgstr "" msgid "No, cancel" -msgstr "" +msgstr "Não, cancelar" msgid "Yes, leave" -msgstr "" +msgstr "Sim, sair" msgid "Add to {{axisName}}" msgstr "" @@ -85,10 +86,10 @@ msgid "Data value set" msgstr "" msgid "JSON" -msgstr "" +msgstr "JSON" msgid "XML" -msgstr "" +msgstr "XML" msgid "Other formats" msgstr "" @@ -106,7 +107,7 @@ msgid "Microsoft Excel" msgstr "" msgid "CSV" -msgstr "" +msgstr "CSV" msgid "Advanced" msgstr "" @@ -242,7 +243,7 @@ msgid "Your most viewed charts and tables" msgstr "" msgid "Aggregation type" -msgstr "" +msgstr "Tipo de agregacão" msgid "Overrides aggregation type for all data values." msgstr "" @@ -257,7 +258,7 @@ msgid "Average" msgstr "Média" msgid "Average (sum in org unit hierarchy)" -msgstr "" +msgstr "Média (soma na hierarquia da unidade organizacional)" msgid "Sum" msgstr "Soma" @@ -375,6 +376,9 @@ msgstr "Incluir cumulativa" msgid "Cumulative values" msgstr "" +msgid "Accumulate cell values along rows" +msgstr "" + msgid "Show data item icon" msgstr "" @@ -474,10 +478,10 @@ msgstr "" msgid "Legend type" msgstr "Tipo de legenda" -msgid "Use pre-defined legend per data item" +msgid "Use pre-defined legend by data item" msgstr "" -msgid "Select a single legend for the entire visualization" +msgid "Select a legend" msgstr "" msgid "Legend changes background color" @@ -690,6 +694,9 @@ msgid "" "Visually plot data on a world map. Data elements use separate map layers." msgstr "" +msgid "Not supported when using cumulative values" +msgstr "" + msgid "No data available" msgstr "" @@ -808,6 +815,14 @@ msgstr "" msgid "Something went wrong" msgstr "" +msgid "" +"There's a problem with the generated analytics. Contact a system " +"administrator." +msgstr "" + +msgid "There's a syntax problem with the analytics request." +msgstr "" + msgid "or" msgstr "ou" @@ -970,6 +985,9 @@ msgstr "" msgid "Totals" msgstr "Totais" +msgid "Totals are not supported when using cumulative values" +msgstr "" + msgid "Vertical (y) axis {{axisId}}" msgstr "" diff --git a/i18n/uz_UZ_Cyrl.po b/i18n/uz_UZ_Cyrl.po index 193d29b0d6..bf4b89e5d4 100644 --- a/i18n/uz_UZ_Cyrl.po +++ b/i18n/uz_UZ_Cyrl.po @@ -1,13 +1,14 @@ # # Translators: -# Ibatov , 2023 +# Ibatov , 2024 +# Philip Larsen Donnelly, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-06T13:41:14.540Z\n" +"POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Ibatov , 2023\n" +"Last-Translator: Philip Larsen Donnelly, 2024\n" "Language-Team: Uzbek (Cyrillic) (https://app.transifex.com/hisp-uio/teams/100509/uz@Cyrl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -180,7 +181,7 @@ msgid "Add Assigned Categories" msgstr "Белгиланган категориялар қўшилсин" msgid "Swap with {{axisName}} axis" -msgstr "{{AxisName}} ўқлари жойини алмаштириш" +msgstr "{{axisName}} ўқлари жойини алмаштириш" msgid "vertical" msgstr "вертикал" @@ -390,6 +391,9 @@ msgstr "Кумуляцияни ўз ичига олади" msgid "Cumulative values" msgstr "Кумулятив қийматлар" +msgid "Accumulate cell values along rows" +msgstr "Йиғма қиймат катак ҳажмидан катта" + msgid "Show data item icon" msgstr "Маълумотлар элементи белгисини кўрсатиш" @@ -723,6 +727,9 @@ msgstr "" "Жаҳон харитасига маълумотларни визуал киритинг. Маълумотлар элементлари " "хаританинг алоҳида қаватларидан фойдаланади." +msgid "Not supported when using cumulative values" +msgstr "Йиғма қиймат қўлланилиши қўллаб қувватланмади" + msgid "No data available" msgstr "Маълумотлар мавжуд эмас" @@ -759,7 +766,7 @@ msgstr "" "{{axes}}." msgid "Add organisation units to {{axisName}}." -msgstr "{{AxisName}} ўқига ташкилий бўлимлар қўшиш" +msgstr "{{axisName}} ўқига ташкилий бўлимлар қўшиш" msgid "Vertical is empty" msgstr "Вертикал ўқ бўш" @@ -868,6 +875,16 @@ msgstr "" msgid "Something went wrong" msgstr "Нимадир нотўғри бажарилди" +msgid "" +"There's a problem with the generated analytics. Contact a system " +"administrator." +msgstr "" +"Генерацияланган аналитикада муаммо мавжуд. Тизим администраторига мурожаат " +"қилинг." + +msgid "There's a syntax problem with the analytics request." +msgstr "Аналитика сўровида синтаксис муаммо мавжуд." + msgid "or" msgstr "ёки" @@ -1036,6 +1053,9 @@ msgstr "Қаторлар" msgid "Totals" msgstr "Жами" +msgid "Totals are not supported when using cumulative values" +msgstr "Кумулятив қиймат жами қўлланилганда қўллаб қувватланмади" + msgid "Vertical (y) axis {{axisId}}" msgstr "Вертикал (y) ўқ {{axisId}}" diff --git a/i18n/uz_UZ_Latn.po b/i18n/uz_UZ_Latn.po index 4ad1158492..301f98e6ae 100644 --- a/i18n/uz_UZ_Latn.po +++ b/i18n/uz_UZ_Latn.po @@ -1,13 +1,14 @@ # # Translators: # Yury Rogachev , 2022 +# Philip Larsen Donnelly, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-06T13:41:14.540Z\n" +"POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Yury Rogachev , 2022\n" +"Last-Translator: Philip Larsen Donnelly, 2024\n" "Language-Team: Uzbek (Latin) (https://app.transifex.com/hisp-uio/teams/100509/uz@Latn/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -180,7 +181,7 @@ msgid "Add Assigned Categories" msgstr "Belgilangan kategoriyalar qoʼshilsin" msgid "Swap with {{axisName}} axis" -msgstr "{{AxisName}} oʼqlari joyini almashtirish" +msgstr "{{axisName}} oʼqlari joyini almashtirish" msgid "vertical" msgstr "" @@ -391,6 +392,9 @@ msgstr "Kumulyatsiyani oʼz ichiga oladi" msgid "Cumulative values" msgstr "Kumulyativ qiymatlar" +msgid "Accumulate cell values along rows" +msgstr "" + msgid "Show data item icon" msgstr "" @@ -710,6 +714,9 @@ msgstr "" "Jahon xaritasiga maʼlumotlarni vizual kiriting. Maʼlumotlar elementlari " "xaritaning alohida qavatlaridan foydalanadi." +msgid "Not supported when using cumulative values" +msgstr "" + msgid "No data available" msgstr "" @@ -746,7 +753,7 @@ msgstr "" " {{axes}}." msgid "Add organisation units to {{axisName}}." -msgstr "{{AxisName}} oʼqiga tashkiliy boʼlimlar qoʼshish" +msgstr "{{axisName}} oʼqiga tashkiliy boʼlimlar qoʼshish" msgid "Vertical is empty" msgstr "Vertikal oʼq boʼsh" @@ -851,6 +858,14 @@ msgstr "" msgid "Something went wrong" msgstr "Nimadir notoʼgʼri bajarildi" +msgid "" +"There's a problem with the generated analytics. Contact a system " +"administrator." +msgstr "" + +msgid "There's a syntax problem with the analytics request." +msgstr "" + msgid "or" msgstr "yoki" @@ -1014,6 +1029,9 @@ msgstr "Qatorlar" msgid "Totals" msgstr "Jami" +msgid "Totals are not supported when using cumulative values" +msgstr "" + msgid "Vertical (y) axis {{axisId}}" msgstr "" diff --git a/i18n/vi.po b/i18n/vi.po index 25966d7935..c5c61a414f 100644 --- a/i18n/vi.po +++ b/i18n/vi.po @@ -1,15 +1,15 @@ # # Translators: -# Philip Larsen Donnelly, 2020 # Viktor Varland , 2021 # Mai Nguyen , 2022 +# Philip Larsen Donnelly, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-06T13:41:14.540Z\n" +"POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: Mai Nguyen , 2022\n" +"Last-Translator: Philip Larsen Donnelly, 2024\n" "Language-Team: Vietnamese (https://app.transifex.com/hisp-uio/teams/100509/vi/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -24,7 +24,7 @@ msgid "Rename successful" msgstr "Đổi tên thành công" msgid "Untitled {{visualizationType}} visualization, {{date}}" -msgstr "Mô hình hóa {{loại mô hình}} không có tiêu đề, {{ngày}}" +msgstr "Mô hình hóa {{visualizationType}} không có tiêu đề, {{date}}" msgid "\"{{- deletedObject}}\" successfully deleted." msgstr "" @@ -49,21 +49,21 @@ msgid "Yes, leave" msgstr "Có" msgid "Add to {{axisName}}" -msgstr "Thêm {{tên trục}" +msgstr "Thêm {{axisName}}" msgid "" "'{{visualizationType}}' is intended to show a single data item. Only the " "first item will be used and saved." msgstr "" -"'{{loại mô hình hóa}}' nhằm hiển thị một mục dữ liệu. Chỉ mục đầu tiên sẽ " +"'{{visualizationType}}' nhằm hiển thị một mục dữ liệu. Chỉ mục đầu tiên sẽ " "được sử dụng và lưu." msgid "" "'{{visualiationType}}' is intended to show maximum {{maxNumber}} number of " "items. Only the first {{maxNumber}} items will be used and saved." msgstr "" -"'{{Loại mô hình hóa}}' nhằm hiển thị số lượng mục tối đa {{số lượng tối " -"đa}}. Chỉ {{số lượng tối đa}} mục đầu tiên sẽ được sử dụng và lưu." +"'{{visualiationType}}' nhằm hiển thị số lượng mục tối đa {{maxNumber}}. Chỉ " +"{{maxNumber}} mục đầu tiên sẽ được sử dụng và lưu." msgid "" "'Scatter' is intended to show a single data item per axis. Only the first " @@ -166,13 +166,13 @@ msgid "All" msgstr "Tất cả" msgid "{{total}} of {{axisMaxNumberOfItems}} selected" -msgstr "{{tổng}} / {{axisMaxNumberOfItems}}Đã chọn " +msgstr "{{total}} / {{axisMaxNumberOfItems}}Đã chọn " msgid "{{total}} selected" -msgstr "Đã chọn {{tổng}}" +msgstr "Đã chọn {{total}}" msgid "{{dimensionName}} is locked to {{axisName}} for {{visTypeName}}" -msgstr "{{mensName}} bị khóa với {{axisName}} cho {{visTypeName}}" +msgstr "{{dimensionName}} bị khóa với {{axisName}} cho {{visTypeName}}" msgid "Not available for Scatter" msgstr "Không có sẵn cho phân tán" @@ -390,6 +390,9 @@ msgstr "Bao gồm các giá trị tính toán" msgid "Cumulative values" msgstr "Các giá trị tích lũy" +msgid "Accumulate cell values along rows" +msgstr "" + msgid "Show data item icon" msgstr "" @@ -709,6 +712,9 @@ msgstr "" "Vẽ dữ liệu trực quan trên bản đồ thế giới. Các phần tử dữ liệu sử dụng các " "lớp bản đồ riêng biệt." +msgid "Not supported when using cumulative values" +msgstr "" + msgid "No data available" msgstr "Không có dữ liệu" @@ -726,13 +732,13 @@ msgid "Add at least one item to {{axisName}}." msgstr "Thêm ít nhất một mục vào {{axisName}}." msgid "{{columnsAxisName}} and {{rowsAxisName}} are empty" -msgstr "{{columnAxisName}} và {{rowsAxisName}} trống" +msgstr "{{columnsAxisName}} và {{rowsAxisName}} trống" msgid "" "Add at least one item to {{columnsAxisName}} or {{rowsAxisName}} to create a" " {{visualizationType}}." msgstr "" -"Thêm ít nhất một mục vào {{columnAxisName}} hoặc {{rowAxisName}} để tạo " +"Thêm ít nhất một mục vào {{columnsAxisName}} hoặc {{rowsAxisName}} để tạo " "{{visualizationType}}." msgid "No period selected" @@ -742,7 +748,7 @@ msgid "" "{{visualizationType}} must have at least one period selected in {{axes}}." msgstr "" "{{visualizationType}} phải có ít nhất một thời điểm được chọn trong " -"{{axis}}." +"{{axes}}." msgid "Add organisation units to {{axisName}}." msgstr "Thêm đơn vị vào {{axisName}}." @@ -777,10 +783,10 @@ msgid "" " set item in {{axes}}." msgstr "" "{{visualizationType}} phải có ít nhất một mục dữ liệu hoặc mục nhóm phần tử " -"dữ liệu trong {{axis}}." +"dữ liệu trong {{axes}}." msgid "{{visualizationType}} must have at least one data item in {{axes}}." -msgstr "{{visualizationType}} phải có ít nhất một mục dữ liệu trong {{axis}}." +msgstr "{{visualizationType}} phải có ít nhất một mục dữ liệu trong {{axes}}." msgid "There's a problem with the layout" msgstr "Có lỗi bố cục" @@ -843,6 +849,14 @@ msgstr "" msgid "Something went wrong" msgstr "Có lỗi" +msgid "" +"There's a problem with the generated analytics. Contact a system " +"administrator." +msgstr "" + +msgid "There's a syntax problem with the analytics request." +msgstr "" + msgid "or" msgstr "hoặc" @@ -1005,6 +1019,9 @@ msgstr "Các đường" msgid "Totals" msgstr "Tổng" +msgid "Totals are not supported when using cumulative values" +msgstr "" + msgid "Vertical (y) axis {{axisId}}" msgstr "" diff --git a/i18n/zh.po b/i18n/zh.po index 77ce15e6fc..8d955a578a 100644 --- a/i18n/zh.po +++ b/i18n/zh.po @@ -1,16 +1,16 @@ # # Translators: -# Philip Larsen Donnelly, 2020 # Viktor Varland , 2020 # 晓东 林 <13981924470@126.com>, 2022 -# easylin , 2023 +# easylin , 2024 +# Philip Larsen Donnelly, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" "POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: easylin , 2023\n" +"Last-Translator: Philip Larsen Donnelly, 2024\n" "Language-Team: Chinese (https://app.transifex.com/hisp-uio/teams/100509/zh/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -59,7 +59,7 @@ msgid "" "'{{visualiationType}}' is intended to show maximum {{maxNumber}} number of " "items. Only the first {{maxNumber}} items will be used and saved." msgstr "" -"'{{visualizationType}}' 打算显示最多{{maxNumber}}个数据项,仅{{maxNumber}} " +"'{{visualiationType}}' 打算显示最多{{maxNumber}}个数据项,仅{{maxNumber}} " "个中第一个条目将被使用和保存。" msgid "" @@ -820,6 +820,14 @@ msgstr "所选数据维度未返回任何有效数据。这种可视化类型只 msgid "Something went wrong" msgstr "出了些问题" +msgid "" +"There's a problem with the generated analytics. Contact a system " +"administrator." +msgstr "生成的分析存在问题。联系系统管理员。" + +msgid "There's a syntax problem with the analytics request." +msgstr "分析请求存在语法问题。" + msgid "or" msgstr "或" diff --git a/i18n/zh_CN.po b/i18n/zh_CN.po index 91ce1f94e0..7260334a59 100644 --- a/i18n/zh_CN.po +++ b/i18n/zh_CN.po @@ -2,13 +2,14 @@ # Translators: # 晓东 林 <13981924470@126.com>, 2020 # easylin , 2022 +# Philip Larsen Donnelly, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-06T13:41:14.540Z\n" +"POT-Creation-Date: 2023-11-13T12:11:28.959Z\n" "PO-Revision-Date: 2019-06-25 18:46+0000\n" -"Last-Translator: easylin , 2022\n" +"Last-Translator: Philip Larsen Donnelly, 2024\n" "Language-Team: Chinese (China) (https://app.transifex.com/hisp-uio/teams/100509/zh_CN/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -26,7 +27,7 @@ msgid "Untitled {{visualizationType}} visualization, {{date}}" msgstr "" msgid "\"{{- deletedObject}}\" successfully deleted." -msgstr "" +msgstr "“{{- deletedObject}}”已成功删除。" msgid "You have unsaved changes." msgstr "你没有保存修改" @@ -375,6 +376,9 @@ msgstr "包括累计" msgid "Cumulative values" msgstr "汇总值" +msgid "Accumulate cell values along rows" +msgstr "" + msgid "Show data item icon" msgstr "" @@ -690,6 +694,9 @@ msgid "" "Visually plot data on a world map. Data elements use separate map layers." msgstr "" +msgid "Not supported when using cumulative values" +msgstr "" + msgid "No data available" msgstr "没有值" @@ -810,6 +817,14 @@ msgstr "" msgid "Something went wrong" msgstr "出了些问题" +msgid "" +"There's a problem with the generated analytics. Contact a system " +"administrator." +msgstr "" + +msgid "There's a syntax problem with the analytics request." +msgstr "" + msgid "or" msgstr "或" @@ -972,6 +987,9 @@ msgstr "线" msgid "Totals" msgstr "总共" +msgid "Totals are not supported when using cumulative values" +msgstr "" + msgid "Vertical (y) axis {{axisId}}" msgstr "" From 77c5bcd3dcd4daed14e66a7dc7c20175ae4b86a0 Mon Sep 17 00:00:00 2001 From: Hendrik de Graaf Date: Fri, 1 Mar 2024 10:36:47 +0100 Subject: [PATCH 13/13] fix(push-analytics): add push analytics instructions (#2985) --- public/push-analytics.json | 85 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 public/push-analytics.json diff --git a/public/push-analytics.json b/public/push-analytics.json new file mode 100644 index 0000000000..b74e9691d6 --- /dev/null +++ b/public/push-analytics.json @@ -0,0 +1,85 @@ +{ + "version": "0.0.1", + "showVisualization": { + "strategy": "navigateToUrl", + "steps": [ + { "goto": "{{appUrl}}/#/{{id}}" }, + { + "waitForSelectorConditionally": [ + { + "dashboardItemProperty": "visualization.type", + "value": "PIVOT_TABLE", + "selector": ".pivot-table-container > table" + }, + { + "dashboardItemProperty": "visualization.type", + "value": [ + "COLUMN", + "STACKED_COLUMN", + "BAR", + "STACKED_BAR", + "LINE", + "AREA", + "STACKED_AREA", + "PIE", + "RADAR", + "GAUGE", + "YEAR_OVER_YEAR_LINE", + "YEAR_OVER_YEAR_COLUMN", + "SCATTER", + "BUBBLE", + "SINGLE_VALUE", + "PIVOT_TABLE", + "OUTLIER_TABLE" + ], + "selector": ".highcharts-container" + } + ] + } + ] + }, + "triggerDownload": { + "strategy": "useUiElements", + "steps": [ + { "click": ".push-analytics-download-dropdown-menu-button" }, + { "click": ".push-analytics-download-menu-item" } + ] + }, + "obtainDownloadArtifactConditionally": [ + { + "dashboardItemProperty": "visualization.type", + "value": "PIVOT_TABLE", + "strategy": "scrapeDownloadPage", + "htmlSelector": "body", + "cssSelector": "style" + }, + { + "dashboardItemProperty": "visualization.type", + "value": [ + "COLUMN", + "STACKED_COLUMN", + "BAR", + "STACKED_BAR", + "LINE", + "AREA", + "STACKED_AREA", + "PIE", + "RADAR", + "GAUGE", + "YEAR_OVER_YEAR_LINE", + "YEAR_OVER_YEAR_COLUMN", + "SCATTER", + "BUBBLE", + "SINGLE_VALUE", + "PIVOT_TABLE", + "OUTLIER_TABLE" + ], + "strategy": "screenShotImgOnDownloadPage", + "htmlSelector": "img" + } + ], + "clearVisualization": { + "strategy": "navigateToUrl", + "steps": [{ "goto": "{{appUrl}}/#/new" }] + } +}