From db0c6dad98c7f759c42e226f4c5d52b5e8c4b051 Mon Sep 17 00:00:00 2001 From: Edoardo Sabadelli Date: Thu, 11 Apr 2024 12:50:54 +0200 Subject: [PATCH] test: add cypress tests for Outlier table DHIS2-17162 (#3008) --- cypress/elements/chart.js | 7 +- .../elements/dimensionModal/dataDimension.js | 14 +- cypress/elements/dimensionModal/index.js | 3 + .../dimensionModal/periodDimension.js | 12 +- .../integration/visTypes/outlierTable.cy.js | 334 ++++++++++++++++++ cypress/support/getExcludedTags.js | 99 ------ package.json | 5 +- patches/react-scripts+5.0.1.patch | 25 -- .../Options/OutlierDetectionMethod.js | 3 +- .../VisualizationPlugin/OutlierTablePlugin.js | 1 + yarn.lock | 55 +-- 11 files changed, 378 insertions(+), 180 deletions(-) create mode 100644 cypress/integration/visTypes/outlierTable.cy.js delete mode 100644 cypress/support/getExcludedTags.js delete mode 100644 patches/react-scripts+5.0.1.patch diff --git a/cypress/elements/chart.js b/cypress/elements/chart.js index a78f41ff32..e2f2bdb966 100644 --- a/cypress/elements/chart.js +++ b/cypress/elements/chart.js @@ -2,6 +2,7 @@ import { VIS_TYPE_COLUMN, VIS_TYPE_GAUGE, VIS_TYPE_PIE, + VIS_TYPE_OUTLIER_TABLE, VIS_TYPE_PIVOT_TABLE, VIS_TYPE_SINGLE_VALUE, VIS_TYPE_YEAR_OVER_YEAR_COLUMN, @@ -23,7 +24,11 @@ const AOTitleDirtyEl = 'titlebar-dirty' const timeout = { timeout: 40000, } -const nonHighchartsTypes = [VIS_TYPE_PIVOT_TABLE, VIS_TYPE_SINGLE_VALUE] +const nonHighchartsTypes = [ + VIS_TYPE_OUTLIER_TABLE, + VIS_TYPE_PIVOT_TABLE, + VIS_TYPE_SINGLE_VALUE, +] export const expectVisualizationToBeVisible = (visType = VIS_TYPE_COLUMN) => nonHighchartsTypes.includes(visType) diff --git a/cypress/elements/dimensionModal/dataDimension.js b/cypress/elements/dimensionModal/dataDimension.js index 8a71c5cdc9..9a26f5fe44 100644 --- a/cypress/elements/dimensionModal/dataDimension.js +++ b/cypress/elements/dimensionModal/dataDimension.js @@ -11,6 +11,8 @@ const selectableItemsEl = 'data-dimension-transfer-sourceoptions' const selectedItemsEl = 'data-dimension-transfer-pickedoptions' const dataTypesSelectButtonEl = 'data-dimension-left-header-data-types-select-field-content' +const dataTypesSelectHelpEl = + 'data-dimension-left-header-data-types-select-field-help' const dataTypeSelectOptionEl = 'data-dimension-left-header-data-types-select-field-option' const groupSelectButtonEl = @@ -78,6 +80,9 @@ export const switchDataTab = (tabName) => { export const expectDataTypeToBe = (type) => cy.getBySel(dataTypesSelectButtonEl).should('contain', type) +export const expectDataTypeSelectHelpToContain = (text) => + cy.getBySel(dataTypesSelectHelpEl).should('have.text', text) + export const expectGroupSelectToNotBeVisible = () => cy.getBySel(groupSelectButtonEl).should('not.exist') @@ -111,8 +116,13 @@ export const switchSubGroupTo = (group) => { } export const switchDataTypeTo = (dataType) => { - cy.getBySel(dataTypesSelectButtonEl).click() - cy.getBySelLike(dataTypeSelectOptionEl).contains(dataType).click() + cy.getBySel(dataTypesSelectButtonEl).then(($typesSelect) => { + // account for disabled type selector with preselected item + if (!$typesSelect.text().includes(dataType)) { + cy.getBySel(dataTypesSelectButtonEl).click() + cy.getBySelLike(dataTypeSelectOptionEl).contains(dataType).click() + } + }) } export const switchDataTypeToAll = () => { diff --git a/cypress/elements/dimensionModal/index.js b/cypress/elements/dimensionModal/index.js index 9fbc71367f..ff96636212 100644 --- a/cypress/elements/dimensionModal/index.js +++ b/cypress/elements/dimensionModal/index.js @@ -113,6 +113,7 @@ export { expectDataItemToBeInactive, expectDataDimensionModalToBeVisible, expectDataTypeToBe, + expectDataTypeSelectHelpToContain, expectGroupSelectToNotBeVisible, expectNoDataItemsToBeSelected, inputSearchTerm, @@ -153,6 +154,8 @@ export { expectFixedPeriodTypeToBe, expectSelectablePeriodItemsAmountToBeLeast, expectSelectablePeriodItemsAmountToBe, + expectPeriodItemToBeInactive, + expectPeriodDimensionModalWarningToContain, } from './periodDimension.js' export { diff --git a/cypress/elements/dimensionModal/periodDimension.js b/cypress/elements/dimensionModal/periodDimension.js index 5de5e8020a..e5e4aa7782 100644 --- a/cypress/elements/dimensionModal/periodDimension.js +++ b/cypress/elements/dimensionModal/periodDimension.js @@ -9,13 +9,14 @@ const fixedPeriodsPeriodTypeButtonEl = 'period-dimension-fixed-period-filter-period-type-content' const periodTypeMenuEl = 'dhis2-uicore-select-menu-menuwrapper' //const fixedPeriodsYearEl = 'period-dimension-fixed-period-filter-year-content' +const optionContentEl = 'period-dimension-transfer-option-content' const selectableItemsEl = 'period-dimension-transfer-sourceoptions' const selectedItemsEl = 'period-dimension-transfer-pickedoptions' const relativePeriodTypeSelectOptionEl = 'period-dimension-relative-period-filter-option' - const fixedPeriodTypeSelectOptionEl = 'period-dimension-fixed-period-filter-period-type-option' +const rightHeaderEl = 'period-dimension-transfer-rightheader' export const expectPeriodDimensionModalToBeVisible = () => expectDimensionModalToBeVisible(DIMENSION_ID_PERIOD) @@ -96,6 +97,12 @@ export const expectFixedPeriodTypeSelectToNotContain = (periodType) => { ) } +export const expectPeriodItemToBeInactive = (id) => + cy + .get(`[data-value="${id}"]`) + .findBySel(optionContentEl) + .should('have.class', 'inactive') + export const openRelativePeriodsTypeSelect = () => cy.getBySel(relativePeriodsPeriodTypeButtonEl).click() @@ -117,3 +124,6 @@ export const expectSelectablePeriodItemsAmountToBeLeast = (amount) => $container.find('[data-test="period-dimension-transfer-option"]') ).to.have.length.of.at.least(amount) }) + +export const expectPeriodDimensionModalWarningToContain = (text) => + cy.getBySel(rightHeaderEl).should('contain', text) diff --git a/cypress/integration/visTypes/outlierTable.cy.js b/cypress/integration/visTypes/outlierTable.cy.js new file mode 100644 index 0000000000..751448ddb2 --- /dev/null +++ b/cypress/integration/visTypes/outlierTable.cy.js @@ -0,0 +1,334 @@ +import { + DIMENSION_ID_DATA, + DIMENSION_ID_ORGUNIT, + DIMENSION_ID_PERIOD, + visTypeDisplayNames, + VIS_TYPE_OUTLIER_TABLE, + AXIS_ID_COLUMNS, +} from '@dhis2/analytics' +import { expectVisualizationToBeVisible } from '../../elements/chart.js' +import { + clearInput, + typeInput, + expectAppToNotBeLoading, +} from '../../elements/common.js' +import { + selectDataElements, + clickDimensionModalHideButton, + clickDimensionModalUpdateButton, + expectDataTypeToBe, + expectDataTypeSelectHelpToContain, + expectPeriodItemToBeInactive, + expectPeriodDimensionModalWarningToContain, + selectRelativePeriods, + unselectAllItemsByButton, +} from '../../elements/dimensionModal/index.js' +import { openDimension } from '../../elements/dimensionsPanel.js' +import { deleteAO, saveNewAO } from '../../elements/fileMenu/index.js' +import { + expectDimensionOnAxisToHaveLockIcon, + expectDimensionOnAxisToHaveWarningIcon, + expectDimensionToHaveItemAmount, +} from '../../elements/layout.js' +import { + clickMenuBarUpdateButton, + openOptionsModal, +} from '../../elements/menuBar.js' +import { + clickOptionsTab, + clickOptionsModalHideButton, + clickOptionsModalUpdateButton, + OPTIONS_TAB_DATA, + OPTIONS_TAB_OUTLIERS, +} from '../../elements/optionsModal/index.js' +import { expectRouteToBeEmpty } from '../../elements/route.js' +import { + expectErrorToContainTitle, + expectStartScreenToBeVisible, + goToStartPage, +} from '../../elements/startScreen.js' +import { changeVisType } from '../../elements/visualizationTypeSelector.js' + +const TEST_DATA_ELEMENT_NAMES = ['ANC 1st visit', 'ANC 2nd visit'] +const TEST_VIS_NAME = `TEST OUTLIER TABLE ${new Date().toLocaleString()}` + +const maxResultsEl = 'option-outliers-max-result-input-content' +const NEW_MAX_RESULTS = 11 + +const detectionMethodEl = 'options-outliers-detection-method' +const NEW_DETECTION_METHOD = 'Z_SCORE' +const newDetectionMethodSelector = `input[value=STANDARD_${NEW_DETECTION_METHOD}]` +const thresholdEl = 'options-outliers-threshold-content' +const NEW_THRESHOLD = 2 + +describe(['>=41'], 'using an Outlier table visualization', () => { + it('creates, edits, saves it correctly', () => { + cy.log('navigates to a new Outlier table visualization') + goToStartPage() + changeVisType(visTypeDisplayNames[VIS_TYPE_OUTLIER_TABLE]) + + cy.log('Data is locked to Columns') + expectDimensionOnAxisToHaveLockIcon(DIMENSION_ID_DATA, AXIS_ID_COLUMNS) + + cy.log('Period is locked to Columns') + expectDimensionOnAxisToHaveLockIcon( + DIMENSION_ID_PERIOD, + AXIS_ID_COLUMNS + ) + + cy.log('Org unit is locked to Columns') + expectDimensionOnAxisToHaveLockIcon( + DIMENSION_ID_ORGUNIT, + AXIS_ID_COLUMNS + ) + + cy.log('Other dimensions section is disabled') + cy.getBySel('dimensions-panel-list-dynamic-dimensions') + .findBySel('dimensions-panel-list-dimension-item') + .should('not.have.css', 'cursor', 'pointer') + + cy.log('Your dimensions section is disabled') + cy.getBySel('dimensions-panel-list-non-predefined-dimensions') + .findBySel('dimensions-panel-list-dimension-item') + .should('not.have.css', 'cursor', 'pointer') + + cy.log( + 'shows a disabled data type selector with preselected Data elements' + ) + openDimension(DIMENSION_ID_DATA) + expectDataTypeToBe('Data elements') + expectDataTypeSelectHelpToContain( + 'Only Data elements can be used in Outlier table' + ) + clickDimensionModalHideButton() + + cy.log('shows error if no data element selected') + clickMenuBarUpdateButton() + expectErrorToContainTitle('No data selected') + + cy.log('selects 2 data elements') + openDimension(DIMENSION_ID_DATA) + selectDataElements(TEST_DATA_ELEMENT_NAMES) + clickDimensionModalHideButton() + expectDimensionToHaveItemAmount(DIMENSION_ID_DATA, 2) + + cy.log('shows error if no period is selected') + openDimension(DIMENSION_ID_PERIOD) + unselectAllItemsByButton() + clickDimensionModalUpdateButton() + expectErrorToContainTitle('No period selected') + + cy.log('adds 2 periods and displays a warning message') + openDimension(DIMENSION_ID_PERIOD) + selectRelativePeriods(['Last 12 months', 'This month'], 'Months') + expectPeriodItemToBeInactive('THIS_MONTH') + expectPeriodDimensionModalWarningToContain( + "'Outlier table' is intended to show a single item for this type of dimension. Only the first item will be used and saved." + ) + clickDimensionModalHideButton() + expectDimensionOnAxisToHaveWarningIcon( + DIMENSION_ID_PERIOD, + AXIS_ID_COLUMNS + ) + + cy.log('shows an outlier table') + clickMenuBarUpdateButton() + expectVisualizationToBeVisible(VIS_TYPE_OUTLIER_TABLE) + + cy.log('has the correct headers in the table') + const modZScoreHeaderLabels = [ + 'Data', + 'Category option combination', + 'Period', + 'Organisation unit', + 'Value', + 'Median', + 'Modified Z-score', + 'Median absolute deviation', + 'Min', + 'Max', + ] + + cy.getBySel('outlier-table') + .findBySel('table-header') + .each(($el, index) => { + expect($el).to.contain(modZScoreHeaderLabels[index]) + }) + + cy.log('has the correct default number of rows') + cy.getBySel('outlier-table') + .findBySel('table-row') + .should('have.length', 20) + + cy.log('has default sorting on Value descending') + cy.getBySel('outlier-table') + .findBySel('table-header') + .containsExact('Value') + .closest('[data-test="table-header"]') + .find('button') + .should('have.attr', 'title', 'Sort ascending by Value and update') + + cy.log('can be sorted on a different column') + cy.getBySel('outlier-table') + .findBySel('table-header') + .find('button[title="Sort descending by Min and update"]') + .click() + + expectVisualizationToBeVisible(VIS_TYPE_OUTLIER_TABLE) + + cy.getBySel('outlier-table') + .findBySel('table-header') + .containsExact('Value') + .closest('[data-test="table-header"]') + .find('button') + .should('have.attr', 'title', 'Sort descending by Value and update') + + cy.getBySel('outlier-table') + .findBySel('table-header') + .containsExact('Min') + .closest('[data-test="table-header"]') + .find('button') + .should('have.attr', 'title', 'Sort ascending by Min and update') + + cy.log('Options -> Data -> change max results') + openOptionsModal(OPTIONS_TAB_DATA) + + cy.intercept('GET', '**/analytics/outlierDetection?*').as( + 'analyticsRequest' + ) + + // NB. typing the 1st 2 digits of the number is a workaround since it's not possible to directly input the whole number without clearing first + // and when clearing the validation sets the value to 1 (the minimum allowed value) + + // min value is 1 + clearInput(maxResultsEl) + cy.getBySel(maxResultsEl).find('input').should('have.value', 1) + + // max value is 500 + typeInput('option-outliers-max-result-input-content', 50) + cy.getBySel(maxResultsEl).find('input').should('have.value', 500) + + clearInput(maxResultsEl) + typeInput('option-outliers-max-result-input-content', 1) + cy.getBySel(maxResultsEl) + .find('input') + .should('have.value', NEW_MAX_RESULTS) + + clickOptionsModalUpdateButton() + + cy.wait('@analyticsRequest').then(({ request, response }) => { + expect(request.url).to.contain(`maxResults=${NEW_MAX_RESULTS}`) + expect(response.body.rows.length).to.eq(NEW_MAX_RESULTS) + }) + + expectVisualizationToBeVisible(VIS_TYPE_OUTLIER_TABLE) + + cy.getBySel('outlier-table') + .findBySel('table-row') + .should('have.length', NEW_MAX_RESULTS) + + cy.log('Options -> Outliers -> change detection method') + openOptionsModal(OPTIONS_TAB_OUTLIERS) + + cy.getBySel(detectionMethodEl).find(newDetectionMethodSelector).click() + + cy.getBySel(detectionMethodEl) + .find(newDetectionMethodSelector) + .should('be.checked') + + clickOptionsModalUpdateButton() + + cy.wait('@analyticsRequest').then(({ request, response }) => { + expect(request.url).to.contain(`algorithm=${NEW_DETECTION_METHOD}`) + expect(response.body.metaData.algorithm).to.eq(NEW_DETECTION_METHOD) + }) + + expectVisualizationToBeVisible(VIS_TYPE_OUTLIER_TABLE) + + const headerLabelsZScore = [ + 'Data', + 'Category option combination', + 'Period', + 'Organisation unit', + 'Value', + 'Mean', + 'Z-score', + 'Standard deviation', + 'Min', + 'Max', + ] + + cy.getBySel('outlier-table') + .findBySel('table-header') + .each(($el, index) => { + expect($el).to.contain(headerLabelsZScore[index]) + }) + + cy.log('Options -> Outliers -> change threshold') + openOptionsModal(OPTIONS_TAB_OUTLIERS) + + clearInput(thresholdEl) + typeInput(thresholdEl, NEW_THRESHOLD) + + cy.getBySel(thresholdEl) + .find('input') + .should('have.value', NEW_THRESHOLD) + + clickOptionsModalUpdateButton() + + cy.wait('@analyticsRequest').then(({ request, response }) => { + expect(request.url).to.contain(`threshold=${NEW_THRESHOLD}`) + expect(response.body.metaData.threshold).to.eq(NEW_THRESHOLD) + }) + + expectVisualizationToBeVisible(VIS_TYPE_OUTLIER_TABLE) + + cy.log('shows a custom error screen if no data returned') + openOptionsModal(OPTIONS_TAB_OUTLIERS) + + clearInput(thresholdEl) + typeInput(thresholdEl, 10) + + clickOptionsModalUpdateButton() + + expectErrorToContainTitle('No outliers found') + + // reset before save + openOptionsModal(OPTIONS_TAB_OUTLIERS) + clearInput(thresholdEl) + typeInput(thresholdEl, NEW_THRESHOLD) + + clickOptionsModalUpdateButton() + + expectVisualizationToBeVisible(VIS_TYPE_OUTLIER_TABLE) + + cy.log('saves and all options are preserved') + saveNewAO(TEST_VIS_NAME) + + expectAppToNotBeLoading() + expectVisualizationToBeVisible(VIS_TYPE_OUTLIER_TABLE) + + openOptionsModal(OPTIONS_TAB_DATA) + + cy.getBySel(maxResultsEl) + .find('input') + .should('have.value', NEW_MAX_RESULTS) + + clickOptionsTab(OPTIONS_TAB_OUTLIERS) + + cy.getBySel(detectionMethodEl) + .find(newDetectionMethodSelector) + .should('be.checked') + + cy.getBySel(thresholdEl) + .find('input') + .should('have.value', NEW_THRESHOLD) + + clickOptionsModalHideButton() + + cy.log('deletes saved Outlier table AO') + deleteAO() + expectStartScreenToBeVisible() + expectRouteToBeEmpty() + }) +}) diff --git a/cypress/support/getExcludedTags.js b/cypress/support/getExcludedTags.js deleted file mode 100644 index b2fdb6a9f6..0000000000 --- a/cypress/support/getExcludedTags.js +++ /dev/null @@ -1,99 +0,0 @@ -const d2config = require('../../d2.config.js') -/* -The list of excluded tags returned by getExcludedTags are the tags that cypress -will ignore when running the test suite. So if a test is tagged with one of the -tags in the excluded list, then that test will not run. - -Using excluded tags (instead of included tags) allows for most of the tests to -remain untagged and be run against all supported versions of DHIS2. - -DHIS2 officially supports the latest 3 released versions of DHIS2. - -For example: 2.38, 2.39 and 2.40. Dev would then have version 2.41-SNAPSHOT. -Therefore, the getExcludedTags calculates the range of tags based on minimum -supported version + 3 (2.38, 2.39, 2.40, 2.41-SNAPSHOT) - -With the minimum supported version of 2.38, the tags will always -contain "38", "39", "40" and "41", but the comparison symbols will depend on -the current instance version. - -Allowed tag comparisons are ">", ">=", "<", "<=" -*/ - -const getString = (v) => (typeof v === 'number' ? v.toString() : v) - -const extractMinorVersion = (v) => - v.indexOf('2.') === 0 ? parseInt(v.slice(2, 4)) : parseInt(v.slice(0, 2)) - -const MIN_DHIS2_VERSION = extractMinorVersion( - getString(d2config.minDHIS2Version) -) - -const getInstanceMinorVersion = (dhis2InstanceVersion) => { - if (dhis2InstanceVersion.toLowerCase() === 'dev') { - return MIN_DHIS2_VERSION + 3 - } - - return extractMinorVersion(dhis2InstanceVersion) -} - -const getExcludedTags = (v) => { - const currentInstanceVersion = getInstanceMinorVersion(getString(v)) - - if (currentInstanceVersion < MIN_DHIS2_VERSION) { - throw new Error( - 'Instance version is lower than the minimum supported version' - ) - } - - let excludeTags = [] - if (currentInstanceVersion === MIN_DHIS2_VERSION) { - // For example instance = 2.38, MIN = 2.38 - excludeTags = [ - `<${currentInstanceVersion}`, - `>${currentInstanceVersion}`, - `>=${currentInstanceVersion + 1}`, - `>${currentInstanceVersion + 1}`, - `>=${currentInstanceVersion + 2}`, - `>${currentInstanceVersion + 2}`, - `>=${currentInstanceVersion + 3}`, - ] - } else if (currentInstanceVersion === MIN_DHIS2_VERSION + 1) { - // For example instance = 2.39, MIN = 2.38 - excludeTags = [ - `<=${currentInstanceVersion - 1}`, - `<${currentInstanceVersion - 1}`, - `<${currentInstanceVersion}`, - `>${currentInstanceVersion}`, - `>=${currentInstanceVersion + 1}`, - `>${currentInstanceVersion + 1}`, - `>=${currentInstanceVersion + 2}`, - ] - } else if (currentInstanceVersion === MIN_DHIS2_VERSION + 2) { - // For example instance = 2.40, MIN = 2.38 - excludeTags = [ - `<=${currentInstanceVersion - 2}`, - `<${currentInstanceVersion - 2}`, - `<=${currentInstanceVersion - 1}`, - `<${currentInstanceVersion - 1}`, - `<${currentInstanceVersion}`, - `>${currentInstanceVersion}`, - `>=${currentInstanceVersion + 1}`, - ] - } else { - // For example instance = 2.41, MIN = 2.38 - excludeTags = [ - `<=${currentInstanceVersion - 3}`, - `<${currentInstanceVersion - 3}`, - `<=${currentInstanceVersion - 2}`, - `<${currentInstanceVersion - 2}`, - `<${currentInstanceVersion - 1}`, - `<=${currentInstanceVersion - 1}`, - `<${currentInstanceVersion}`, - ] - } - - return excludeTags -} - -module.exports = { getExcludedTags } diff --git a/package.json b/package.json index 08e33654c1..0c8862f850 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,6 @@ "private": true, "scripts": { "deduplicate": "d2-app-scripts deduplicate", - "postinstall": "patch-package", "build": "d2-app-scripts build", "start": "d2-app-scripts start", "start:nobrowser": "BROWSER=none yarn start", @@ -35,10 +34,10 @@ "eslint-plugin-react-hooks": "^4.2.0", "jest-enzyme": "^7.1.2", "loglevel": "^1.8.1", - "patch-package": "^7.0.0", "postinstall-postinstall": "^2.1.0", "redux-mock-store": "^1.5.4", - "start-server-and-test": "^2.0.0" + "start-server-and-test": "^2.0.0", + "typescript": "^4.8.4" }, "dependencies": { "@dhis2/analytics": "^26.6.0", diff --git a/patches/react-scripts+5.0.1.patch b/patches/react-scripts+5.0.1.patch deleted file mode 100644 index e14ed4776a..0000000000 --- a/patches/react-scripts+5.0.1.patch +++ /dev/null @@ -1,25 +0,0 @@ -diff --git a/node_modules/react-scripts/config/webpack.config.js b/node_modules/react-scripts/config/webpack.config.js -index e465d8e..2bc428c 100644 ---- a/node_modules/react-scripts/config/webpack.config.js -+++ b/node_modules/react-scripts/config/webpack.config.js -@@ -239,19 +239,7 @@ module.exports = function (webpackEnv) { - : isEnvDevelopment && - (info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')), - }, -- cache: { -- type: 'filesystem', -- version: createEnvironmentHash(env.raw), -- cacheDirectory: paths.appWebpackCache, -- store: 'pack', -- buildDependencies: { -- defaultWebpack: ['webpack/lib/'], -- config: [__filename], -- tsconfig: [paths.appTsConfig, paths.appJsConfig].filter(f => -- fs.existsSync(f) -- ), -- }, -- }, -+ cache: false, - infrastructureLogging: { - level: 'none', - }, diff --git a/src/components/VisualizationOptions/Options/OutlierDetectionMethod.js b/src/components/VisualizationOptions/Options/OutlierDetectionMethod.js index 4f7cb0f951..a3d0ef1215 100644 --- a/src/components/VisualizationOptions/Options/OutlierDetectionMethod.js +++ b/src/components/VisualizationOptions/Options/OutlierDetectionMethod.js @@ -13,7 +13,7 @@ const OutlierDetectionMethod = ({ }) => ( <>
- + {methods.map(({ id, label }) => ( ) diff --git a/src/components/VisualizationPlugin/OutlierTablePlugin.js b/src/components/VisualizationPlugin/OutlierTablePlugin.js index 687559ce45..0352a60f34 100644 --- a/src/components/VisualizationPlugin/OutlierTablePlugin.js +++ b/src/components/VisualizationPlugin/OutlierTablePlugin.js @@ -230,6 +230,7 @@ const OutlierTablePlugin = ({ ref={mountAndObserveContainerRef} >
diff --git a/yarn.lock b/yarn.lock index 5460909cae..e82cc23cd9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5363,7 +5363,7 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== -ci-info@^3.2.0, ci-info@^3.7.0: +ci-info@^3.2.0: version "3.8.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== @@ -7915,13 +7915,6 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -find-yarn-workspace-root@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd" - integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ== - dependencies: - micromatch "^4.0.2" - flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -10524,13 +10517,6 @@ kind-of@^6.0.0, kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -klaw-sync@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c" - integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ== - dependencies: - graceful-fs "^4.1.11" - kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -11771,7 +11757,7 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -open@^7.0.3, open@^7.4.2: +open@^7.0.3: version "7.4.2" resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== @@ -12043,26 +12029,6 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= -patch-package@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-7.0.0.tgz#5c646b6b4b4bf37e5184a6950777b21dea6bb66e" - integrity sha512-eYunHbnnB2ghjTNc5iL1Uo7TsGMuXk0vibX3RFcE/CdVdXzmdbMsG/4K4IgoSuIkLTI5oHrMQk4+NkFqSed0BQ== - dependencies: - "@yarnpkg/lockfile" "^1.1.0" - chalk "^4.1.2" - ci-info "^3.7.0" - cross-spawn "^7.0.3" - find-yarn-workspace-root "^2.0.0" - fs-extra "^9.0.0" - klaw-sync "^6.0.0" - minimist "^1.2.6" - open "^7.4.2" - rimraf "^2.6.3" - semver "^5.6.0" - slash "^2.0.0" - tmp "^0.0.33" - yaml "^2.2.2" - path-browserify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" @@ -13825,13 +13791,6 @@ rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -rimraf@^2.6.3: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - rimraf@~2.6.2: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" @@ -15425,6 +15384,11 @@ typescript@^3.6.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.3.tgz#d3ac8883a97c26139e42df5e93eeece33d610b8a" integrity sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ== +typescript@^4.8.4: + version "4.8.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" + integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== + uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" @@ -16417,11 +16381,6 @@ yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yaml@^2.2.2: - version "2.3.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" - integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== - yargs-parser@20.2.4, yargs-parser@^20.2.2: version "20.2.4" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"