From 0b8c57e7aabe88a6c19d10e4b62c8b6948ab8b5d Mon Sep 17 00:00:00 2001 From: Shenoy Pratik Date: Tue, 19 Apr 2022 11:55:30 -0700 Subject: [PATCH 001/135] removing add sample data test (#668) Signed-off-by: Shenoy Pratik --- .../.cypress/integration/panels.spec.js | 33 ++----------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/dashboards-observability/.cypress/integration/panels.spec.js b/dashboards-observability/.cypress/integration/panels.spec.js index 730dd4040..e7b349633 100644 --- a/dashboards-observability/.cypress/integration/panels.spec.js +++ b/dashboards-observability/.cypress/integration/panels.spec.js @@ -502,37 +502,7 @@ describe('Testing a panel', () => { }); }); -describe('Add samples and clean up all test data', () => { - it('Add sample data', () => { - moveToPanelHome(); - cy.get('.euiButton__text').contains('Actions').trigger('mouseover').click(); - cy.wait(delay); - cy.get('.euiContextMenuItem__text').contains('Add samples').trigger('mouseover').click(); - cy.wait(delay * 3); - cy.get('.euiModalHeader__title[data-test-subj="confirmModalTitleText"]') - .contains('Add samples') - .should('exist'); - cy.wait(delay); - cy.get('.euiButton__text').contains('Yes').trigger('mouseover').click(); - cy.wait(delay * 5); - cy.route2('POST', '/addSamplePanels').as('removePage'); - cy.wait('@removePage').then(() => { - cy.get('.euiTableCellContent').contains(SAMPLE_PANEL).should('exist'); - }); - cy.wait(delay); - }); - - it('Validate sample data', () => { - moveToPanelHome(); - cy.get('.euiTableCellContent').contains(SAMPLE_PANEL).trigger('mouseover').click(); - cy.wait(delay * 3); - cy.get('h1').contains(SAMPLE_PANEL).should('exist'); - cy.wait(delay); - SAMPLE_VISUALIZATIONS_NAMES.forEach((vizName) => - cy.get('h5').contains(vizName).should('exist') - ); - cy.wait(delay); - }); +describe('Clean up all test data', () => { it('Delete visualizations from event analytics', () => { moveToEventsHome(); @@ -572,3 +542,4 @@ describe('Add samples and clean up all test data', () => { cy.get('.euiTextAlign').contains('No Operational Panels').should('exist'); }); }); + From aaeadb0e95979032a812d197691300d26f908eb0 Mon Sep 17 00:00:00 2001 From: Eugene Lee Date: Tue, 19 Apr 2022 13:30:33 -0700 Subject: [PATCH 002/135] Fix change availability bug (#667) Signed-off-by: Eugene Lee --- .../public/components/application_analytics/home.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dashboards-observability/public/components/application_analytics/home.tsx b/dashboards-observability/public/components/application_analytics/home.tsx index be405fa03..d3074da8b 100644 --- a/dashboards-observability/public/components/application_analytics/home.tsx +++ b/dashboards-observability/public/components/application_analytics/home.tsx @@ -200,7 +200,9 @@ export const Home = (props: HomeProps) => { .get(`${APP_ANALYTICS_API_PREFIX}/`) .then(async (res) => { // Want to calculate availability going down the table + const mainVisIdStore: Record = {}; for (let i = 0; i < res.data.length; i++) { + mainVisIdStore[res.data[i].id] = res.data[i].availability.mainVisId; res.data[i].availability = { name: 'loading', color: '', mainVisId: '' }; } setApplicationList(res.data); @@ -209,7 +211,7 @@ export const Home = (props: HomeProps) => { http, pplService, res.data[i], - res.data[i].availability.mainVisId, + mainVisIdStore[res.data[i].id], () => {} ); // Need to set state with new object to trigger re-render From 4ee5d2a41521752bba016d96f9e0c8167cd17fbe Mon Sep 17 00:00:00 2001 From: Eugene Lee Date: Tue, 19 Apr 2022 16:04:57 -0700 Subject: [PATCH 003/135] Fix test to check for empty event analytics (#669) Signed-off-by: Eugene Lee --- .../integration/app_analytics.spec.js | 48 ++++++++++--------- .../.cypress/utils/app_constants.js | 8 +--- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/dashboards-observability/.cypress/integration/app_analytics.spec.js b/dashboards-observability/.cypress/integration/app_analytics.spec.js index 61dcbbef7..80a66b210 100644 --- a/dashboards-observability/.cypress/integration/app_analytics.spec.js +++ b/dashboards-observability/.cypress/integration/app_analytics.spec.js @@ -22,6 +22,8 @@ import { trace_one, trace_two, trace_three, + query_one, + query_two, visOneName, visTwoName, composition, @@ -348,7 +350,7 @@ describe('Viewing application', () => { cy.get('.aa-List').find('.aa-Item').should('have.length', 11); cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus().type('x', {delay: TYPING_DELAY}); cy.focused().clear(); - cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus().type('where DestCityName = "Venice" | stats count() by span( timestamp , 6h )', {delay: TYPING_DELAY}); + cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus().type(query_one, {delay: TYPING_DELAY}); changeTimeTo24('months'); cy.wait(delay * 2); cy.get('.euiTab').contains('Visualizations').click(); @@ -412,7 +414,7 @@ describe('Viewing application', () => { cy.get('[data-test-subj="searchAutocompleteTextArea"]').clear(); cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus().type('x', {delay: TYPING_DELAY}); cy.focused().clear(); - cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus().type('where DestCityName = "Venice" | stats count() by span( timestamp , 6h )', {delay: TYPING_DELAY}); + cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus().type(query_two, {delay: TYPING_DELAY}); cy.get('.euiButton').contains('Refresh').click(); cy.wait(delay); cy.get('.euiTab').contains('Visualizations').click(); @@ -481,30 +483,30 @@ describe('Viewing application', () => { it('Hides application visualizations in Event Analytics', () => { cy.visit(`${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/event_analytics`); cy.wait(delay*3); - if (cy.get('.euiFieldSearch').length > 1) { + if (cy.get('.euiButton').length == 2) { cy.get('input.euiFieldSearch').type(visOneName, {delay: TYPING_DELAY}); - cy.wait(delay); - cy.get('.euiTableCellContent__text').contains('No items found').should('exist'); - cy.get('input.euiFieldSearch').clear().type(visTwoName, {delay: TYPING_DELAY}); - cy.wait(delay); - cy.get('.euiTableCellContent__text').contains('No items found').should('exist'); - cy.get('.euiFormControlLayoutClearButton').click(); - cy.wait(delay); - cy.get('[data-test-subj="tablePaginationPopoverButton"]').click(); - cy.get('.euiContextMenuItem__text').contains('50 rows').click(); - cy.get('.euiCheckbox__input[data-test-subj="checkboxSelectAll"]').click(); - cy.wait(delay); - cy.get('.euiButton__text').contains('Actions').click(); - cy.wait(delay); - cy.get('.euiContextMenuItem__text').contains('Delete').click(); - cy.wait(delay); + cy.wait(delay); + cy.get('.euiTableCellContent__text').contains('No items found').should('exist'); + cy.get('input.euiFieldSearch').clear().type(visTwoName, {delay: TYPING_DELAY}); + cy.wait(delay); + cy.get('.euiTableCellContent__text').contains('No items found').should('exist'); + cy.get('.euiFormControlLayoutClearButton').click(); + cy.wait(delay); + cy.get('[data-test-subj="tablePaginationPopoverButton"]').click(); + cy.get('.euiContextMenuItem__text').contains('50 rows').click(); + cy.get('.euiCheckbox__input[data-test-subj="checkboxSelectAll"]').click(); + cy.wait(delay); + cy.get('.euiButton__text').contains('Actions').click(); + cy.wait(delay); + cy.get('.euiContextMenuItem__text').contains('Delete').click(); + cy.wait(delay); - cy.get('button.euiButton--danger').should('be.disabled'); + cy.get('button.euiButton--danger').should('be.disabled'); - cy.get('input.euiFieldText[placeholder="delete"]').type('delete'); - cy.get('button.euiButton--danger').should('not.be.disabled'); - cy.get('.euiButton__text').contains('Delete').click(); - cy.wait(delay); + cy.get('input.euiFieldText[placeholder="delete"]').type('delete'); + cy.get('button.euiButton--danger').should('not.be.disabled'); + cy.get('.euiButton__text').contains('Delete').click(); + cy.wait(delay); } }); diff --git a/dashboards-observability/.cypress/utils/app_constants.js b/dashboards-observability/.cypress/utils/app_constants.js index bc3b7e856..86e713879 100644 --- a/dashboards-observability/.cypress/utils/app_constants.js +++ b/dashboards-observability/.cypress/utils/app_constants.js @@ -81,12 +81,8 @@ export const service_two = 'payment'; export const trace_one = 'HTTP POST'; export const trace_two = 'HTTP GET'; export const trace_three = 'client_pay_order'; -export const spanQueryOnePartOne = 'where DestCityName '; -export const spanQueryOnePartTwo = '= "Venice" | stats count() by span( timestamp '; -export const spanQueryOnePartThree = ', 6h )'; -export const spanQueryTwoPartOne = 'where OriginCityName '; -export const spanQueryTwoPartTwo = '= "Seoul" | stats count() by span( timestamp '; -export const spanQueryTwoPartThree = ', 6h )'; +export const query_one = 'where DestCityName = "Venice" | stats count() by span( timestamp , 6h )'; +export const query_two = 'where OriginCityName = "Seoul" | stats count() by span( timestamp , 6h )'; export const visOneName = 'Flights to Venice'; export const visTwoName = 'Flights from Seoul'; export const composition = 'order, payment, HTTP POST, HTTP GET, client_pay_order' From f0afa84dcb68be9ef096021da55de7b1c846ba93 Mon Sep 17 00:00:00 2001 From: Joshua Li Date: Wed, 20 Apr 2022 16:54:07 -0700 Subject: [PATCH 004/135] Add release notes for 2.0.0-rc1 (#674) Signed-off-by: Joshua Li --- ...observability.release-notes-2.0.0.0-rc1.md | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 release-notes/opensearch-observability.release-notes-2.0.0.0-rc1.md diff --git a/release-notes/opensearch-observability.release-notes-2.0.0.0-rc1.md b/release-notes/opensearch-observability.release-notes-2.0.0.0-rc1.md new file mode 100644 index 000000000..cbc88da32 --- /dev/null +++ b/release-notes/opensearch-observability.release-notes-2.0.0.0-rc1.md @@ -0,0 +1,51 @@ +## Version 2.0.0.0-rc1 Release Notes + +Compatible with OpenSearch 2.0.0.0-rc1 + +### Enhancements +* remove button toggle and add stop button ([#623](https://github.com/opensearch-project/observability/pull/623)) + +### Bug Fixes +* Edit visualization time change ([#617](https://github.com/opensearch-project/observability/pull/617)) +* Remove duplicated node dependencies ([#620](https://github.com/opensearch-project/observability/pull/620)) +* Bug fixes for application analytics ([#608](https://github.com/opensearch-project/observability/pull/608)) +* Fixes trace analytics invalid service map and increase span limit ([#629](https://github.com/opensearch-project/observability/pull/629)) +* Adding legacy UI route for traces ([#653](https://github.com/opensearch-project/observability/pull/653)) +* Fix change availability bug ([#667](https://github.com/opensearch-project/observability/pull/667)) +* Fix test to check for empty event analytics ([#669](https://github.com/opensearch-project/observability/pull/669)) +* Bump prismjs from 1.25.0 to 1.27.0 in /dashboards-observability ([#508](https://github.com/opensearch-project/observability/pull/508)) +* Bump minimist from 1.2.5 to 1.2.6 in /dashboards-observability ([#614](https://github.com/opensearch-project/observability/pull/614)) +* Bump moment from 2.29.1 to 2.29.2 in /dashboards-observability ([#636](https://github.com/opensearch-project/observability/pull/636)) +* Bump async from 3.2.1 to 3.2.3 in /dashboards-observability ([#654](https://github.com/opensearch-project/observability/pull/654)) + +### Infrastructure +* Bwc update ([#604](https://github.com/opensearch-project/observability/pull/604)) +* Event cypress tests ([#611](https://github.com/opensearch-project/observability/pull/611)) +* Test 2.0 ([#624](https://github.com/opensearch-project/observability/pull/624)) +* Updated panel flaky cypress tests ([#633](https://github.com/opensearch-project/observability/pull/633)) +* Updated notebook cypress tests ([#637](https://github.com/opensearch-project/observability/pull/637)) +* updated events flyout ui, unskip jest tests ([#638](https://github.com/opensearch-project/observability/pull/638)) +* Remove zips used by bwc tests ([#648](https://github.com/opensearch-project/observability/pull/648)) +* Fix trace analytics cypress ([#652](https://github.com/opensearch-project/observability/pull/652)) +* Event analytics jest tests ([#651](https://github.com/opensearch-project/observability/pull/651)) +* 2.0 cypress tests ([#658](https://github.com/opensearch-project/observability/pull/658)) +* Updated issue templates from .github. ([#662](https://github.com/opensearch-project/observability/pull/662)) +* Removing add sample data test from panels cypress ([#668](https://github.com/opensearch-project/observability/pull/668)) + +### Documentation +* Remove master and whitelist text ([#657](https://github.com/opensearch-project/observability/pull/657)) + +### Maintenance +* Bump plugins to 2.0 and support build.version_qualifier ([#602](https://github.com/opensearch-project/observability/pull/602)) +* Add alpha1 qualifier and JDK 17 for backend ([#607](https://github.com/opensearch-project/observability/pull/607)) +* Add alpha1 qualifiers for dashboards plugin ([#616](https://github.com/opensearch-project/observability/pull/616)) +* Tweak build.gradle to have the correct qualifiers in 2.0.0 ([#619](https://github.com/opensearch-project/observability/pull/619)) +* Change alpha1 to rc1 for first 2.0 release ([#635](https://github.com/opensearch-project/observability/pull/635)) +* Change 2.0-alpha1 to 2.0-rc1. ([#655](https://github.com/opensearch-project/observability/pull/655)) + +### Refactoring +* Modularize and cleanup traces ([#601](https://github.com/opensearch-project/observability/pull/601)) +* Modularize and cleanup panel ([#603](https://github.com/opensearch-project/observability/pull/603)) +* Modularize event Analytics live tail and fix bug ([#647](https://github.com/opensearch-project/observability/pull/647)) +* Fix lint and modularize dashboard ([#583](https://github.com/opensearch-project/observability/pull/583)) +* Modularize service and fix issues ([#595](https://github.com/opensearch-project/observability/pull/595)) \ No newline at end of file From 79613cdd4289bd7ed65a5136cbef3b20708ce9c8 Mon Sep 17 00:00:00 2001 From: Mrunal Zambre <79525611+mrunal-z@users.noreply.github.com> Date: Wed, 27 Apr 2022 23:15:10 +0530 Subject: [PATCH 005/135] remove candlestick from visualizations (#690) Signed-off-by: Mrunal Zambre --- .../financial/candle_stick/candle_stick.tsx | 217 ------------------ .../candle_stick/candle_stick_type.ts | 81 ------- 2 files changed, 298 deletions(-) delete mode 100644 dashboards-observability/public/components/visualizations/charts/financial/candle_stick/candle_stick.tsx delete mode 100644 dashboards-observability/public/components/visualizations/charts/financial/candle_stick/candle_stick_type.ts diff --git a/dashboards-observability/public/components/visualizations/charts/financial/candle_stick/candle_stick.tsx b/dashboards-observability/public/components/visualizations/charts/financial/candle_stick/candle_stick.tsx deleted file mode 100644 index 150b93ab5..000000000 --- a/dashboards-observability/public/components/visualizations/charts/financial/candle_stick/candle_stick.tsx +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React from 'react'; - -import { Plt } from '../../../plotly/plot'; - -export const CandleStick = ({ visualizations, layout, config }) => { - const trace1 = { - x: [ - '2017-01-04', - '2017-01-05', - '2017-01-06', - '2017-01-09', - '2017-01-10', - '2017-01-11', - '2017-01-12', - '2017-01-13', - '2017-01-17', - '2017-01-18', - '2017-01-19', - '2017-01-20', - '2017-01-23', - '2017-01-24', - '2017-01-25', - '2017-01-26', - '2017-01-27', - '2017-01-30', - '2017-01-31', - '2017-02-01', - '2017-02-02', - '2017-02-03', - '2017-02-06', - '2017-02-07', - '2017-02-08', - '2017-02-09', - '2017-02-10', - '2017-02-13', - '2017-02-14', - '2017-02-15', - ], - - close: [ - 116.019997, - 116.610001, - 117.910004, - 118.989998, - 119.110001, - 119.75, - 119.25, - 119.040001, - 120, - 119.989998, - 119.779999, - 120, - 120.080002, - 119.970001, - 121.879997, - 121.940002, - 121.949997, - 121.629997, - 121.349998, - 128.75, - 128.529999, - 129.080002, - 130.289993, - 131.529999, - 132.039993, - 132.419998, - 132.119995, - 133.289993, - 135.020004, - 135.509995, - ], - - decreasing: { line: { color: '#7F7F7F' } }, - - high: [ - 116.510002, - 116.860001, - 118.160004, - 119.43, - 119.379997, - 119.93, - 119.300003, - 119.620003, - 120.239998, - 120.5, - 120.089996, - 120.449997, - 120.809998, - 120.099998, - 122.099998, - 122.440002, - 122.349998, - 121.629997, - 121.389999, - 130.490005, - 129.389999, - 129.190002, - 130.5, - 132.089996, - 132.220001, - 132.449997, - 132.940002, - 133.820007, - 135.089996, - 136.270004, - ], - - increasing: { line: { color: '#17BECF' } }, - - line: { color: 'rgba(31,119,180,1)' }, - - low: [ - 115.75, - 115.809998, - 116.470001, - 117.940002, - 118.300003, - 118.599998, - 118.209999, - 118.809998, - 118.220001, - 119.709999, - 119.370003, - 119.730003, - 119.769997, - 119.5, - 120.279999, - 121.599998, - 121.599998, - 120.660004, - 120.620003, - 127.010002, - 127.779999, - 128.160004, - 128.899994, - 130.449997, - 131.220001, - 131.119995, - 132.050003, - 132.75, - 133.25, - 134.619995, - ], - - open: [ - 115.849998, - 115.919998, - 116.779999, - 117.949997, - 118.769997, - 118.739998, - 118.900002, - 119.110001, - 118.339996, - 120, - 119.400002, - 120.449997, - 120, - 119.550003, - 120.419998, - 121.669998, - 122.139999, - 120.93, - 121.150002, - 127.029999, - 127.980003, - 128.309998, - 129.130005, - 130.539993, - 131.350006, - 131.649994, - 132.460007, - 133.080002, - 133.470001, - 135.520004, - ], - - type: 'candlestick', - xaxis: 'x', - yaxis: 'y', - }; - - const candleStickData = [trace1]; - - const finalLayout = { - dragmode: 'zoom', - margin: { - r: 10, - t: 25, - b: 40, - l: 60, - }, - showlegend: false, - xaxis: { - autorange: true, - domain: [0, 1], - range: ['2017-01-03 12:00', '2017-02-15 12:00'], - rangeslider: { range: ['2017-01-03 12:00', '2017-02-15 12:00'] }, - title: 'Date', - type: 'date', - }, - yaxis: { - autorange: true, - domain: [0, 1], - range: [114.609999778, 137.410004222], - type: 'linear', - }, - ...layout, - }; - - return ; -}; diff --git a/dashboards-observability/public/components/visualizations/charts/financial/candle_stick/candle_stick_type.ts b/dashboards-observability/public/components/visualizations/charts/financial/candle_stick/candle_stick_type.ts deleted file mode 100644 index df9dd2fa9..000000000 --- a/dashboards-observability/public/components/visualizations/charts/financial/candle_stick/candle_stick_type.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { CandleStick } from './candle_stick'; -import { getPlotlySharedConfigs, getPlotlyCategory } from '../../shared/shared_configs'; -import { LensIconChartBar } from '../../../assets/chart_bar'; -import { VizDataPanel } from '../../../../event_analytics/explorer/visualizations/config_panel/config_panes/default_vis_editor'; -import { ConfigEditor } from '../../../../event_analytics/explorer/visualizations/config_panel/config_panes/json_editor'; -import { ConfigValueOptions } from '../../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls'; - -const sharedConfigs = getPlotlySharedConfigs(); -const VIS_CATEGORY = getPlotlyCategory(); - -export interface BarTypeParams {} - -export const createCandleStickDefinition = (params: BarTypeParams = {}) => ({ - name: 'candle_stick', - type: 'candle_stick', - id: 'candle_stick', - label: 'Candle Stick', - fullLabel: 'Candle Stick', - selection: { - dataLoss: 'nothing', - }, - category: VIS_CATEGORY.BASICS, - icon: LensIconChartBar, - categoryAxis: 'xaxis', - seriesAxis: 'yaxis', - orientation: 'v', - component: CandleStick, - editorConfig: { - panelTabs: [ - { - id: 'data-panel', - name: 'Data', - mapTo: 'dataConfig', - editor: VizDataPanel, - sections: [ - { - id: 'value_options', - name: 'Value options', - editor: ConfigValueOptions, - mapTo: 'valueOptions', - schemas: [ - { - name: 'X-axis', - isSingleSelection: true, - component: null, - mapTo: 'xaxis', - }, - { - name: 'Y-axis', - isSingleSelection: false, - component: null, - mapTo: 'yaxis', - }, - ], - }, - ], - }, - { - id: 'style-panel', - name: 'Layout', - mapTo: 'layoutConfig', - editor: ConfigEditor, - content: [], - }, - ], - }, - visConfig: { - layout: { - ...sharedConfigs.layout, - }, - config: { - ...sharedConfigs.config, - }, - isUniColor: false, - }, -}); From f3b65982657db93a0feeca6e65a5aa79ab7bfdf6 Mon Sep 17 00:00:00 2001 From: Kawika Avilla Date: Thu, 28 Apr 2022 10:37:23 -0700 Subject: [PATCH 006/135] [OSD][Tests] add test subject to app title for app analytics (#686) * [OSD][Tests] add test subject to app title for app analytics Using a test subject we can find the specific element instead of trying to search the DOM for the class and hope the class is the right class that contains the element we are looking for. We can search the dom for this specific test subject and actually make the test runner wait until this test subject exists before executing the tests. Issue related: https://github.com/opensearch-project/observability/issues/679 Signed-off-by: Kawika Avilla * update one example for cypress tests Signed-off-by: Kawika Avilla --- .../.cypress/integration/app_analytics.spec.js | 2 +- .../components/application_analytics/components/application.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dashboards-observability/.cypress/integration/app_analytics.spec.js b/dashboards-observability/.cypress/integration/app_analytics.spec.js index 80a66b210..58a49c34d 100644 --- a/dashboards-observability/.cypress/integration/app_analytics.spec.js +++ b/dashboards-observability/.cypress/integration/app_analytics.spec.js @@ -205,7 +205,7 @@ describe('Viewing application', () => { it('Has working breadcrumbs', () => { cy.get('.euiBreadcrumb').contains('Cypress').click(); cy.wait(delay); - cy.get('.euiTitle').contains(nameOne).should('exist'); + cy.get('[data-test-subj="appAnalyticsAppName"]').contains(nameOne).should('exist'); cy.get('.euiBreadcrumb').contains('Application analytics').click(); cy.wait(delay); cy.get('.euiTitle').contains('Applications').should('exist'); diff --git a/dashboards-observability/public/components/application_analytics/components/application.tsx b/dashboards-observability/public/components/application_analytics/components/application.tsx index 08ab10f22..a07221167 100644 --- a/dashboards-observability/public/components/application_analytics/components/application.tsx +++ b/dashboards-observability/public/components/application_analytics/components/application.tsx @@ -503,7 +503,7 @@ export function Application(props: AppDetailProps) { -

{application.name}

+

{application.name}

{application.description}

From 9ebb7d266250fd902567f9f804c425d5a4f9774f Mon Sep 17 00:00:00 2001 From: Joshua Li Date: Mon, 2 May 2022 09:31:13 -0700 Subject: [PATCH 007/135] Support integTestRemote with security enabled endpoint (#699) Signed-off-by: Joshua Li --- .../observability/PluginRestTestCase.kt | 105 ++++++++---------- .../src/test/resources/security/sample.pem | 28 +++++ .../src/test/resources/security/test-kirk.jks | Bin 0 -> 3874 bytes 3 files changed, 72 insertions(+), 61 deletions(-) create mode 100644 opensearch-observability/src/test/resources/security/sample.pem create mode 100644 opensearch-observability/src/test/resources/security/test-kirk.jks diff --git a/opensearch-observability/src/test/kotlin/org/opensearch/observability/PluginRestTestCase.kt b/opensearch-observability/src/test/kotlin/org/opensearch/observability/PluginRestTestCase.kt index 33afa6e80..759674eef 100644 --- a/opensearch-observability/src/test/kotlin/org/opensearch/observability/PluginRestTestCase.kt +++ b/opensearch-observability/src/test/kotlin/org/opensearch/observability/PluginRestTestCase.kt @@ -6,17 +6,7 @@ package org.opensearch.observability import com.google.gson.JsonObject -import org.apache.http.Header import org.apache.http.HttpHost -import org.apache.http.auth.AuthScope -import org.apache.http.auth.UsernamePasswordCredentials -import org.apache.http.client.CredentialsProvider -import org.apache.http.client.config.RequestConfig -import org.apache.http.conn.ssl.NoopHostnameVerifier -import org.apache.http.impl.client.BasicCredentialsProvider -import org.apache.http.impl.nio.client.HttpAsyncClientBuilder -import org.apache.http.message.BasicHeader -import org.apache.http.ssl.SSLContextBuilder import org.junit.After import org.junit.AfterClass import org.junit.Before @@ -25,13 +15,14 @@ import org.opensearch.client.RequestOptions import org.opensearch.client.Response import org.opensearch.client.ResponseException import org.opensearch.client.RestClient -import org.opensearch.client.RestClientBuilder +import org.opensearch.client.WarningsHandler +import org.opensearch.common.io.PathUtils import org.opensearch.common.settings.Settings -import org.opensearch.common.unit.TimeValue -import org.opensearch.common.util.concurrent.ThreadContext import org.opensearch.common.xcontent.DeprecationHandler import org.opensearch.common.xcontent.NamedXContentRegistry import org.opensearch.common.xcontent.XContentType +import org.opensearch.commons.ConfigConstants +import org.opensearch.commons.rest.SecureRestClientBuilder import org.opensearch.test.rest.OpenSearchRestTestCase import java.io.BufferedReader import java.io.IOException @@ -39,7 +30,6 @@ import java.io.InputStreamReader import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.Path -import java.security.cert.X509Certificate import javax.management.MBeanServerInvocationHandler import javax.management.ObjectName import javax.management.remote.JMXConnectorFactory @@ -79,63 +69,56 @@ abstract class PluginRestTestCase : OpenSearchRestTestCase() { val jsonObject: Map<*, *> = index as java.util.HashMap<*, *> val indexName: String = jsonObject["index"] as String // .opendistro_security isn't allowed to delete from cluster - if (indexName != ".opendistro_security") { - client().performRequest(Request("DELETE", "/$indexName")) + if (".opendistro_security" != indexName) { + val request = Request("DELETE", "/$indexName") + // TODO: remove PERMISSIVE option after moving system index access to REST API call + val options = RequestOptions.DEFAULT.toBuilder() + options.setWarningsHandler(WarningsHandler.PERMISSIVE) + request.options = options.build() + adminClient().performRequest(request) } } } } + /** + * Returns the REST client settings used for super-admin actions like cleaning up after the test has completed. + */ + override fun restAdminSettings(): Settings { + return Settings + .builder() + .put("http.port", 9200) + .put(ConfigConstants.OPENSEARCH_SECURITY_SSL_HTTP_ENABLED, isHttps()) + .put(ConfigConstants.OPENSEARCH_SECURITY_SSL_HTTP_PEMCERT_FILEPATH, "sample.pem") + .put(ConfigConstants.OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_FILEPATH, "test-kirk.jks") + .put(ConfigConstants.OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_PASSWORD, "changeit") + .put(ConfigConstants.OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_KEYPASSWORD, "changeit") + .build() + } + @Throws(IOException::class) override fun buildClient(settings: Settings, hosts: Array): RestClient { - val builder = RestClient.builder(*hosts) if (isHttps()) { - configureHttpsClient(builder, settings) + val keystore = settings.get(ConfigConstants.OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_FILEPATH) + return when (keystore != null) { + true -> { + // create adminDN (super-admin) client + val uri = javaClass.classLoader.getResource("security/sample.pem").toURI() + val configPath = PathUtils.get(uri).parent.toAbsolutePath() + SecureRestClientBuilder(settings, configPath).setSocketTimeout(60000).build() + } + false -> { + // create client with passed user + val userName = System.getProperty("user") + val password = System.getProperty("password") + SecureRestClientBuilder(hosts, isHttps(), userName, password).setSocketTimeout(60000).build() + } + } } else { + val builder = RestClient.builder(*hosts) configureClient(builder, settings) - } - builder.setStrictDeprecationMode(true) - return builder.build() - } - - @Throws(IOException::class) - protected open fun configureHttpsClient(builder: RestClientBuilder, settings: Settings) { - val headers = ThreadContext.buildDefaultHeaders(settings) - val defaultHeaders = arrayOfNulls
(headers.size) - var i = 0 - for ((key, value) in headers) { - defaultHeaders[i++] = BasicHeader(key, value) - } - builder.setDefaultHeaders(defaultHeaders) - builder.setHttpClientConfigCallback { httpClientBuilder: HttpAsyncClientBuilder -> - val userName = System.getProperty("user") - val password = System.getProperty("password") - val credentialsProvider: CredentialsProvider = BasicCredentialsProvider() - credentialsProvider.setCredentials(AuthScope.ANY, UsernamePasswordCredentials(userName, password)) - try { - return@setHttpClientConfigCallback httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider) - // disable the certificate since our testing cluster just uses the default security configuration - .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) - .setSSLContext( - SSLContextBuilder.create() - .loadTrustMaterial(null) { _: Array?, _: String? -> true } - .build() - ) - } catch (e: Exception) { - throw RuntimeException(e) - } - } - val socketTimeoutString = settings[CLIENT_SOCKET_TIMEOUT] - val socketTimeout = TimeValue.parseTimeValue(socketTimeoutString ?: "60s", CLIENT_SOCKET_TIMEOUT) - builder.setRequestConfigCallback { conf: RequestConfig.Builder -> - conf.setSocketTimeout( - Math.toIntExact( - socketTimeout.millis - ) - ) - } - if (settings.hasValue(CLIENT_PATH_PREFIX)) { - builder.setPathPrefix(settings[CLIENT_PATH_PREFIX]) + builder.setStrictDeprecationMode(true) + return builder.build() } } diff --git a/opensearch-observability/src/test/resources/security/sample.pem b/opensearch-observability/src/test/resources/security/sample.pem new file mode 100644 index 000000000..fa785ca10 --- /dev/null +++ b/opensearch-observability/src/test/resources/security/sample.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIEyTCCA7GgAwIBAgIGAWLrc1O2MA0GCSqGSIb3DQEBCwUAMIGPMRMwEQYKCZIm +iZPyLGQBGRYDY29tMRcwFQYKCZImiZPyLGQBGRYHZXhhbXBsZTEZMBcGA1UECgwQ +RXhhbXBsZSBDb20gSW5jLjEhMB8GA1UECwwYRXhhbXBsZSBDb20gSW5jLiBSb290 +IENBMSEwHwYDVQQDDBhFeGFtcGxlIENvbSBJbmMuIFJvb3QgQ0EwHhcNMTgwNDIy +MDM0MzQ3WhcNMjgwNDE5MDM0MzQ3WjBeMRIwEAYKCZImiZPyLGQBGRYCZGUxDTAL +BgNVBAcMBHRlc3QxDTALBgNVBAoMBG5vZGUxDTALBgNVBAsMBG5vZGUxGzAZBgNV +BAMMEm5vZGUtMC5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAJa+f476vLB+AwK53biYByUwN+40D8jMIovGXm6wgT8+9Sbs899dDXgt +9CE1Beo65oP1+JUz4c7UHMrCY3ePiDt4cidHVzEQ2g0YoVrQWv0RedS/yx/DKhs8 +Pw1O715oftP53p/2ijD5DifFv1eKfkhFH+lwny/vMSNxellpl6NxJTiJVnQ9HYOL +gf2t971ITJHnAuuxUF48HcuNovW4rhtkXef8kaAN7cE3LU+A9T474ULNCKkEFPIl +ZAKN3iJNFdVsxrTU+CUBHzk73Do1cCkEvJZ0ZFjp0Z3y8wLY/gqWGfGVyA9l2CUq +eIZNf55PNPtGzOrvvONiui48vBKH1LsCAwEAAaOCAVkwggFVMIG8BgNVHSMEgbQw +gbGAFJI1DOAPHitF9k0583tfouYSl0BzoYGVpIGSMIGPMRMwEQYKCZImiZPyLGQB +GRYDY29tMRcwFQYKCZImiZPyLGQBGRYHZXhhbXBsZTEZMBcGA1UECgwQRXhhbXBs +ZSBDb20gSW5jLjEhMB8GA1UECwwYRXhhbXBsZSBDb20gSW5jLiBSb290IENBMSEw +HwYDVQQDDBhFeGFtcGxlIENvbSBJbmMuIFJvb3QgQ0GCAQEwHQYDVR0OBBYEFKyv +78ZmFjVKM9g7pMConYH7FVBHMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgXg +MCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjA1BgNVHREELjAsiAUq +AwQFBYISbm9kZS0wLmV4YW1wbGUuY29tgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZI +hvcNAQELBQADggEBAIOKuyXsFfGv1hI/Lkpd/73QNqjqJdxQclX57GOMWNbOM5H0 +5/9AOIZ5JQsWULNKN77aHjLRr4owq2jGbpc/Z6kAd+eiatkcpnbtbGrhKpOtoEZy +8KuslwkeixpzLDNISSbkeLpXz4xJI1ETMN/VG8ZZP1bjzlHziHHDu0JNZ6TnNzKr +XzCGMCohFfem8vnKNnKUneMQMvXd3rzUaAgvtf7Hc2LTBlf4fZzZF1EkwdSXhaMA +1lkfHiqOBxtgeDLxCHESZ2fqgVqsWX+t3qHQfivcPW6txtDyrFPRdJOGhiMGzT/t +e/9kkAtQRgpTb3skYdIOOUOV0WGQ60kJlFhAzIs= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/opensearch-observability/src/test/resources/security/test-kirk.jks b/opensearch-observability/src/test/resources/security/test-kirk.jks new file mode 100644 index 0000000000000000000000000000000000000000..174dbda656f41b10341adb78ab91a46afaae8a1c GIT binary patch literal 3874 zcmY+GcQhM}zs8e@RFtSn>{Y9_XzfvZl*TSQG6F9FNd(wu zFab99cRY+FKt2CO5E21u`*&mo0s{VisDB9%$qk|Z?*;}S1PKGv1*1XCugHHEKp;B6 z69Saqd|bch;ZdXcj@o48Or^T{VjiQWQ)um?koax&EW2Jd6%cmO+99&?<0M#TkhMY0 z>TOc9NNj$5o%GwnI2>ZpA<-syd;YVlrkqVstJxqe_w8#F0dlKW!#D3WVDWfwaN@uX z{)l!>hgv`=r)M_tPedAH8wS zrMCsCM3^vbf3iWkdUoK)O(h9`bxp3s^zq4CU5%IJN;Y04OLiLfXPS%;Duo}L?EKtE z$4DyO?uRf+Ovm@OBmMKYjcI;;3k(jA`wJ`_W&){Es6Nv(A-s;NYZhfPTZJ%tBZ{1@ zc|_(P(o|Du6c{sJ4@Q6w- zF)*aVb&dDqmGoH8(8Y;T2S?DR9+P|nUT>q8177|so}DjY7IWc!jB(9r?rJ%YyVvh5 z4`BJLeFX6F2g1N^WT?dWin3^|1>$*MQP~CSqFMgQ4m&bJp``1>I(!5Pe9&NB7{wXc z+p)Bs6Durb104tWmIOYRkBU~Waz;l#k`+@Fye00vbTIQq3dY*R{KBH-UF3%r{=+v` zqu(DD1~xv;*N0vqhN9l+bCm(5u37KF+&JF&or0qB&J%}ZmdviHekDmr#GlPK60J4Q zJ#vSZYt1pSxEPM~S27`bL-X}ig&?t1ubwy1&P?lEwQUs|t?a7>dqM7^&@^5tSL9pMp+&5H?jk>BGMj!JcQ+3*rxFcY4MY2z z4C?1*^xq&(g`+u7JnXS-Yuq8?$%DG-Zs#VDo=cTmcJRfEFTG1T4~(u1j$Snc+7Cs; zyB9?mE4rqbq_*xqj?#OlN%@YGt*PgH+-~Fy+blur5jn zu_S?>vGKl_57zp6>#CW5Q&HHKl|qVToNrM`8!zz5n*{CQ+r2#n4{2tk@;0m{ zM8pbY25rVQv1<0iw2CPT?uG+>NVZVLalVoRSZQdC(&M@`0$mC@6l?zxF&LAM8XHR1Ah3S zb?4&7@N$w<+PVC^0ws=h2pqrozQ!=b!?Zy2@uQjFh1)BEPT$JlDa9Q8(%YHT_r)w# z<4bW`j)gX^ktonho#Uf=U=ZH5QT!;ug%qe!Fi?N(OjphEVY3YTU5B*j^ZMOg+XmnL zPpT%`zoHjGCw~=w|5zC`KWOFwsF`=Jjwez^hwA2rgTt^ z^10Gp<3*%@mI37QZ>P3$*PX4;4LpFQqK9AnvMxAg!|B)unEQ{13w`0LO;;mgV22L5 z=Y8bwo8Fch2UFgZEqeTdMGZMKmz)4Uzb#-R)&H4zUC45?<4&g?`6XX-=`F2|(~Esf z4P+-+Y;J{*hV8L55?o`K^wL+ zE>e|WH7ZW48)vi%Zq4nbkLikeTd&2pCr5A#jJC9jypS>*@uF<#i}Xp$3X7~b0>bXQ zd@CV7FY-$A{IR_m5uZie z+ckdOpNC4bjck=wZ@3lTl5+`W3~_4oPuGx4#mk-f?CsbGulgu|BAb)LTI|hBYM==Q zPLdu6@x)I_O{qq^{%cI*Q`-C+WZjpp^GjGiWv(#7Vr(pZ@A532u&Rn|3@4+xgKqNc zMhtgDOn)7lv}KZc^U}jD!KU{3;=7as(>uBwDx5}ii8iIz!F(WDlbe(V`WH5PS-XhZ zPJFI;eV}4{aJ?&?Sv%?zMZJ9SRFL%?ZZ0C(FdozY2R@i=1>&&E< z<(hauSRE!6;QE6ujbYrYrWNm9;!ixJV`}*=J$7wZ^0l>rTb7|)`olK^*^m3Ex%nq2 zL({r^1)T=Q7qM>-F~1lC817t!PNhq1c&?{#kiAuiMtlDELuI?Ut6LMQ6()675@U5L z_g(P7&7MR-N3z!C5a+qZ$!xmrg0qbsQn*7vqc!v-^yqc6`tlc%aQl-Fe+IYP5Pe^K z^%zx2w*a+^&+F*;<~HZ&=XwRTB6z)Uec2XkH=^cl)cHs|VxGqSQStks&td*NQbTPW z@??ewN#dRVCH?t{p-$)JDIxkVF$#9Q?iS!Qqby9p zttQuw3k2_4Hs9`5TG}3Jwk97Nste6#I!jG)f$b(~xI#)Bs7nQ7es#6RzYPh=8vCY$@K;aE z0JYYxSm&6)?GS&eI-ibs8vhi$EXK)Yhv7%bHy2C$czjfz?F4J+b%lJkXj+1&h?Ti_R;#D>}h%qh-ltN3^kJE=J$q9lGN z97&*c`aeQNBG8(G3ADz4#|D3&4&?Ix=oLK>L?VDUkp%Gi|FbTdf2<2!*X4kUenR(; zb%6=se)ca%eZ zOyn3`1eb66NoONNlb!Qgq|BuMxwULjnW>4u2iuhj(ZUV8fC!eY=nsZF*}w6V0(LxJ zVJ|ew^cV0%UizR_Y1yOEtM1}iw*f#fPAX(#E)%*G)QD7W7O$XT5e!*pv0krMED!yw zv)_h?54B@8<=GZ6ukEmkmrx<@jaUud2Y%EQU-vBcCChZ&9Xf`1Rw3w4G=@{y>I<<5 zr)BfiiXe`(Z@ksE4@BqB5d!$>pA(N&9b7XX5GBfr?j{H(J6=OSr*~9Ff8Zh0^d;HS3|V9O<+-Py zxI&YAI-gM^t2+X1O6JyQ*^8SfuZ5{?m1F14fGg;0aeF|P)4c8tw{C;?*J)`bjV2~qOsSjk^$@gQ1{3jw}OGfYhan!3#Y zHIQX-5|4fmT69zTvDd3aW(AkQqj4t}?Md}bd>>Q>N!29V@klLOr#L%^gPrlgw8ASS>!fstf*6i;ka?xLu@MUq>?r_mf*HCZ0jHy2N^B`x>Y90Tt5-jn7*G)Ai~?r^6!i zChFK}Z-Np|s#K(ct1NYcNSoxM%p~ng6bf7}uXm#_v&(wHHp4Tljgd6EW$Kg0xZkkr zi&o;({o`MC#=#JXFx-Py14vyFMbGypX`-a>1F9n21b`MXKk|zU$zEO&>l1Rjkx$4Vg-UeUetqM3xCVt2 z#4}QY$t__sQxkuq9U8E_JbjM8#9JvlSK48A@`?q^I*~JnT-!@f$l49YlT>fpGqYJ9 zr+k*tw-oT8l~Dr<$GT8lt$6D+{n7Af1%CX7h0*}>N)s;I);DZqq{57a Date: Tue, 3 May 2022 11:23:37 -0700 Subject: [PATCH 008/135] Add data test subj to app analytics (#704) Signed-off-by: Eugene Lee --- .../integration/app_analytics.spec.js | 457 ++++++++---------- .../.cypress/utils/app_constants.js | 16 +- .../common/constants/application_analytics.ts | 12 +- .../__snapshots__/create.test.tsx.snap | 272 +++++++++-- .../__snapshots__/log_config.test.tsx.snap | 12 + .../service_config.test.tsx.snap | 42 +- .../__snapshots__/trace_config.test.tsx.snap | 42 +- .../components/app_table.tsx | 28 +- .../components/application.tsx | 22 +- .../config_components/log_config.tsx | 2 + .../config_components/service_config.tsx | 7 +- .../config_components/trace_config.tsx | 5 +- .../components/configuration.tsx | 3 +- .../components/create.tsx | 13 +- .../service_detail_flyout.tsx | 4 +- .../flyout_components/trace_detail_flyout.tsx | 4 +- .../application_analytics/helpers/utils.tsx | 1 + .../custom_panel_table.test.tsx.snap | 13 + .../custom_panel_view.test.tsx.snap | 20 + .../custom_panels/custom_panel_table.tsx | 10 +- .../custom_panels/custom_panel_view.tsx | 7 +- .../custom_input_model.test.tsx.snap | 4 + .../__snapshots__/utils.test.tsx.snap | 8 + .../helpers/add_visualization_popover.tsx | 1 + .../helpers/custom_input_modal.tsx | 15 +- .../helpers/modal_containers.tsx | 2 +- .../__snapshots__/empty_panel.test.tsx.snap | 12 + .../panel_modules/empty_panel.tsx | 2 +- .../visualization_container.test.tsx.snap | 2 + .../visualization_container.tsx | 9 +- .../event_analytics/explorer/explorer.tsx | 2 +- .../config_panel/config_panel.tsx | 6 +- .../config_controls/config_thresholds.tsx | 7 +- .../count_distribution.test.tsx.snap | 2 + .../components/event_analytics/home/home.tsx | 2 +- .../components/common/filters/filters.tsx | 4 +- .../service_map_scale.test.tsx.snap | 2 + .../dashboard_table.test.tsx.snap | 5 + .../components/dashboard/dashboard_table.tsx | 1 + .../flyout_list_item.test.tsx.snap | 1 + .../service_breakdown_panel.test.tsx.snap | 2 + .../span_detail_flyout.test.tsx.snap | 7 + .../span_detail_panel.test.tsx.snap | 2 + .../components/traces/flyout_list_item.tsx | 12 +- .../components/traces/span_detail_flyout.tsx | 8 +- .../__tests__/__snapshots__/bar.test.tsx.snap | 2 + .../__snapshots__/line.test.tsx.snap | 2 + .../__tests__/__snapshots__/pie.test.tsx.snap | 2 + .../__snapshots__/plotly.test.tsx.snap | 2 + .../components/visualizations/plotly/plot.tsx | 1 + 50 files changed, 755 insertions(+), 364 deletions(-) diff --git a/dashboards-observability/.cypress/integration/app_analytics.spec.js b/dashboards-observability/.cypress/integration/app_analytics.spec.js index 58a49c34d..c98096fcf 100644 --- a/dashboards-observability/.cypress/integration/app_analytics.spec.js +++ b/dashboards-observability/.cypress/integration/app_analytics.spec.js @@ -41,28 +41,25 @@ describe('Creating application', () => { expectMessageOnHover('Name is required.'); cy.get('[data-test-subj="nameFormRow"]').type(nameOne); expectMessageOnHover('Provide at least one log source, service, entity or trace group.'); - cy.get('.euiAccordion').contains('Log source').trigger('mouseover').click(); - cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus().type(baseQuery, {delay: TYPING_DELAY}); - cy.get('.euiButton').contains('Create').should('not.be.disabled'); - cy.get('[data-test-subj="searchAutocompleteTextArea"]').clear(); - expectMessageOnHover('Provide at least one log source, service, entity or trace group.'); - cy.get('.euiAccordion').contains('Services & entities').trigger('mouseover').click();; + cy.get('[data-test-subj="servicesEntitiesAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="servicesEntitiesComboBox"]').trigger('mouseover').click(); cy.focused().type('{downArrow}'); cy.focused().type('{enter}'); - cy.get('.euiBadge').contains('1').should('exist'); - cy.get('.euiButton').contains('Create').should('not.be.disabled'); + cy.get('[data-test-subj="servicesEntitiesCountBadge"]').should('contain', '1'); + cy.get('[data-test-subj="createButton"]').should('not.be.disabled'); + cy.get('[data-test-subj="logSourceAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus().type(baseQuery, {delay: TYPING_DELAY}); - cy.get('.euiAccordion').contains('Trace groups').trigger('mouseover').click(); + cy.get('[data-test-subj="traceGroupsAccordion"]').scrollIntoView(); + cy.get('[data-test-subj="traceGroupsAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="traceGroupsComboBox"]').scrollIntoView().type('http'); - cy.get('.euiFilterSelectItem').contains(trace_one).trigger('click') - cy.get('.euiFilterSelectItem').contains(trace_two).trigger('click'); - cy.get('.euiBadge').contains('2').should('exist'); - cy.get('.euiButton').contains('Create').should('not.be.disabled'); + cy.get('.euiFilterSelectItem').contains(trace_one).click({ force: true }); + cy.get('.euiFilterSelectItem').contains(trace_two).click({ force: true }); + cy.get('[data-test-subj="traceGroupsCountBadge"]').should('contain', '2'); + cy.get('[data-test-subj="createButton"]').should('not.be.disabled'); }); it('Suggests correct autocompletion', () => { - cy.get('.euiAccordion').contains('Log source').trigger('mouseover').click(); + cy.get('[data-test-subj="logSourceAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="searchAutocompleteTextArea"]').click(); cy.get('.aa-List').find('.aa-Item').should('have.length', 1); cy.get('.aa-Item').contains('source').should('exist'); @@ -95,105 +92,98 @@ describe('Creating application', () => { it('Creates an application and redirects to application', () => { cy.get('[data-test-subj="nameFormRow"]').type(nameOne); cy.get('[data-test-subj="descriptionFormRow"]').type('This application is for testing.'); - cy.get('.euiAccordion').contains('Log source').trigger('mouseover').click(); - cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus().type(baseQuery, {delay: TYPING_DELAY}); - cy.get('.euiAccordion').contains('Services & entities').trigger('mouseover').click(); + cy.get('[data-test-subj="servicesEntitiesAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="servicesEntitiesComboBox"]').click(); cy.focused().type('{downArrow}'); cy.focused().type('{enter}'); - cy.get('.euiBadge').contains('1').should('exist'); - cy.get('.euiAccordion').contains('Trace groups').trigger('mouseover').click(); + cy.get('[data-test-subj="servicesEntitiesCountBadge"]').should('contain', '1'); + cy.get('[data-test-subj="logSourceAccordion"]').trigger('mouseover').click(); + cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus().type(baseQuery, {delay: TYPING_DELAY}); + cy.get('[data-test-subj="traceGroupsAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="traceGroupsComboBox"]').scrollIntoView().type('http'); cy.get('.euiFilterSelectItem').contains(trace_one).trigger('click'); cy.get('.euiFilterSelectItem').contains(trace_two).trigger('click'); - cy.get('.euiBadge').contains('2').should('exist'); - cy.get('.euiButton').contains('Create').should('not.be.disabled'); - cy.get('.euiButton').contains('Create').click(); - cy.wait(delay*3); - cy.get('.euiTitle').contains(nameOne).should('exist'); - cy.get('.euiTab').contains('Panel').click(); - cy.get('.euiText').contains('Start by adding your first visualization').should('exist'); + cy.get('[data-test-subj="traceGroupsCountBadge"]').should('contain', '2'); + cy.get('[data-test-subj="createButton"]').should('not.be.disabled'); + cy.get('[data-test-subj="createButton"]').click(); + cy.wait(delay * 3); + cy.get('[data-test-subj="applicationTitle"]').should('contain', nameOne); + cy.get('[data-test-subj="app-analytics-panelTab"]').click(); + cy.get('[data-test-subj="addFirstVisualizationText"]').should('exist'); }); it('Hides application panels in Operational Panels', () => { cy.visit( `${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/operational_panels/` ); - cy.wait(delay*4); - if (cy.get('.euiButton').contains('Create panel').length < 2) { - cy.get('input.euiFieldSearch').type(`${nameOne}'s Panel`, {delay: TYPING_DELAY}); - cy.wait(delay); - cy.get('.euiTableCellContent__text').contains('No items found').should('exist'); - cy.get('.euiFormControlLayoutClearButton').click(); - cy.wait(delay); - } + cy.get('body').then(($body) => { + if ($body.find('[data-test-subj="customPanels__emptyCreateNewPanels"]').length == 1) { + cy.get('[data-test-subj="operationalPanelSearchBar"]').type(`${nameOne}'s Panel`, {delay: TYPING_DELAY}); + cy.wait(delay); + cy.get('.euiTableCellContent__text').contains('No items found').should('exist'); + cy.get('.euiFormControlLayoutClearButton').click(); + cy.wait(delay); + } + }); }); it('Redirects to home page on cancel', () => { - cy.get('.euiButton').contains('Cancel').click(); - cy.wait(delay); - cy.get('.euiTitle').contains('Applications').should('exist'); + cy.get('[data-test-subj="cancelCreateButton"]').contains('Cancel').click(); + cy.get('[data-test-subj="applicationHomePageTitle"]').should('exist'); }); it('Saves current input on reload', () => { cy.get('[data-test-subj="nameFormRow"]').type(nameOne); cy.get('[data-test-subj="descriptionFormRow"]').type(description); - cy.get('.euiAccordion').contains('Log source').trigger('mouseover').click(); + cy.get('[data-test-subj="logSourceAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="searchAutocompleteTextArea"]').click(); cy .get('[data-test-subj="searchAutocompleteTextArea"]') .focus() .type(baseQuery, {delay: TYPING_DELAY}); - cy.get('.euiAccordion').contains('Services & entities').trigger('mouseover').click(); - cy.wait(delay); + cy.get('[data-test-subj="servicesEntitiesAccordion"]').trigger('mouseover').click(); + cy.get('[data-test-subj="servicesEntitiesComboBox"]').scrollIntoView(); cy.get('[data-test-subj="servicesEntitiesComboBox"]').trigger('mouseover').click(); - cy.wait(delay); - cy.get('.euiFilterSelectItem').contains(service_one).trigger('click'); - cy.get('.euiBadge').contains('1').should('exist'); - cy.get('.euiAccordion').contains('Trace groups').trigger('mouseover').click(); + cy.get('.euiFilterSelectItem').contains(service_one).click({ force: true }); + cy.get('[data-test-subj="servicesEntitiesCountBadge"]').should('contain', '1'); + cy.get('[data-test-subj="traceGroupsAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="traceGroupsComboBox"]').scrollIntoView().type('http'); cy.get('.euiFilterSelectItem').contains(trace_one).trigger('click'); cy.get('.euiFilterSelectItem').contains(trace_two).trigger('click'); - cy.get('.euiBadge').contains('2').should('exist'); + cy.get('[data-test-subj="traceGroupsCountBadge"]').should('contain', '2'); cy.reload(); cy.wait(delay); cy.get('[data-test-subj="nameFormRow"]').find('.euiFieldText').should('contain.value', nameOne); cy.get('[data-test-subj="descriptionFormRow"]').find('.euiFieldText').should('contain.value', description); - cy.get('.euiAccordion').contains('Log source').trigger('mouseover').click(); + cy.get('[data-test-subj="logSourceAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="searchAutocompleteTextArea"]').should('contain.value', baseQuery); - cy.get('.euiBadge').contains('1').should('exist'); - cy.get('.euiBadge').contains('2').should('exist'); + cy.get('[data-test-subj="servicesEntitiesCountBadge"]').should('contain', '1'); + cy.get('[data-test-subj="traceGroupsCountBadge"]').should('contain', '2'); }); it('Shows clear modals before clearing', () => { - cy.get('.euiAccordion').contains('Log source').click(); - cy.get('.euiButton-isDisabled').contains('Clear').should('exist'); + cy.get('[data-test-subj="logSourceAccordion"]').trigger('mouseover').click(); + cy.get('[data-test-subj="clearLogSourceButton"]').should('be.disabled'); cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus().type(baseQuery, {delay: TYPING_DELAY}); - cy.get('.euiButton').contains('Clear').click(); + cy.get('[data-test-subj="clearLogSourceButton"]').click(); cy.get('.euiButton--danger').contains('Clear').click(); cy.get('[data-test-subj="searchAutocompleteTextArea"]').should('contain.value', ''); - cy.get('.euiAccordion').contains('Services & entities').trigger('mouseover').click(); + cy.get('[data-test-subj="servicesEntitiesAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="servicesEntitiesComboBox"]').trigger('mouseover').click(); cy.get('.euiFilterSelectItem').contains(service_one).trigger('click'); - cy.get('.euiBadge').contains('1').should('exist'); - cy.get('.euiAccordion-isOpen').within(($service) => { - cy.get('.euiButton').contains('Clear all').click(); - }) + cy.get('[data-test-subj="servicesEntitiesCountBadge"]').should('contain', '1'); + cy.get('[data-test-subj="clearServicesEntitiesButton"]').click(); cy.get('.euiButton--danger').contains('Clear all').click(); - cy.get('.euiBadge').contains('1').should('not.exist'); - cy.get('.euiBadge').contains('0').should('exist'); - cy.get('.euiAccordion').contains('Services & entities').trigger('mouseover').click(); - cy.get('.euiAccordion').contains('Trace groups').trigger('mouseover').click(); + cy.get('[data-test-subj="servicesEntitiesCountBadge"]').should('contain', '0'); + cy.get('[data-test-subj="servicesEntitiesAccordion"]').trigger('mouseover').click(); + cy.get('[data-test-subj="traceGroupsAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="traceGroupsComboBox"]').scrollIntoView().type('http'); cy.get('.euiFilterSelectItem').contains(trace_one).trigger('click'); cy.get('.euiFilterSelectItem').contains(trace_two).trigger('click'); - cy.get('.euiBadge').contains('2').should('exist'); - cy.get('.euiAccordion-isOpen').within(($trace) => { - cy.get('.euiButton').contains('Clear all').click(); - }) + cy.get('[data-test-subj="traceGroupsCountBadge"]').should('contain', '2'); + cy.get('[data-test-subj="clearTraceGroupsButton"]').click(); cy.get('.euiButton--danger').contains('Clear all').click(); - cy.get('.euiBadge').contains('2').should('not.exist'); - cy.get('.euiBadge').contains('0').should('exist'); + cy.get('[data-test-subj="traceGroupsCountBadge"]').should('contain', '0'); }); }); @@ -203,36 +193,33 @@ describe('Viewing application', () => { }); it('Has working breadcrumbs', () => { - cy.get('.euiBreadcrumb').contains('Cypress').click(); - cy.wait(delay); - cy.get('[data-test-subj="appAnalyticsAppName"]').contains(nameOne).should('exist'); - cy.get('.euiBreadcrumb').contains('Application analytics').click(); - cy.wait(delay); - cy.get('.euiTitle').contains('Applications').should('exist'); - cy.get('.euiBreadcrumb').contains('Observability').click(); - cy.wait(delay); - cy.get('.euiTitle').contains('Event analytics').should('exist'); + cy.get('.euiBreadcrumb').contains(nameOne).click(); + cy.get('[data-test-subj="applicationTitle"]').should('contain', nameOne); + cy.get('.euiBreadcrumb[href="#/application_analytics"]').click(); + cy.get('[data-test-subj="applicationHomePageTitle"]').should('contain', 'Applications'); + cy.get('.euiBreadcrumb[href="observability-dashboards#/"]').click(); + cy.get('[data-test-subj="eventHomePageTitle"]').should('contain', 'Event analytics'); }); it('Shares time range among tabs', () => { moveToApplication(nameOne); changeTimeTo24('months'); cy.get('[data-test-subj="superDatePickerShowDatesButton"]').should('contain', 'Last 24 months'); - cy.get('.euiTab').contains('Services').click(); + cy.get('[data-test-subj="app-analytics-serviceTab"]').click(); cy.get('[data-test-subj="superDatePickerShowDatesButton"]').should('contain', 'Last 24 months'); - cy.get('.euiTab').contains('Traces & Spans').click(); + cy.get('[data-test-subj="app-analytics-traceTab"]').click(); supressResizeObserverIssue(); cy.get('[data-test-subj="superDatePickerShowDatesButton"]').should('contain', 'Last 24 months'); - cy.get('.euiTab').contains('Log Events').click(); + cy.get('[data-test-subj="app-analytics-logTab"]').click(); cy.get('[data-test-subj="superDatePickerShowDatesButton"]').should('contain', 'Last 24 months'); - cy.get('.euiTab').contains('Panel').click(); + cy.get('[data-test-subj="app-analytics-panelTab"]').click(); cy.get('[data-test-subj="superDatePickerShowDatesButton"]').should('contain', 'Last 24 months'); }); it('Shows latency variance in dashboards table', () => { moveToApplication(nameOne); changeTimeTo24('months'); - cy.get('.euiBasicTable').first().within(($table) => { + cy.get('[data-test-subj="dashboardTable"]').first().within(($table) => { cy.get('.plot-container').should('have.length.at.least', 1); }) }); @@ -240,147 +227,135 @@ describe('Viewing application', () => { it('Saves time range for each application', () => { moveToCreatePage(); cy.get('[data-test-subj="nameFormRow"]').type(nameTwo); - cy.get('.euiAccordion').contains('Log source').trigger('mouseover').click(); + cy.get('[data-test-subj="logSourceAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus().type(baseQuery, {delay: TYPING_DELAY}); - cy.get('.euiButton').contains('Create').should('not.be.disabled'); - cy.get('.euiButton').contains('Create').click(); - cy.wait(delay); - cy.get('.euiTitle').contains(nameTwo).should('exist'); + cy.get('[data-test-subj="createButton"]').should('not.be.disabled'); + cy.get('[data-test-subj="createButton"]').click(); + cy.get('[data-test-subj="applicationTitle"]').should('contain', nameTwo); changeTimeTo24('weeks'); cy.get('[data-test-subj="superDatePickerShowDatesButton"]').should('contain', 'Last 24 weeks'); - cy.get('.euiBreadcrumb').contains('Application analytics').click(); + cy.get('.euiBreadcrumb[href="#/application_analytics"]').click(); cy.wait(delay); cy.get('.euiLink').contains(nameOne).click(); - cy.wait(delay); - cy.get('.euiTitle').contains(nameOne).should('exist'); + cy.get('[data-test-subj="applicationTitle"]').should('contain', nameOne); cy.get('[data-test-subj="superDatePickerShowDatesButton"]').should('contain', 'Last 24 months'); - cy.get('.euiBreadcrumb').contains('Application analytics').click(); - cy.wait(delay); + cy.get('.euiBreadcrumb[href="#/application_analytics"]').click(); cy.get('.euiLink').contains(nameTwo).click(); - cy.wait(delay); - cy.get('.euiTitle').contains(nameTwo).should('exist'); + cy.get('[data-test-subj="applicationTitle"]').should('contain', nameTwo); cy.get('[data-test-subj="superDatePickerShowDatesButton"]').should('contain', 'Last 24 weeks'); }); it('Adds filter when Trace group name is clicked', () => { moveToApplication(nameOne); - cy.get('.euiTab').contains('Overview').click(); - cy.wait(delay); + cy.get('[data-test-subj="app-analytics-overviewTab"]').click(); cy.get('.euiLink').contains('client_create_order').click(); cy.get('.euiTableRow').should('have.length', 1); - cy.get('.euiPopover').contains('traceGroup: client_create_order').should('exist'); - cy.get('[aria-label="Remove filter"]').click(); - cy.get('.euiPopover').contains('traceGroup: client_create_order').should('not.exist'); + cy.get('[data-test-subj="client_create_orderFilterBadge"]').should('exist'); + cy.get('[data-test-subj="filterBadge"]').click(); + cy.get('[data-test-subj="deleteFilterIcon"]').click(); + cy.get('[data-test-subj="client_create_orderFilterBadge"]').should('not.exist'); }); it('Opens service detail flyout when Service Name is clicked', () => { - cy.get('.euiTab').contains('Services').click(); + cy.get('[data-test-subj="app-analytics-serviceTab"]').click(); cy.wait(delay); cy.get('.euiLink').contains('authentication').click(); supressResizeObserverIssue(); - cy.wait(delay * 3); - cy.get('.euiFlyout').contains('Service detail').should('be.visible'); - cy.get('.euiFlyout').within(($flyout) => { - cy.get('.euiDescriptionList').contains('3.52').should('exist'); + cy.get('[data-test-subj="serviceDetailFlyoutTitle"]').should('be.visible'); + cy.get('[data-test-subj="serviceDetailFlyout"]').within(($flyout) => { + cy.get('[data-test-subj="Number of connected servicesDescriptionList"]').should('contain', '3'); cy.get('[data-text="Error rate"]').click(); cy.get('.ytitle').contains('Error rate').should('exist'); }); - cy.get('[data-test-subj="euiFlyoutCloseButton"]').click(); - cy.get('.euiFlyout').should('not.be.visible'); - cy.get('.euiLink').contains('authentication').click(); - supressResizeObserverIssue(); - cy.wait(delay * 3); - cy.get('.euiFlyout').contains('Service detail').scrollIntoView().should('be.visible'); cy.get('[data-test-subj="dataGridRowCell"] button').contains('718dc32a693c8a17').click(); - cy.get('.euiFlyout').contains('Span detail').should('be.visible'); + cy.get('[data-test-subj="spanDetailFlyout"]').contains('Span detail').should('be.visible'); + cy.get('[data-test-subj="ServiceDescriptionList"]').should('contain', 'authentication'); cy.get('[data-test-subj="euiFlyoutCloseButton"]').click(); - cy.get('.euiFlyout').should('not.be.visible'); + cy.get('[data-test-subj="serviceDetailFlyout"]').should('not.be.visible'); + cy.get('[data-test-subj="spanDetailFlyout"]').should('not.be.visible'); }); it('Opens trace detail flyout when Trace ID is clicked', () => { - cy.get('.euiTab').contains('Traces & Spans').click(); + cy.get('[data-test-subj="app-analytics-traceTab"]').click(); supressResizeObserverIssue(); cy.wait(delay); cy.get('[title="03f9c770db5ee2f1caac0afc36db49ba"]').click(); - cy.get('.euiFlyout').contains('Trace detail').should('be.visible'); - cy.get('.euiFlyout').within(($flyout) => { - cy.get('.euiDescriptionList').contains('224.99').should('exist'); + cy.get('[data-test-subj="traceDetailFlyoutTitle"]').should('be.visible'); + cy.get('[data-test-subj="traceDetailFlyout"]').within(($flyout) => { + cy.get('[data-test-subj="LatencyDescriptionList"]').should('contain', '224.99'); }); cy.get('[data-test-subj="euiFlyoutCloseButton"]').click(); cy.wait(delay); - cy.get('.euiFlyout').should('not.be.visible'); + cy.get('[data-test-subj="traceDetailFlyout"]').should('not.be.visible'); cy.get('[title="03f9c770db5ee2f1caac0afc36db49ba"]').click(); cy.get('[data-text="Span list"]').click(); cy.wait(delay); cy.get('[data-test-subj="dataGridRowCell"] button').contains('d67c5bb617ba9203').click(); - cy.get('.euiFlyout').contains('Span detail').should('be.visible'); + cy.get('[data-test-subj="spanDetailFlyout"]').should('be.visible'); cy.get('[data-test-subj="euiFlyoutCloseButton"]').click(); - cy.get('.euiFlyout').should('not.be.visible'); + cy.get('[data-test-subj="spanDetailFlyout"]').should('not.be.visible'); }); it('Opens span detail flyout when Span ID is clicked', () => { - cy.get('.euiTab').contains('Traces & Spans').click(); + cy.get('[data-test-subj="app-analytics-traceTab"]').click(); supressResizeObserverIssue(); cy.wait(delay); - cy.get('.euiLink').contains('5ff3516909562c60').click(); - cy.get('.euiFlyout').contains('Span detail').should('be.visible'); - cy.get('.euiFlyout').within(($flyout) => { - cy.get('.euiText').contains('HTTP GET').should('exist'); + cy.get('[data-test-subj="dataGridRowCell"]').contains('5ff3516909562c60').click(); + cy.get('[data-test-subj="spanDetailFlyout"]').should('be.visible'); + cy.get('[data-test-subj="spanDetailFlyout"]').within(($flyout) => { + cy.get('[data-test-subj="OperationDescriptionList"]').should('contain', 'HTTP GET'); }); cy.get('.euiText').contains('order').click(); cy.get('[aria-label="span-flyout-filter-icon"]').click(); cy.focused().blur(); cy.get('[data-test-subj="euiFlyoutCloseButton"]').click(); - cy.get('.euiPopover').contains('serviceName: order').should('exist'); + cy.get('[data-test-subj="filterBadge"][title="serviceName: order"]').should('exist'); cy.get('[aria-label="Remove filter"]').click(); - cy.get('.euiPopover').contains('serviceName: order').should('not.exist'); + cy.get('[data-test-subj="filterBadge"][title="serviceName: order"]').should('not.exist'); }); it('Shows base query', () => { - cy.get('.euiTab').contains('Log Events').click(); - cy.get('.euiBadge__text').contains('Base Query').should('exist'); + cy.get('[data-test-subj="app-analytics-logTab"]').click(); + cy.get('.euiBadge[title="Base Query"]').should('exist'); + cy.get('.euiBadge[title="Base Query"]').trigger('mouseover'); + cy.get('.euiToolTipPopover').contains('source = opensearch_dashboards_sample_data_flights').should('exist'); }); it('Saves visualization #1 to panel', () => { - cy.get('.euiTab').contains('Panel').click(); - cy.get('.euiButton').contains('Add').click(); + cy.get('[data-test-subj="app-analytics-panelTab"]').click(); + cy.get('[data-test-subj="addVisualizationButton"]').first().click(); cy.wait(delay); - cy.get('.plot-container').should('exist'); + cy.get('[id="explorerPlotComponent"]').should('exist'); cy.get('[data-test-subj="searchAutocompleteTextArea"]').click(); cy.get('.aa-List').find('.aa-Item').should('have.length', 11); - cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus().type('x', {delay: TYPING_DELAY}); - cy.focused().clear(); cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus().type(query_one, {delay: TYPING_DELAY}); changeTimeTo24('months'); cy.wait(delay * 2); - cy.get('.euiTab').contains('Visualizations').click(); + cy.get('[data-test-subj="main-content-visTab"]').click(); supressResizeObserverIssue(); cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').click(); cy.get('[data-test-subj="eventExplorer__querySaveName"]').click().type(visOneName); cy.wait(delay); cy.get('[data-test-subj="eventExplorer__querySaveConfirm"]').click(); + cy.get('[data-test-subj="app-analytics-panelTab"]').click(); cy.wait(delay); - cy.get('.euiTab').contains('Panel').click(); - cy.wait(delay); - cy.get('.js-plotly-plot').should('exist'); + cy.get('[data-test-subj="Flights to VeniceVisualizationPanel"]').should('exist'); + cy.get('[id="explorerPlotComponent"]').should('exist'); + cy.get('[class="trace bars"]').should('exist'); }); it('Adds availability level to visualization #1', () => { - cy.get('.euiTab').contains('Panel').click(); - cy.wait(delay); + cy.get('[data-test-subj="app-analytics-panelTab"]').click(); cy.get('[aria-label="actionMenuButton"]').click(); - cy.get('.euiContextMenuItem').contains('Edit').click(); + cy.get('[data-test-subj="editVizContextMenuItem"]').click(); supressResizeObserverIssue(); - cy.wait(delay); cy.get('[data-test-subj="superDatePickerShowDatesButton"]').should('contain', 'Last 24 months'); - cy.get('.euiBadge').contains('Bar').click(); + cy.get('[title="Bar"]').click(); cy.focused().type('{downArrow}'); cy.focused().type('{enter}'); - cy.wait(delay); - cy.get('.euiButton').contains('+ Add availability level').click(); + cy.get('[data-test-subj="addThresholdButton"]').click(); cy.get('[data-test-subj="euiColorPickerAnchor"]').click(); cy.get('[aria-label="Select #54B399 as the color"]').click(); - cy.wait(delay); cy.get('[data-test-subj="nameFieldText"]').click().type('Available'); cy.get('option').contains('≥').should('exist'); cy.get('option').contains('≤').should('exist'); @@ -391,39 +366,36 @@ describe('Viewing application', () => { cy.get('[data-test-subj="expressionSelect"]').select('>'); cy.get('[data-test-subj="valueFieldNumber"]').clear().type('0.5'); cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); - cy.wait(delay); cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').click(); cy.get('[data-test-subj="eventExplorer__querySaveConfirm"]').click(); - cy.wait(delay); - cy.get('.euiTab').contains('Panel').click(); - cy.wait(delay); - cy.get('.js-plotly-plot').should('exist'); + cy.get('[data-test-subj="app-analytics-panelTab"]').click(); + cy.get('[id="explorerPlotComponent"]').should('exist'); + cy.get('[class="lines"]').should('exist'); cy.get('.textpoint').contains('Available').should('exist'); - cy.get('.euiBreadcrumb').contains('Application analytics').click(); - cy.wait(delay); - cy.get('.euiBadge').contains('Available').should('exist'); - cy.get('[style="background-color: rgb(84, 179, 153); color: rgb(0, 0, 0);"]').should('exist'); + cy.get('.euiBreadcrumb[href="#/application_analytics"]').click(); + cy.get('[data-test-subj="AvailableAvailabilityBadge"]').should('contain', 'Available'); + cy.get('[data-test-subj="AvailableAvailabilityBadge"][style="background-color: rgb(84, 179, 153); color: rgb(0, 0, 0);"]').should('exist'); }); it('Saves visualization #2 to panel with availability level', () => { moveToApplication(nameOne); changeTimeTo24('months'); - cy.get('.euiTab').contains('Log Events').click(); + cy.get('[data-test-subj="app-analytics-logTab"]').click(); cy.wait(delay); - cy.get('.plot-container').should('exist'); + cy.get('[id="explorerPlotComponent"]').should('exist'); cy.get('[data-test-subj="searchAutocompleteTextArea"]').clear(); cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus().type('x', {delay: TYPING_DELAY}); cy.focused().clear(); cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus().type(query_two, {delay: TYPING_DELAY}); - cy.get('.euiButton').contains('Refresh').click(); + cy.get('[data-test-subj="superDatePickerApplyTimeButton"]').click(); cy.wait(delay); - cy.get('.euiTab').contains('Visualizations').click(); + cy.get('[data-test-subj="main-content-visTab"]').click(); supressResizeObserverIssue(); - cy.get('.euiBadge').contains('Bar').click(); + cy.get('[title="Bar"]').click(); cy.focused().type('{downArrow}'); cy.focused().type('{enter}'); cy.wait(delay); - cy.get('.euiButton').contains('+ Add availability level').click(); + cy.get('[data-test-subj="addThresholdButton"]').click(); cy.get('[data-test-subj="euiColorPickerAnchor"]').click(); cy.get('[aria-label="Select #9170B8 as the color"]').click(); cy.wait(delay); @@ -432,7 +404,7 @@ describe('Viewing application', () => { cy.get('[data-test-subj="valueFieldNumber"]').clear().type('5.5'); cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); cy.wait(delay); - cy.get('.euiButton').contains('+ Add availability level').click(); + cy.get('[data-test-subj="addThresholdButton"]').click(); cy.get('[data-test-subj="euiColorPickerAnchor"]').first().click(); cy.get('[aria-label="Select #CA8EAE as the color"]').click(); cy.wait(delay); @@ -446,20 +418,19 @@ describe('Viewing application', () => { cy.wait(delay); cy.get('[data-test-subj="eventExplorer__querySaveConfirm"]').click(); cy.wait(delay); - cy.get('.euiTab').contains('Panel').click(); + cy.get('[data-test-subj="app-analytics-panelTab"]').click(); cy.wait(delay); - cy.get('.js-plotly-plot').should('have.length', 2); + cy.get('[id="explorerPlotComponent"]').should('have.length', 2); moveToHomePage(); - cy.get('.euiBadge').contains('Super').should('exist'); - cy.get('[style="background-color: rgb(145, 112, 184); color: rgb(0, 0, 0);"]').should('exist'); + cy.get('[data-test-subj="SuperAvailabilityBadge"][style="background-color: rgb(145, 112, 184); color: rgb(0, 0, 0);"]').should('contain', 'Super'); }); it('Configuration tab shows details', () => { - cy.get('.euiLink').contains(nameOne).click(); + cy.get(`[data-test-subj="${nameOne}ApplicationLink"]`).click(); cy.wait(delay); - cy.get('.euiTab').contains('Configuration').click(); + cy.get('[data-test-subj="app-analytics-configTab"]').click(); cy.wait(delay); - cy.get('.euiCodeBlock__code').contains(baseQuery).should('exist'); + cy.get('[data-test-subj="configBaseQueryCode"]').should('contain', baseQuery); cy.get('[aria-label="List of services and entities"]').find('li').should('have.length', 1); cy.get('[aria-label="List of trace groups"]').find('li').should('have.length', 2); cy.get('option').should('have.length', 2); @@ -467,83 +438,78 @@ describe('Viewing application', () => { it('Changes availability visualization', () => { - cy.get('.euiTab').contains('Configuration').click(); + cy.get('[data-test-subj="app-analytics-configTab"]').click(); cy.wait(delay); cy.get('select').select(visOneName); cy.wait(delay); moveToHomePage(); - cy.get('.euiBadge').contains('Available').should('exist'); - cy.get('[style="background-color: rgb(84, 179, 153); color: rgb(0, 0, 0);"]').should('exist'); + cy.get('[data-test-subj="AvailableAvailabilityBadge"][style="background-color: rgb(84, 179, 153); color: rgb(0, 0, 0);"]').should('contain', 'Available'); moveToApplication(nameOne); - cy.get('.euiTab').contains('Configuration').click(); + cy.get('[data-test-subj="app-analytics-configTab"]').click(); cy.wait(delay); cy.get('select').find('option:selected').should('have.text', visOneName); }) it('Hides application visualizations in Event Analytics', () => { cy.visit(`${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/event_analytics`); - cy.wait(delay*3); - if (cy.get('.euiButton').length == 2) { - cy.get('input.euiFieldSearch').type(visOneName, {delay: TYPING_DELAY}); - cy.wait(delay); - cy.get('.euiTableCellContent__text').contains('No items found').should('exist'); - cy.get('input.euiFieldSearch').clear().type(visTwoName, {delay: TYPING_DELAY}); - cy.wait(delay); - cy.get('.euiTableCellContent__text').contains('No items found').should('exist'); - cy.get('.euiFormControlLayoutClearButton').click(); - cy.wait(delay); - cy.get('[data-test-subj="tablePaginationPopoverButton"]').click(); - cy.get('.euiContextMenuItem__text').contains('50 rows').click(); - cy.get('.euiCheckbox__input[data-test-subj="checkboxSelectAll"]').click(); - cy.wait(delay); - cy.get('.euiButton__text').contains('Actions').click(); - cy.wait(delay); - cy.get('.euiContextMenuItem__text').contains('Delete').click(); - cy.wait(delay); - - cy.get('button.euiButton--danger').should('be.disabled'); - - cy.get('input.euiFieldText[placeholder="delete"]').type('delete'); - cy.get('button.euiButton--danger').should('not.be.disabled'); - cy.get('.euiButton__text').contains('Delete').click(); - cy.wait(delay); - } + cy.wait(delay * 3); + // When there are saved queries or visualizations there are two buttons + cy.get('body').then(($body) => { + if ($body.find('.euiButton').length == 2) { + cy.get('input.euiFieldSearch').type(visOneName, {delay: TYPING_DELAY}); + cy.wait(delay); + cy.get('.euiTableCellContent__text').contains('No items found').should('exist'); + cy.get('input.euiFieldSearch').clear().type(visTwoName, {delay: TYPING_DELAY}); + cy.wait(delay); + cy.get('.euiTableCellContent__text').contains('No items found').should('exist'); + cy.get('[class="euiFormControlLayoutClearButton"]').click(); + cy.wait(delay); + cy.get('[data-test-subj="tablePaginationPopoverButton"]').click(); + cy.get('.euiContextMenuItem__text').contains('50 rows').click(); + cy.get('.euiCheckbox__input[data-test-subj="checkboxSelectAll"]').click(); + cy.wait(delay); + cy.get('[data-test-subj="eventHomeAction"]').click(); + cy.wait(delay); + cy.get('[data-test-subj="eventHomeAction__delete"]').click(); + cy.wait(delay); + cy.get('[data-test-subj="popoverModal__deleteButton"]').should('be.disabled'); + cy.get('[data-test-subj="popoverModal__deleteTextInput"]').type('delete'); + cy.get('[data-test-subj="popoverModal__deleteButton"]').should('not.be.disabled'); + cy.get('[data-test-subj="popoverModal__deleteButton"]').click(); + cy.wait(delay); + } + }) }); it('Hides application visualizations in Operational Panels', () => { cy.visit( `${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/operational_panels/` ); - cy.wait(delay*3); - cy.get('.euiButton__text').contains('Actions').click(); + cy.get('[data-test-subj="operationalPanelsActionsButton"]').click(); cy.wait(delay); - cy.get('.euiContextMenuItem__text').contains('Add samples').click(); + cy.get('[data-test-subj="addSampleContextMenuItem"]').click(); cy.wait(delay); - cy.get('.euiButton__text').contains('Yes').click(); - cy.wait(delay*2); + cy.get('[data-test-subj="confirmModalConfirmButton"]').click(); + cy.wait(delay * 2); cy.get('.euiLink').contains('[Logs] Web traffic Panel').first().click(); cy.wait(delay); - cy.get('.euiButton__text').contains('Add visualization').click(); + cy.get('[data-test-subj="addVisualizationButton"]').click(); cy.get('.euiContextMenuItem__text').contains('Select existing visualization').click(); cy.get('option').contains(visOneName).should('not.exist'); cy.get('option').contains(visTwoName).should('not.exist'); cy.visit( `${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/operational_panels/` ); - cy.wait(delay*3); - cy.get('input.euiFieldSearch').type('[Logs] Web traffic Panel', {delay: TYPING_DELAY}); + cy.get('[data-test-subj="operationalPanelSearchBar"]').type('[Logs] Web traffic Panel', {delay: TYPING_DELAY}); cy.wait(delay); cy.get('.euiTableRow').first().within(($row) => { cy.get('.euiCheckbox').click(); }); - cy.get('.euiButton__text').contains('Actions').click(); - cy.wait(delay); - cy.get('.euiContextMenuItem__text').contains('Delete').click(); - cy.wait(delay); - cy.get('input.euiFieldText[placeholder="delete"]').type('delete'); - cy.get('button.euiButton--danger').should('not.be.disabled'); - cy.get('.euiButton__text').contains('Delete').click(); - cy.wait(delay); + cy.get('[data-test-subj="operationalPanelsActionsButton"]').click(); + cy.get('[data-test-subj="deleteContextMenuItem"]').click(); + cy.get('[data-test-subj="popoverModal__deleteTextInput"]').type('delete'); + cy.get('[data-test-subj="popoverModal__deleteButton"]').should('not.be.disabled'); + cy.get('[data-test-subj="popoverModal__deleteButton"]').click(); }); }); @@ -553,25 +519,23 @@ describe('Editing application', () => { }); it('Redirects to application after saving changes', () => { - cy.get('.euiAccordion').contains('Log source').click(); + cy.get('[data-test-subj="logSourceAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="searchAutocompleteTextArea"]').should('be.disabled'); - cy.get('.euiAccordion').contains('Trace groups').click(); - cy.get('[data-test-subj="comboBoxToggleListButton"]').filter(':visible').click(); - cy.get('.euiFilterSelectItem').contains(trace_three).trigger('click'); - cy.get('.euiBadge').contains('3').should('exist'); - cy.get('.euiAccordion').contains('Trace groups').click(); - cy.get('.euiAccordion').contains('Services & entities').click(); + cy.get('[data-test-subj="servicesEntitiesAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="servicesEntitiesComboBox"]').click(); - cy.get('.euiFilterSelectItem').contains(service_two).trigger('click'); - cy.get('.euiBadge').contains('2').should('exist'); - cy.get('.euiButton').contains('Save').click(); - cy.wait(delay); - cy.get('.euiTab').contains('Configuration').click(); - cy.wait(delay); - cy.get('.euiCodeBlock__code').contains(baseQuery).should('exist'); + cy.get('.euiFilterSelectItem').contains(service_two).click(); + cy.get('[data-test-subj="servicesEntitiesCountBadge"]').should('contain', '2'); + cy.get('[data-test-subj="traceGroupsAccordion"]').trigger('mouseover').click(); + cy.get('[data-test-subj="comboBoxToggleListButton"]').eq(1).click(); + cy.get('.euiFilterSelectItem').contains(trace_three).trigger('click'); + cy.get('[data-test-subj="traceGroupsCountBadge"]').should('contain', '3'); + cy.get('[data-test-subj="traceGroupsAccordion"]').trigger('mouseover').click(); + cy.get('[data-test-subj="createButton"]').click(); + cy.get('[data-test-subj="app-analytics-configTab"]').click(); + cy.get('[data-test-subj="configBaseQueryCode"]').should('contain', baseQuery); cy.get('[aria-label="List of services and entities"]').find('li').should('have.length', 2); cy.get('[aria-label="List of trace groups"]').find('li').should('have.length', 3); - cy.get('.euiTitle').contains(nameOne).should('exist'); + cy.get('[data-test-subj="applicationTitle"]').should('contain', nameOne); }); }); @@ -581,24 +545,22 @@ describe('Application Analytics home page', () => { }) it('Show correct information in table', () => { - cy.get('.euiLink').contains(nameOne).should('exist'); - cy.get('[data-test-subj="appAnalytics__compositionColumn"]').contains(composition).should('exist'); - cy.get('.euiBadge').contains('Available').should('exist'); - cy.get('[style="background-color: rgb(84, 179, 153); color: rgb(0, 0, 0);"]').should('exist'); + cy.get(`[data-test-subj="${nameOne}ApplicationLink"]`).should('exist'); + cy.get('[data-test-subj="appAnalytics__compositionColumn"]').should('contain', composition); + cy.get('[data-test-subj="AvailableAvailabilityBadge"][style="background-color: rgb(84, 179, 153); color: rgb(0, 0, 0);"]').should('contain', 'Available') }); it('Renames application', () => { - cy.get('.euiPopover').contains('Actions').click(); - cy.wait(delay); - cy.get('.euiContextMenuItem-isDisabled').contains('Rename').should('exist'); + cy.get('[data-test-subj="appAnalyticsActionsButton"]').click(); + cy.get('[data-test-subj="renameApplicationContextMenuItem"]').should('be.disabled'); + cy.get('[data-test-subj="appAnalyticsActionsButton"]').click(); cy.get('.euiTableRow').first().within(($row) => { cy.get('.euiCheckbox').click(); }); - cy.wait(delay); - cy.get('.euiPopover').contains('Actions').click(); - cy.get('.euiContextMenuItem').contains('Rename').click(); - cy.get('.euiFieldText').clear().focus().type(newName); - cy.get('.euiButton--fill').contains('Rename').click(); + cy.get('[data-test-subj="appAnalyticsActionsButton"]').click(); + cy.get('[data-test-subj="renameApplicationContextMenuItem"]').click(); + cy.get('[data-test-subj="customModalFieldText"]').clear().focus().type(newName); + cy.get('[data-test-subj="runModalButton"]').click(); cy.wait(delay); cy.get('.euiToast').contains(`Application successfully renamed to "${newName}"`); cy.get('.euiTableRow').first().within(($row) => { @@ -607,21 +569,20 @@ describe('Application Analytics home page', () => { }); it('Deletes application', () => { - cy.get('.euiPopover').contains('Actions').click(); - cy.get('.euiContextMenuItem-isDisabled').contains('Delete').should('exist'); - cy.get('.euiTableRow').eq(0).within(($row) => { + cy.get('[data-test-subj="appAnalyticsActionsButton"]').click(); + cy.get('[data-test-subj="deleteApplicationContextMenuItem"]').should('exist'); + cy.get('[data-test-subj="appAnalyticsActionsButton"]').click(); + cy.get('.euiTableRow').first().within(($row) => { cy.get('.euiCheckbox').click(); }); cy.get('.euiTableRow').eq(1).within(($row) => { cy.get('.euiCheckbox').click(); }); - cy.wait(delay); - cy.get('.euiPopover').contains('Actions').click(); - cy.wait(delay); - cy.get('.euiContextMenuItem').contains('Delete').click(); - cy.get('.euiButton--fill').contains('Delete').click(); + cy.get('[data-test-subj="appAnalyticsActionsButton"]').click(); + cy.get('[data-test-subj="deleteApplicationContextMenuItem"]').click(); + cy.get('[data-test-subj="confirmModalConfirmButton"]').click(); cy.wait(delay); cy.get('.euiToast').contains(`Applications successfully deleted!`); - cy.get('.euiLink').contains(newName).should('not.exist'); + cy.get(`[data-test-subj="${newName}ApplicationLink"]`).should('not.exist'); }); }); diff --git a/dashboards-observability/.cypress/utils/app_constants.js b/dashboards-observability/.cypress/utils/app_constants.js index 86e713879..a36a6fb50 100644 --- a/dashboards-observability/.cypress/utils/app_constants.js +++ b/dashboards-observability/.cypress/utils/app_constants.js @@ -17,30 +17,28 @@ export const moveToHomePage = () => { export const moveToCreatePage = () => { cy.visit(`${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/application_analytics/`); - cy.wait(delay * 2); - cy.get('.euiButton__text').contains('Create application').click(); + cy.get('.euiButton[href="#/application_analytics/create"]').eq(0).click(); supressResizeObserverIssue(); - cy.wait(delay); - cy.get('.euiTitle').contains('Create application').should('exist'); + cy.get('[data-test-subj="createPageTitle"]').should('contain', 'Create application'); }; export const moveToApplication = (name) => { cy.visit(`${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/application_analytics/`); supressResizeObserverIssue(); cy.wait(delay * 6); - cy.get('.euiLink').contains(name).click(); + cy.get(`[data-test-subj="${name}ApplicationLink"]`).click(); cy.wait(delay); - cy.get('.euiTitle').contains(name).should('exist'); + cy.get('[data-test-subj="applicationTitle"]').should('contain', name); changeTimeTo24('years'); }; export const moveToEditPage = () => { moveToApplication(nameOne); - cy.get('.euiTab').contains('Configuration').click(); - cy.get('.euiButton').contains('Edit').click(); + cy.get('[data-test-subj="app-analytics-configTab"]').click(); + cy.get('[data-test-subj="editApplicationButton"]').click(); supressResizeObserverIssue(); cy.wait(delay); - cy.get('.euiTitle').contains('Edit application'); + cy.get('[data-test-subj="createPageTitle"]').should('contain', 'Edit application'); }; export const changeTimeTo24 = (timeUnit) => { diff --git a/dashboards-observability/common/constants/application_analytics.ts b/dashboards-observability/common/constants/application_analytics.ts index dc24b495b..df675a704 100644 --- a/dashboards-observability/common/constants/application_analytics.ts +++ b/dashboards-observability/common/constants/application_analytics.ts @@ -3,12 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -export const TAB_OVERVIEW_ID_TXT_PFX = 'app-analytics-overview-'; -export const TAB_SERVICE_ID_TXT_PFX = 'app-analytics-service-'; -export const TAB_TRACE_ID_TXT_PFX = 'app-analytics-trace-'; -export const TAB_LOG_ID_TXT_PFX = 'app-analytics-log-'; -export const TAB_PANEL_ID_TXT_PFX = 'app-analytics-panel-'; -export const TAB_CONFIG_ID_TXT_PFX = 'app-analytics-config-'; +export const TAB_OVERVIEW_ID = 'app-analytics-overview'; +export const TAB_SERVICE_ID = 'app-analytics-service'; +export const TAB_TRACE_ID = 'app-analytics-trace'; +export const TAB_LOG_ID = 'app-analytics-log'; +export const TAB_PANEL_ID = 'app-analytics-panel'; +export const TAB_CONFIG_ID = 'app-analytics-config'; export const TAB_OVERVIEW_TITLE = 'Overview'; export const TAB_SERVICE_TITLE = 'Services'; export const TAB_TRACE_TITLE = 'Traces & Spans'; diff --git a/dashboards-observability/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap b/dashboards-observability/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap index c93d4518e..c8710d638 100644 --- a/dashboards-observability/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap +++ b/dashboards-observability/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap @@ -27,6 +27,7 @@ Object { >

Create application @@ -156,6 +157,7 @@ Object {
+
+ + + +
@@ -2325,7 +2349,7 @@ Object { class="euiToolTipAnchor" > + + @@ -3527,7 +3575,7 @@ Object { class="euiToolTipAnchor" > + + @@ -4670,7 +4742,7 @@ Object { class="euiToolTipAnchor" > + + @@ -5872,7 +5968,7 @@ Object { class="euiToolTipAnchor" > + + @@ -7015,7 +7135,7 @@ Object { class="euiToolTipAnchor" > + + @@ -8152,7 +8296,7 @@ Object { class="euiToolTipAnchor" > + + @@ -9232,7 +9400,7 @@ Object { class="euiToolTipAnchor" > + + @@ -10400,7 +10592,7 @@ Object { class="euiToolTipAnchor" > +
+ + + +
@@ -11510,7 +11725,7 @@ Object { class="euiToolTipAnchor" > +
+ + + +
@@ -12711,7 +12949,7 @@ Object { class="euiToolTipAnchor" > + + @@ -13854,7 +14116,7 @@ Object { class="euiToolTipAnchor" > + + @@ -15056,7 +15342,7 @@ Object { class="euiToolTipAnchor" > + + @@ -16199,7 +16509,7 @@ Object { class="euiToolTipAnchor" > + + @@ -17371,7 +17705,7 @@ Object { class="euiToolTipAnchor" > + + @@ -18596,7 +18954,7 @@ Object { class="euiToolTipAnchor" > + + diff --git a/dashboards-observability/public/components/application_analytics/components/app_table.tsx b/dashboards-observability/public/components/application_analytics/components/app_table.tsx index 5f6b33b37..27611aa76 100644 --- a/dashboards-observability/public/components/application_analytics/components/app_table.tsx +++ b/dashboards-observability/public/components/application_analytics/components/app_table.tsx @@ -39,6 +39,7 @@ import { getCustomModal } from '../../custom_panels/helpers/modal_containers'; import { getClearModal } from '../helpers/modal_containers'; import { pageStyles, UI_DATE_FORMAT } from '../../../../common/constants/shared'; import { ApplicationListType } from '../../../../common/types/app_analytics'; +import { AvailabilityType } from '../helpers/types'; interface AppTableProps extends AppAnalyticsComponentDeps { loading: boolean; @@ -47,6 +48,7 @@ interface AppTableProps extends AppAnalyticsComponentDeps { renameApplication: (newAppName: string, appId: string) => void; deleteApplication: (appList: string[], panelIdList: string[], toastMessage?: string) => void; clearStorage: () => void; + moveToApp: (id: string, type: string) => void; } export function AppTable(props: AppTableProps) { @@ -59,6 +61,7 @@ export function AppTable(props: AppTableProps) { deleteApplication, setFilters, clearStorage, + moveToApp, } = props; const [isModalVisible, setIsModalVisible] = useState(false); const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false); @@ -179,6 +182,34 @@ export function AppTable(props: AppTableProps) { // Add sample application, ]; + const renderAvailability = (value: AvailabilityType, record: ApplicationListType) => { + if (value.color === 'loading') { + return ; + } else if (value.name) { + return ( + + {value.name} + + ); + } else if (value.color === 'undefined') { + return No match; + } else if (value.color === 'null') { + return -; + } else { + return ( + moveToApp(record.id, 'createSetAvailability')} + > + Set Availability + + ); + } + }; + const tableColumns = [ { field: 'name', @@ -211,22 +242,7 @@ export function AppTable(props: AppTableProps) { field: 'availability', name: 'Current Availability', sortable: true, - render: (value, record) => { - if (value.name === 'loading') { - return ; - } else if (value.name) { - return ( - - {value.name} - - ); - } else { - return Undefined; - } - }, + render: renderAvailability, }, { field: 'dateModified', diff --git a/dashboards-observability/public/components/application_analytics/components/application.tsx b/dashboards-observability/public/components/application_analytics/components/application.tsx index a5d3c26d5..dd766ce0b 100644 --- a/dashboards-observability/public/components/application_analytics/components/application.tsx +++ b/dashboards-observability/public/components/application_analytics/components/application.tsx @@ -75,14 +75,6 @@ const searchBarConfigs = { }, }; -export interface DetailTab { - id: string; - label: string; - description: string; - onClick: () => void; - testId: string; -} - interface AppDetailProps extends AppAnalyticsComponentDeps { disabled?: boolean; appId: string; @@ -93,6 +85,7 @@ interface AppDetailProps extends AppAnalyticsComponentDeps { notifications: NotificationsStart; updateApp: (appId: string, updateAppData: Partial, type: string) => void; setToasts: (title: string, color?: string, text?: ReactChild) => void; + callback: (childfunction: () => void) => void; } export function Application(props: AppDetailProps) { @@ -113,6 +106,7 @@ export function Application(props: AppDetailProps) { setAppConfigs, setToasts, setFilters, + callback, } = props; const [application, setApplication] = useState({ name: '', @@ -124,6 +118,7 @@ export function Application(props: AppDetailProps) { availabilityVisId: '', }); const dispatch = useDispatch(); + const [triggerAvailability, setTriggerAvailability] = useState(false); const [selectedTabId, setSelectedTab] = useState(TAB_OVERVIEW_ID); const [serviceFlyoutName, setServiceFlyoutName] = useState(''); const [traceFlyoutId, setTraceFlyoutId] = useState(''); @@ -206,6 +201,7 @@ export function Application(props: AppDetailProps) { ); const tabId = `application-analytics-tab-${appId}`; initializeTabData(dispatch, tabId, NEW_TAB); + callback(switchToEvent); }, [appId]); useEffect(() => { @@ -373,6 +369,8 @@ export function Application(props: AppDetailProps) { setStartTime={setStartTimeForApp} setEndTime={setEndTimeForApp} appBaseQuery={application.baseQuery} + callback={callback} + callbackInApp={callbackInApp} curSelectedTabId={selectedTabId} /> ); @@ -422,13 +420,25 @@ export function Application(props: AppDetailProps) { } }; + const switchToAvailability = () => { + switchToEvent(); + setTriggerAvailability(true); + }; + + const callbackInApp = (childFunc: () => void) => { + if (childFunc && triggerAvailability) { + childFunc(); + setTriggerAvailability(false); + } + }; + const getConfig = () => { return ( diff --git a/dashboards-observability/public/components/application_analytics/components/configuration.tsx b/dashboards-observability/public/components/application_analytics/components/configuration.tsx index c155b115f..c8c4ce2cb 100644 --- a/dashboards-observability/public/components/application_analytics/components/configuration.tsx +++ b/dashboards-observability/public/components/application_analytics/components/configuration.tsx @@ -11,6 +11,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, + EuiLink, EuiPage, EuiPageBody, EuiPageContent, @@ -25,14 +26,14 @@ import { } from '@elastic/eui'; import { ApplicationType } from 'common/types/app_analytics'; import { last } from 'lodash'; -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; interface ConfigProps { appId: string; application: ApplicationType; parentBreadcrumbs: EuiBreadcrumb[]; visWithAvailability: EuiSelectOption[]; - switchToEditViz: (savedVizId: string) => void; + switchToAvailability: () => void; updateApp: (appId: string, updateAppData: Partial, type: string) => void; } @@ -43,12 +44,9 @@ export const Configuration = (props: ConfigProps) => { parentBreadcrumbs, visWithAvailability, updateApp, - switchToEditViz, + switchToAvailability, } = props; const [availabilityVisId, setAvailabilityVisId] = useState(application.availabilityVisId || ''); - useEffect(() => { - switchToEditViz(''); - }, []); const onAvailabilityVisChange = (event: any) => { setAvailabilityVisId(event.target.value); @@ -127,11 +125,20 @@ export const Configuration = (props: ConfigProps) => {

Availability

- + {visWithAvailability.length > 0 ? ( + + ) : ( + switchToAvailability()} + > + Set Availability + + )} diff --git a/dashboards-observability/public/components/application_analytics/components/create.tsx b/dashboards-observability/public/components/application_analytics/components/create.tsx index e247878de..390aa3612 100644 --- a/dashboards-observability/public/components/application_analytics/components/create.tsx +++ b/dashboards-observability/public/components/application_analytics/components/create.tsx @@ -39,7 +39,7 @@ interface CreateAppProps extends AppAnalyticsComponentDeps { dslService: DSLService; pplService: PPLService; setToasts: (title: string, color?: string, text?: ReactChild) => void; - createApp: (app: ApplicationType) => void; + createApp: (app: ApplicationType, type: string) => void; updateApp: (appId: string, updateAppData: Partial, type: string) => void; clearStorage: () => void; existingAppId: string; @@ -126,19 +126,21 @@ export const CreateApp = (props: CreateAppProps) => { const isDisabled = !name || (!query && !selectedTraces.length && !selectedServices.length); - const missingField = () => { - if (isDisabled) { - let popoverContent = ''; + const missingField = (needLog: boolean) => { + let popoverContent = ''; + if (isDisabled || (needLog && !query)) { if (!name) { popoverContent = 'Name is required.'; } else if (!query && !selectedServices.length && !selectedTraces.length) { popoverContent = 'Provide at least one log source, service, entity or trace group.'; + } else if (needLog && !query) { + popoverContent = 'Log source is required to set availability.'; } return

{popoverContent}

; } }; - const onCreate = () => { + const onCreate = (type: string) => { const appData = { name, description, @@ -148,7 +150,7 @@ export const CreateApp = (props: CreateAppProps) => { panelId: '', availabilityVisId: '', }; - createApp(appData); + createApp(appData, type); }; const onUpdate = () => { @@ -235,17 +237,31 @@ export const CreateApp = (props: CreateAppProps) => { - + onCreate('create')} + fill={editMode ? true : false} > {editMode ? 'Save' : 'Create'} + {editMode || ( + + + onCreate('createSetAvailability')} + > + Create and Set Availability + + + + )} diff --git a/dashboards-observability/public/components/application_analytics/helpers/types.tsx b/dashboards-observability/public/components/application_analytics/helpers/types.tsx new file mode 100644 index 000000000..07ac03c9d --- /dev/null +++ b/dashboards-observability/public/components/application_analytics/helpers/types.tsx @@ -0,0 +1,10 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export interface AvailabilityType { + name: string; + color: string; + mainVisId: string; +} diff --git a/dashboards-observability/public/components/application_analytics/helpers/utils.tsx b/dashboards-observability/public/components/application_analytics/helpers/utils.tsx index 970957418..d956fb06b 100644 --- a/dashboards-observability/public/components/application_analytics/helpers/utils.tsx +++ b/dashboards-observability/public/components/application_analytics/helpers/utils.tsx @@ -18,7 +18,10 @@ import { VisualizationType } from '../../../../common/types/custom_panels'; import { NEW_SELECTED_QUERY_TAB, TAB_CREATED_TYPE } from '../../../../common/constants/explorer'; import { APP_ANALYTICS_API_PREFIX } from '../../../../common/constants/application_analytics'; import { HttpSetup } from '../../../../../../src/core/public'; -import { init as initFields, remove as removefields } from '../../event_analytics/redux/slices/field_slice'; +import { + init as initFields, + remove as removefields, +} from '../../event_analytics/redux/slices/field_slice'; import { init as initVisualizationConfig, reset as resetVisualizationConfig, @@ -33,6 +36,7 @@ import { remove as removeQueryResult, } from '../../event_analytics/redux/slices/query_result_slice'; import { addTab, removeTab } from '../../event_analytics/redux/slices/query_tab_slice'; +import { AvailabilityType } from './types'; // Name validation export const isNameValid = (name: string, existingNames: string[]) => { @@ -193,7 +197,7 @@ export const calculateAvailability = async ( application: ApplicationType | ApplicationListType, availabilityVisId: string, setVisWithAvailability: (visList: EuiSelectOption[]) => void -): Promise<{ name: string; color: string; mainVisId: string }> => { +): Promise => { let availability = { name: '', color: '', mainVisId: '' }; const panelId = application.panelId; if (!panelId) return availability; @@ -204,15 +208,18 @@ export const calculateAvailability = async ( const visWithAvailability = []; let availabilityFound = false; for (let i = 0; i < savedVisualizationsIds.length; i++) { - let hasAvailability = false; const visualizationId = savedVisualizationsIds[i]; // Fetches data for visualization const visData = await fetchVisualizationById(http, visualizationId, (value: string) => console.error(value) ); - // If there are thresholds, we get the current value - if (visData.user_configs.dataConfig?.hasOwnProperty('thresholds')) { - const thresholds = visData.user_configs.dataConfig.thresholds.reverse(); + // If there are levels, we get the current value + if (visData.user_configs.availabilityConfig?.hasOwnProperty('level')) { + // For every saved visualization with availability levels we push it to visWithAvailability + // This is used to populate the options in configuration + visWithAvailability.push({ value: visualizationId, text: visData.name }); + + const levels = visData.user_configs.availabilityConfig.level.reverse(); let currValue = Number.MIN_VALUE; const finalQuery = preprocessQuery({ rawQuery: visData.query, @@ -236,72 +243,76 @@ export const calculateAvailability = async ( .catch((err) => { console.error(err); }); - // We check each threshold if it has expression which means it is an availability level - for (let j = 0; j < thresholds.length; j++) { - const threshold = thresholds[j]; - if (threshold.hasOwnProperty('expression')) { - hasAvailability = true; - // If there is an availiabilityVisId selected we only want to compute availability based on that - if (availabilityVisId ? availabilityVisId === visualizationId : true) { - if (threshold.value !== null) { - if (!availabilityFound && threshold.expression) { - const expression = threshold.expression; + for (let j = 0; j < levels.length; j++) { + const level = levels[j]; + // If there is an availiabilityVisId selected we only want to compute availability based on that + if (availabilityVisId ? availabilityVisId === visualizationId : true) { + if (level.value !== null) { + if (currValue === null) { + availability = { + name: '', + color: 'null', + mainVisId: '', + }; + } else { + if (!availabilityFound) { + const expression = level.expression; switch (expression) { case '≥': - if (currValue >= parseFloat(threshold.value)) { + if (currValue >= parseFloat(level.value)) { availability = { - name: threshold.name, - color: threshold.color, + name: level.name, + color: level.color, mainVisId: visualizationId, }; availabilityFound = true; } break; case '≤': - if (currValue <= parseFloat(threshold.value)) { + if (currValue <= parseFloat(level.value)) { availability = { - name: threshold.name, - color: threshold.color, + name: level.name, + color: level.color, mainVisId: visualizationId, }; availabilityFound = true; } break; case '>': - if (currValue > parseFloat(threshold.value)) { + if (currValue > parseFloat(level.value)) { availability = { - name: threshold.name, - color: threshold.color, + name: level.name, + color: level.color, mainVisId: visualizationId, }; availabilityFound = true; } break; case '<': - if (currValue < parseFloat(threshold.value)) { + if (currValue < parseFloat(level.value)) { availability = { - name: threshold.name, - color: threshold.color, + name: level.name, + color: level.color, mainVisId: visualizationId, }; availabilityFound = true; } break; case '=': - if (currValue === parseFloat(threshold.value)) { + if (currValue === parseFloat(level.value)) { availability = { - name: threshold.name, - color: threshold.color, + name: level.name, + color: level.color, mainVisId: visualizationId, }; availabilityFound = true; } break; case '≠': - if (currValue !== parseFloat(threshold.value)) { + if (currValue !== parseFloat(level.value)) { availability = { - name: threshold.name, - color: threshold.color, + name: level.name, + color: level.color, mainVisId: visualizationId, }; availabilityFound = true; @@ -314,12 +325,10 @@ export const calculateAvailability = async ( } } } - // For every saved visualization with availability levels we push it to visWithAvailability - if (hasAvailability) { - // This is used to populate the options in configuration - visWithAvailability.push({ value: visualizationId, text: visData.name }); - } } setVisWithAvailability(visWithAvailability); + if (!availabilityFound && visWithAvailability.length > 0) { + return { name: '', color: 'undefined', mainVisId: '' }; + } return availability; }; diff --git a/dashboards-observability/public/components/application_analytics/home.tsx b/dashboards-observability/public/components/application_analytics/home.tsx index d3074da8b..1d837d437 100644 --- a/dashboards-observability/public/components/application_analytics/home.tsx +++ b/dashboards-observability/public/components/application_analytics/home.tsx @@ -67,6 +67,7 @@ export const Home = (props: HomeProps) => { chrome, notifications, } = props; + const [triggerSwitchToEvent, setTriggerSwitchToEvent] = useState(0); const dispatch = useDispatch(); const [applicationList, setApplicationList] = useState([]); const [toasts, setToasts] = useState([]); @@ -145,7 +146,14 @@ export const Home = (props: HomeProps) => { setQueryWithStorage(''); }; - const createPanelForApp = (applicationId: string, appName: string) => { + const moveToApp = (id: string, type: string) => { + window.location.assign(`${last(parentBreadcrumbs)!.href}application_analytics/${id}`); + if (type === 'createSetAvailability') { + setTriggerSwitchToEvent(2); + } + }; + + const createPanelForApp = (applicationId: string, appName: string, type: string) => { return http .post(`${CUSTOM_PANELS_API_PREFIX}/panels`, { body: JSON.stringify({ @@ -154,7 +162,7 @@ export const Home = (props: HomeProps) => { }), }) .then((res) => { - updateApp(applicationId, { panelId: res.newPanelId }, 'addPanel'); + updateApp(applicationId, { panelId: res.newPanelId }, type); }) .catch((err) => { setToast( @@ -203,7 +211,7 @@ export const Home = (props: HomeProps) => { const mainVisIdStore: Record = {}; for (let i = 0; i < res.data.length; i++) { mainVisIdStore[res.data[i].id] = res.data[i].availability.mainVisId; - res.data[i].availability = { name: 'loading', color: '', mainVisId: '' }; + res.data[i].availability = { name: '', color: 'loading', mainVisId: '' }; } setApplicationList(res.data); for (let i = res.data.length - 1; i > -1; i--) { @@ -228,7 +236,7 @@ export const Home = (props: HomeProps) => { }; // Create a new application - const createApp = (application: ApplicationType) => { + const createApp = (application: ApplicationType, type: string) => { const toast = isNameValid( application.name, applicationList.map((obj) => obj.name) @@ -252,7 +260,7 @@ export const Home = (props: HomeProps) => { body: JSON.stringify(requestBody), }) .then(async (res) => { - createPanelForApp(res.newAppId, application.name); + createPanelForApp(res.newAppId, application.name, type); setToast(`Application "${application.name}" successfully created!`); clearStorage(); }) @@ -314,11 +322,10 @@ export const Home = (props: HomeProps) => { if (type === 'update') { setToast('Application successfully updated.'); clearStorage(); + moveToApp(res.updatedAppId, type); } - if (type !== 'editAvailability') { - window.location.assign( - `${last(parentBreadcrumbs)!.href}application_analytics/${res.updatedAppId}` - ); + if (type.startsWith('create')) { + moveToApp(res.updatedAppId, type); } }) .catch((err) => { @@ -355,6 +362,13 @@ export const Home = (props: HomeProps) => { }); }; + const callback = (childFunc: () => void) => { + if (childFunc && triggerSwitchToEvent > 0) { + childFunc(); + setTriggerSwitchToEvent(triggerSwitchToEvent - 1); + } + }; + return (
{ renameApplication={renameApp} deleteApplication={deleteApp} clearStorage={clearStorage} + moveToApp={moveToApp} {...commonProps} /> @@ -412,6 +427,7 @@ export const Home = (props: HomeProps) => { notifications={notifications} setToasts={setToast} updateApp={updateApp} + callback={callback} {...commonProps} /> )} diff --git a/dashboards-observability/public/components/common/search/date_picker.tsx b/dashboards-observability/public/components/common/search/date_picker.tsx index 2d3ec8b19..75087c9f1 100644 --- a/dashboards-observability/public/components/common/search/date_picker.tsx +++ b/dashboards-observability/public/components/common/search/date_picker.tsx @@ -11,7 +11,7 @@ import { uiSettingsService } from '../../../../common/utils'; export function DatePicker(props: IDatePickerProps) { const { startTime, endTime, handleTimePickerChange, handleTimeRangePickerRefresh } = props; - const handleTimeChange = (e) => handleTimePickerChange([e.start, e.end]); + const handleTimeChange = (e: any) => handleTimePickerChange([e.start, e.end]); return ( void; setIsOutputStale: () => void; handleTimePickerChange: (timeRange: string[]) => any; + handleTimeRangePickerRefresh: () => any; } export const Search = (props: any) => { @@ -119,10 +120,10 @@ export const Search = (props: any) => { const liveButton = ( ); @@ -192,10 +193,7 @@ export const Search = (props: any) => { )} {isLiveTailOn && ( - + )} {showSaveButton && searchBarConfigs[selectedSubTabId]?.showSaveButton && ( diff --git a/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap b/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap index aa8a1a9f4..5a513f839 100644 --- a/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap +++ b/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap @@ -170,6 +170,12 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "mapTo": "layoutConfig", "name": "Layout", }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, ], }, "fullLabel": "Bar", @@ -378,6 +384,12 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "mapTo": "layoutConfig", "name": "Layout", }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, ], }, "fullLabel": "Bar", @@ -609,6 +621,12 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "mapTo": "layoutConfig", "name": "Layout", }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, ], }, "fullLabel": "Bar", @@ -983,6 +1001,12 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "mapTo": "layoutConfig", "name": "Layout", }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, ], }, "fullLabel": "Line", @@ -1208,6 +1232,12 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "mapTo": "layoutConfig", "name": "Layout", }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, ], }, "fullLabel": "Line", @@ -1487,6 +1517,12 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "mapTo": "layoutConfig", "name": "Layout", }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, ], }, "fullLabel": "Line", diff --git a/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx b/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx index e406a8d85..a7fd42262 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ /* eslint-disable react-hooks/exhaustive-deps */ -/* eslint-disable no-console */ import './explorer.scss'; import React, { useState, useMemo, useEffect, useRef, useCallback, ReactElement } from 'react'; @@ -50,11 +49,15 @@ import { EVENT_ANALYTICS_DOCUMENTATION_URL, TAB_EVENT_ID, TAB_CHART_ID, - INDEX, - FINAL_QUERY, + DEFAULT_AVAILABILITY_QUERY, DATE_PICKER_FORMAT, } from '../../../../common/constants/explorer'; -import { PPL_STATS_REGEX, PPL_NEWLINE_REGEX, LIVE_OPTIONS, LIVE_END_TIME } from '../../../../common/constants/shared'; +import { + PPL_STATS_REGEX, + PPL_NEWLINE_REGEX, + LIVE_OPTIONS, + LIVE_END_TIME, +} from '../../../../common/constants/shared'; import { getIndexPatternFromRawQuery, preprocessQuery, buildQuery } from '../../../../common/utils'; import { useFetchEvents, useFetchVisualizations } from '../hooks'; import { changeQuery, changeDateRange, selectQueries } from '../redux/slices/query_slice'; @@ -71,10 +74,7 @@ import { change as updateVizConfig } from '../redux/slices/viualization_config_s import { IExplorerProps, IVisualizationContainerProps } from '../../../../common/types/explorer'; import { TabContext } from '../hooks'; import { getVizContainerProps } from '../../visualizations/charts/helpers'; -import { - parseGetSuggestions, - onItemSelect, -} from '../../common/search/autocomplete_logic'; +import { parseGetSuggestions, onItemSelect } from '../../common/search/autocomplete_logic'; import { formatError } from '../utils'; import { sleep } from '../../common/live_tail/live_tail_button'; @@ -103,6 +103,8 @@ export const Explorer = ({ endTime, setStartTime, setEndTime, + callback, + callbackInApp, }: IExplorerProps) => { const dispatch = useDispatch(); const requestParams = { tabId }; @@ -138,8 +140,11 @@ export const Explorer = ({ const [liveHits, setLiveHits] = useState(0); const [browserTabFocus, setBrowserTabFocus] = useState(true); const [liveTimestamp, setLiveTimestamp] = useState(DATE_PICKER_FORMAT); + const [triggerAvailability, setTriggerAvailability] = useState(false); const queryRef = useRef(); + const appBasedRef = useRef(''); + appBasedRef.current = appBaseQuery; const selectedPanelNameRef = useRef(''); const explorerFieldsRef = useRef(); const isLiveTailOnRef = useRef(false); @@ -159,7 +164,7 @@ export const Explorer = ({ const momentStart = dateMath.parse(start)!; const momentEnd = dateMath.parse(end)!; const diffSeconds = momentEnd.unix() - momentStart.unix(); - + // less than 1 second if (diffSeconds <= 1) minInterval = 'ms'; // less than 2 minutes @@ -174,7 +179,7 @@ export const Explorer = ({ else if (diffSeconds <= 86400 * 93) minInterval = 'w'; // less than 1 year else if (diffSeconds <= 86400 * 366) minInterval = 'M'; - + setTimeIntervalOptions([ { text: 'Auto', value: 'auto_' + minInterval }, ...TIME_INTERVAL_OPTIONS, @@ -182,11 +187,10 @@ export const Explorer = ({ }; useEffect(() => { - document.addEventListener("visibilitychange", function() { + document.addEventListener('visibilitychange', function () { if (document.hidden) { setBrowserTabFocus(false); - } - else { + } else { setBrowserTabFocus(true); } }); @@ -199,7 +203,7 @@ export const Explorer = ({ timeField: string, isLiveQuery: boolean ) => { - const fullQuery = buildQuery(appBaseQuery, curQuery![RAW_QUERY]); + const fullQuery = buildQuery(appBasedRef.current, curQuery![RAW_QUERY]); if (isEmpty(fullQuery)) return ''; return preprocessQuery({ rawQuery: fullQuery, @@ -224,7 +228,7 @@ export const Explorer = ({ const currQuery = appLogEvents ? objectData?.query.replace(appBaseQuery + '| ', '') : objectData?.query || ''; - + if (appLogEvents) { if (objectData?.selected_date_range?.start && objectData?.selected_date_range?.end) { setStartTime(objectData.selected_date_range.start); @@ -298,9 +302,9 @@ export const Explorer = ({ indexPattern: string ): Promise => await timestampUtils.getTimestamp(indexPattern); - const fetchData = async (startTime?: string, endTime?: string) => { + const fetchData = async (startingTime?: string, endingTime?: string) => { const curQuery = queryRef.current; - const rawQueryStr = buildQuery(appBaseQuery, curQuery![RAW_QUERY]); + const rawQueryStr = buildQuery(appBasedRef.current, curQuery![RAW_QUERY]); const curIndex = getIndexPatternFromRawQuery(rawQueryStr); if (isEmpty(rawQueryStr)) return; @@ -323,16 +327,16 @@ export const Explorer = ({ } } - if ((isEqual(typeof startTime, 'undefined')) && (isEqual(typeof endTime, 'undefined'))) { - startTime = curQuery![SELECTED_DATE_RANGE][0]; - endTime = curQuery![SELECTED_DATE_RANGE][1]; + if (isEqual(typeof startingTime, 'undefined') && isEqual(typeof endingTime, 'undefined')) { + startingTime = curQuery![SELECTED_DATE_RANGE][0]; + endingTime = curQuery![SELECTED_DATE_RANGE][1]; } // compose final query const finalQuery = composeFinalQuery( curQuery, - startTime, - endTime, + startingTime!, + endingTime!, curTimestamp, isLiveTailOnRef.current ); @@ -348,12 +352,12 @@ export const Explorer = ({ ); // search - if (rawQueryStr.match(PPL_STATS_REGEX)) { + if (finalQuery.match(PPL_STATS_REGEX)) { getVisualizations(); getAvailableFields(`search source=${curIndex}`); } else { findAutoInterval(startTime, endTime); - if (isLiveTailOnRef.current){ + if (isLiveTailOnRef.current) { getLiveTail(undefined, (error) => { const formattedError = formatError(error.name, error.message, error.body.message); notifications.toasts.addError(formattedError, { @@ -372,7 +376,7 @@ export const Explorer = ({ } // for comparing usage if for the same tab, user changed index from one to another - if (!isLiveTailOnRef.current){ + if (!isLiveTailOnRef.current) { setPrevIndex(curTimestamp); if (!queryRef.current!.isLoaded) { dispatch( @@ -394,6 +398,25 @@ export const Explorer = ({ await getSavedDataById(objectId); }; + const prepareAvailability = async () => { + setSelectedContentTab(TAB_CHART_ID); + setTriggerAvailability(true); + await setTempQuery(DEFAULT_AVAILABILITY_QUERY); + await updateQueryInStore(DEFAULT_AVAILABILITY_QUERY); + await handleTimeRangePickerRefresh(true); + }; + + useEffect(() => { + if (!isEmpty(appBasedRef.current)) { + if (callback) { + callback(() => prepareAvailability()); + } + if (callbackInApp) { + callbackInApp(() => prepareAvailability()); + } + } + }, [appBasedRef.current]); + useEffect(() => { if (queryRef.current!.isLoaded) return; let objectId; @@ -413,29 +436,10 @@ export const Explorer = ({ if (appLogEvents) { if (savedObjectId) { updateTabData(savedObjectId); - } else { - setTempQuery(''); - emptyTab(); } } }, [savedObjectId]); - const emptyTab = async () => { - await dispatch( - changeQuery({ - tabId, - query: { - [RAW_QUERY]: '', - [FINAL_QUERY]: '', - [INDEX]: '', - [SELECTED_TIMESTAMP]: '', - [SAVED_OBJECT_ID]: '', - }, - }) - ); - await fetchData(); - }; - const handleAddField = (field: IField) => toggleFields(field, AVAILABLE_FIELDS, SELECTED_FIELDS); const handleRemoveField = (field: IField) => @@ -467,8 +471,8 @@ export const Explorer = ({ ); }; - const handleTimeRangePickerRefresh = () => { - handleQuerySearch(); + const handleTimeRangePickerRefresh = (availability?: boolean) => { + handleQuerySearch(availability); }; /** @@ -535,17 +539,19 @@ export const Explorer = ({ }; const totalHits: number = useMemo(() => { - if (isLiveTailOn && countDistribution?.data) { - let hits = reduce( - countDistribution['data']['count()'], - (sum, n) => { - return sum + n; - }, - liveHits - ) - setLiveHits(hits); - return hits - }} , [countDistribution?.data]); + if (isLiveTailOn && countDistribution?.data) { + const hits = reduce( + countDistribution.data['count()'], + (sum, n) => { + return sum + n; + }, + liveHits + ); + setLiveHits(hits); + return hits; + } + return 0; + }, [countDistribution?.data]); const getMainContent = () => { return ( @@ -637,24 +643,24 @@ export const Explorer = ({

{isLiveTailOnRef.current && ( - <> - - - - -   Live streaming - - - { } } /> - - - since {liveTimestamp} - - - + <> + + + + +   Live streaming + + + {}} + /> + + since {liveTimestamp} + + + )} void) => { + if (childFunc && triggerAvailability) { + childFunc(); + setTriggerAvailability(false); + } + }; + const getExplorerVis = () => { return ( ); }; @@ -786,18 +801,23 @@ export const Explorer = ({ ); }; - const handleQuerySearch = useCallback(async () => { - // clear previous selected timestamp when index pattern changes - if ( - !isEmpty(tempQuery) && - !isEmpty(query[RAW_QUERY]) && - isIndexPatternChanged(tempQuery, query[RAW_QUERY]) - ) { - await updateCurrentTimeStamp(''); - } - await updateQueryInStore(tempQuery); - fetchData(); - }, [tempQuery, query[RAW_QUERY]]); + const handleQuerySearch = useCallback( + async (availability?: boolean) => { + // clear previous selected timestamp when index pattern changes + if ( + !isEmpty(tempQuery) && + !isEmpty(query[RAW_QUERY]) && + isIndexPatternChanged(tempQuery, query[RAW_QUERY]) + ) { + await updateCurrentTimeStamp(''); + } + if (availability !== true) { + await updateQueryInStore(tempQuery); + } + fetchData(); + }, + [tempQuery, query[RAW_QUERY]] + ); const handleQueryChange = async (newQuery: string) => { setTempQuery(newQuery); @@ -1019,21 +1039,21 @@ export const Explorer = ({ const liveTailLoop = async ( name: string, - startTime: string, - endTime: string, + startingTime: string, + endingTime: string, delayTime: number ) => { setLiveTailName(name); - setLiveTailTabId(curSelectedTabId.current); + setLiveTailTabId((curSelectedTabId.current as unknown) as string); setIsLiveTailOn(true); setToast('Live tail On', 'success'); setIsLiveTailPopoverOpen(false); - setLiveTimestamp(dateMath.parse(endTime)?.utc().format(DATE_PICKER_FORMAT)); + setLiveTimestamp(dateMath.parse(endingTime)?.utc().format(DATE_PICKER_FORMAT) || ''); setLiveHits(0); await sleep(2000); const curLiveTailname = liveTailNameRef.current; while (isLiveTailOnRef.current === true && curLiveTailname === liveTailNameRef.current) { - handleLiveTailSearch(startTime, endTime); + handleLiveTailSearch(startingTime, endingTime); if (liveTailTabIdRef.current !== curSelectedTabId.current) { setIsLiveTailOn(false); isLiveTailOnRef.current = false; @@ -1050,36 +1070,36 @@ export const Explorer = ({ setLiveHits(0); setIsLiveTailPopoverOpen(false); if (isLiveTailOnRef.current) setToast('Live tail Off', 'danger'); - } + }; useEffect(() => { - if ((isEqual(selectedContentTabId, TAB_CHART_ID)) || (!browserTabFocus)) { + if (isEqual(selectedContentTabId, TAB_CHART_ID) || !browserTabFocus) { stopLive(); } }, [selectedContentTabId, browserTabFocus]); - //stop live tail if the page is moved using breadcrumbs - let lastUrl = location.href; + // stop live tail if the page is moved using breadcrumbs + let lastUrl = location.href; new MutationObserver(() => { const url = location.href; - if (url !== lastUrl) { - lastUrl = url; - stopLive(); - } - }).observe(document, {subtree: true, childList: true}); + if (url !== lastUrl) { + lastUrl = url; + stopLive(); + } + }).observe(document, { subtree: true, childList: true }); const popoverItems: ReactElement[] = LIVE_OPTIONS.map((e) => { return ( - { - liveTailLoop(e.label, e.startTime, LIVE_END_TIME, e.delayTime); - }} - data-test-subj={'eventLiveTail__delay'+e.label} - > - {e.label} - - ) + { + liveTailLoop(e.label, e.startTime, LIVE_END_TIME, e.delayTime); + }} + data-test-subj={'eventLiveTail__delay' + e.label} + > + {e.label} + + ); }); const dateRange = @@ -1090,9 +1110,9 @@ export const Explorer = ({ : [startTime, endTime]; const handleLiveTailSearch = useCallback( - async (startTime: string, endTime: string) => { + async (startingTime: string, endingTime: string) => { await updateQueryInStore(tempQuery); - fetchData(startTime, endTime); + fetchData(startingTime, endingTime); }, [tempQuery] ); @@ -1153,4 +1173,4 @@ export const Explorer = ({
); -}; \ No newline at end of file +}; diff --git a/dashboards-observability/public/components/event_analytics/explorer/sidebar/sidebar.tsx b/dashboards-observability/public/components/event_analytics/explorer/sidebar/sidebar.tsx index 9a49ca926..c949c9e49 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/sidebar/sidebar.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/sidebar/sidebar.tsx @@ -15,6 +15,7 @@ import { Field } from './field'; import { IExplorerFields, IField } from '../../../../../common/types/explorer'; interface ISidebarProps { + query: string; explorerFields: IExplorerFields; explorerData: any; selectedTimestamp: string; diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap index 69eda470d..065ec0e9c 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap @@ -282,6 +282,12 @@ exports[`Config panel component Renders config panel with visualization data 1`] "mapTo": "layoutConfig", "name": "Layout", }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, ], }, "fullLabel": "Bar", @@ -452,6 +458,12 @@ exports[`Config panel component Renders config panel with visualization data 1`] "mapTo": "layoutConfig", "name": "Layout", }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, ], }, "fullLabel": "Bar", @@ -574,6 +586,12 @@ exports[`Config panel component Renders config panel with visualization data 1`] "mapTo": "layoutConfig", "name": "Layout", }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, ], }, "fullLabel": "Line", @@ -1030,6 +1048,12 @@ exports[`Config panel component Renders config panel with visualization data 1`] "mapTo": "layoutConfig", "name": "Layout", }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, ], }, "fullLabel": "Bar", @@ -1197,6 +1221,12 @@ exports[`Config panel component Renders config panel with visualization data 1`] "mapTo": "layoutConfig", "name": "Layout", }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, ], }, "fullLabel": "Bar", @@ -1374,6 +1404,12 @@ exports[`Config panel component Renders config panel with visualization data 1`] "mapTo": "layoutConfig", "name": "Layout", }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, ], } } @@ -1481,6 +1517,12 @@ exports[`Config panel component Renders config panel with visualization data 1`] "mapTo": "layoutConfig", "name": "Layout", }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, ], }, "fullLabel": "Bar", @@ -1652,6 +1694,12 @@ exports[`Config panel component Renders config panel with visualization data 1`] "mapTo": "layoutConfig", "name": "Layout", }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, ], } } @@ -1794,6 +1842,12 @@ exports[`Config panel component Renders config panel with visualization data 1`] "mapTo": "layoutConfig", "name": "Layout", }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, ], } } @@ -2069,9 +2123,10 @@ exports[`Config panel component Renders config panel with visualization data 1`] className="euiPanel euiPanel--paddingSmall euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow" > , + "id": "availability-panel", + "name": "Availability", + }, ] } >
+ + +
@@ -6632,32 +9216,46 @@ exports[`Config panel component Renders config panel with visualization data 1`]
- - Orientation + Show Legend - -
- + + Show Legend + + +
- -
-
-
-

- Select a field -

- + -
-
-
- -
- +
+ + + + + + +
-
- - -
- - + + Hidden + + + + + + +
+ + + @@ -6905,32 +9483,46 @@ exports[`Config panel component Renders config panel with visualization data 1`] className="euiSpacer euiSpacer--s" /> - - Mode + Position - -
- + + Position + + +
- -
-
-
-

- Select a field -

- + + Right + + + + + + + + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+ + +

+ Orientation +

+
+ +
+ + +
+ + + Orientation + + +
+ + + + + + + + + + +
+
+
+ + +
+ + + +

+ Mode +

+
+ +
+ + +
+ + + Mode + + +
+ + + + + + + + + + +
+
+
+ + +
+ + + +

+ Rotate bar labels +

+
+ +
+ + + +
+ +
+ +
+ + + + + + +
, + } + } + value={0} + > + + + + + + + + +
, + } + } + value={0} + > + + + + + + + + +
, + } + } + value={0} + > + + + + + + + + +
, + } + } + value={0} + > + + + + + + + + +
, + } + } + value={0} + > + + +
+ + + + +
+ +
+ + + +
+
+ + + + +
+
+
+
+
+
+ + + + +
+ + + +

+ Group width +

+
+ +
+ + + +
+ +
+ + + +
+
+
+ + + +
+
+ + -
- + + +
- + + +
+
+
+ + +
+ + + +

+ Bar width +

+
+ +
+ + + +
+ +
+ + + +
+
+
+ + + +
- - - + + + + +
+
+
+
+
+
+ + + + +
+ + + +

+ Line width +

+
+ +
+ + + +
+ +
+ + + +
+
+
+ + + +
+
+ + + + +
+
+
+
+
+
+ + + + +
+ + + +

+ Fill Opacity +

+
+ +
+ + + +
+ +
+ + + +
+
+
+ + + +
+
+ + + +
- -
-
- - -
- - +
+ + + +
+ + + + +
+ +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+ + + + + @@ -7184,7 +13310,7 @@ exports[`Config panel component Renders config panel with visualization data 1`]
- +
diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_bar_chart_styles.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_bar_chart_styles.tsx new file mode 100644 index 000000000..7098ae117 --- /dev/null +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_bar_chart_styles.tsx @@ -0,0 +1,94 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useMemo, useCallback } from 'react'; +import { EuiAccordion, EuiSpacer } from '@elastic/eui'; +import { ButtonGroupItem } from './config_button_group'; +import { IConfigPanelOptionSection } from '../../../../../../../../common/types/explorer'; + +export const ConfigBarChartStyles = ({ + visualizations, + schemas, + vizState, + handleConfigChange, + sectionName, + sectionId = 'chartStyles' +}: any) => { + const { data } = visualizations; + const { data: vizData = {}, metadata: { fields = [] } = {} } = data?.rawVizData; + + const handleConfigurationChange = useCallback( + (stateFieldName) => { + return (changes) => { + handleConfigChange({ + ...vizState, + [stateFieldName]: changes, + }); + }; + }, + [handleConfigChange, vizState] + ); + + /* To update the schema options based on current style mode selection */ + const currentSchemas = useMemo(() => { + if (vizState?.orientation === 'h') { + return schemas.filter((schema: IConfigPanelOptionSection) => schema.mapTo !== 'rotateBarLabels'); + } + return schemas; + }, [vizState]); + + const dimensions = useMemo(() => + currentSchemas.map((schema: IConfigPanelOptionSection, index: number) => { + let params; + const DimensionComponent = schema.component || ButtonGroupItem; + + const createDimensionComponent = (dimProps) => ( + <> + + + + ) + if (schema.eleType === 'buttons') { + params = { + title: schema.name, + legend: schema.name, + groupOptions: schema?.props?.options.map((btn: { name: string }) => ({ ...btn, label: btn.name })), + idSelected: vizState[schema.mapTo] || schema?.props?.defaultSelections[0]?.id, + handleButtonChange: handleConfigurationChange(schema.mapTo), + vizState, + ...schema.props, + }; + return createDimensionComponent(params); + } + + if (schema.eleType === 'slider') { + params = { + minRange: schema?.props?.min || 0, + maxRange: schema.props.max, + step: schema?.props?.step || 1, + title: schema.name, + currentRange: vizState[schema.mapTo] || schema?.defaultState, + ticks: schema?.props?.ticks, + showTicks: schema?.props?.showTicks || false, + handleSliderChange: handleConfigurationChange(schema.mapTo), + vizState, + ...schema.props, + }; + return createDimensionComponent(params); + } + }).filter(item => item) + , [schemas, vizState, handleConfigurationChange]); + + return ( + + {dimensions} + + ); +}; diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_button_group.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_button_group.tsx index 0955decc0..31d8ffaac 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_button_group.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_button_group.tsx @@ -4,8 +4,7 @@ */ import React from 'react'; -import { uniqueId } from 'lodash'; -import { EuiTitle, EuiSpacer, EuiButtonGroup } from '@elastic/eui'; +import { EuiTitle, EuiSpacer, EuiButtonGroup, htmlIdGenerator } from '@elastic/eui'; interface ToggleButtonOptions { id: string; label: string; @@ -20,22 +19,21 @@ interface ToggleGroupProps { export const ButtonGroupItem: React.FC = ({ title, legend, groupOptions, idSelected, handleButtonChange }) => ( - <> - -

{title}

-
- -
- -
- + <> + +

{title}

+
+ + + + ); diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_color_theme.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_color_theme.tsx new file mode 100644 index 000000000..af429b4f3 --- /dev/null +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_color_theme.tsx @@ -0,0 +1,125 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useCallback } from 'react'; +import { + EuiButton, + EuiAccordion, + EuiFormRow, + EuiColorPicker, + EuiSpacer, + EuiIcon, + EuiFlexGroup, + EuiFlexItem, + htmlIdGenerator, + EuiComboBox, +} from '@elastic/eui'; +import { isEmpty } from 'lodash'; +import { ADD_BUTTON_TEXT } from '../../../../../../../../common/constants/explorer'; + + +export const ConfigColorTheme = ({ + visualizations, + schemas, + vizState = [], + handleConfigChange, + sectionName = 'Color Theme', +}: any) => { + const { data } = visualizations; + const { data: vizData = {}, metadata: { fields = [] } = {} } = data?.rawVizData; + + const addButtonText = ADD_BUTTON_TEXT; + const getColorThemeRow = () => ({ + ctid: htmlIdGenerator('ct')(), + name: '', + color: '#FC0505', + }) + + const options = fields.map((item) => ({ + ...item, + label: item.name, + })); + + const getUpdatedOptions = () => options.filter(option => !vizState.some(vizOpt => + option.name === vizOpt?.name?.name + )) + + const handleAddColorTheme = useCallback(() => { + const res = isEmpty(vizState) ? [] : vizState; + handleConfigChange([getColorThemeRow(), ...res]); + }, [vizState, handleConfigChange]); + + const handleColorThemeChange = useCallback( + (ctId, ctName) => (event) => { + handleConfigChange([ + ...vizState.map((ct) => { + if (ctId !== ct.ctid) return ct; + return { + ...ct, + [ctName]: (ctName === 'color' ? event : event[0]) || '', + }; + }), + ]); + }, + [vizState, handleConfigChange] + ); + + const handleColorThemeDelete = useCallback( + (ctId) => (event) => + handleConfigChange([...vizState.filter((ct) => ct.ctid !== ctId)]), + [vizState, handleConfigChange] + ); + return ( + + + {addButtonText} + + + {!isEmpty(vizState) && + vizState.map((ct) => { + return ( + <> + + + + + + + + + + + + + + + + + + + + + ); + })} + + ); +}; diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_style_slider.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_style_slider.tsx new file mode 100644 index 000000000..c90fd4180 --- /dev/null +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_style_slider.tsx @@ -0,0 +1,48 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { ReactNode } from 'react'; +import { EuiTitle, EuiSpacer, EuiRange, htmlIdGenerator, } from '@elastic/eui'; + +export interface EuiRangeTick { + value: number; + label: ReactNode; +} + +interface Props { + title: string; + currentRange: string; + minRange?: number; + maxRange: number; + showTicks?: boolean; + ticks?: EuiRangeTick[]; + step: number; + handleSliderChange: (e: React.ChangeEvent | React.MouseEvent) => void; +} + +export const SliderConfig: React.FC = ({ + title, currentRange, handleSliderChange, minRange, maxRange, showTicks, ticks, step +}) => ( + <> + +

{title}

+
+ + handleSliderChange(e.target.value)} + showTicks={showTicks} + ticks={ticks} + step={step} + compressed + showInput + /> + +); diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap index 9a1ab2672..1d19db62f 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap @@ -202,6 +202,7 @@ exports[`Bar component Renders bar component 1`] = ` "userConfigs": Object {}, }, "vis": Object { + "barWidth": 0.97, "category": "Visualizations", "categoryAxis": "xaxis", "component": [Function], @@ -235,60 +236,197 @@ exports[`Bar component Renders bar component 1`] = ` }, Object { "editor": [Function], - "id": "chart_options", - "mapTo": "chartOptions", - "name": "Chart options", + "id": "legend", + "mapTo": "legend", + "name": "Legend", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, + "mapTo": "showLegend", + "name": "Show Legend", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, + }, + Object { + "component": null, + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, + }, + ], + }, + Object { + "editor": [Function], + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", + "schemas": Array [ + Object { + "component": [Function], + "eleType": "buttons", "mapTo": "orientation", "name": "Orientation", "props": Object { "defaultSelections": Array [ Object { + "id": "v", "name": "Vertical", - "orientationId": "v", }, ], - "dropdownList": Array [ + "options": Array [ Object { + "id": "v", "name": "Vertical", - "orientationId": "v", }, Object { + "id": "h", "name": "Horizontal", - "orientationId": "h", }, ], }, }, Object { - "component": null, - "isSingleSelection": true, + "component": [Function], + "eleType": "buttons", "mapTo": "mode", "name": "Mode", "props": Object { "defaultSelections": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, Object { - "modeId": "stack", + "id": "stack", "name": "Stack", }, ], }, }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateBarLabels", + "name": "Rotate bar labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, + Object { + "component": [Function], + "defaultState": 0.7, + "eleType": "slider", + "mapTo": "groupWidth", + "name": "Group width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 0.97, + "eleType": "slider", + "mapTo": "barWidth", + "name": "Bar width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 1, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 80, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill Opacity", + "props": Object { + "max": 100, + }, + }, ], }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color Theme", + "schemas": Array [], + }, ], }, Object { @@ -306,17 +444,24 @@ exports[`Bar component Renders bar component 1`] = ` }, ], }, + "fillOpacity": 80, "fullLabel": "Bar", + "groupWidth": 0.7, "icon": [Function], "iconType": "visBarVerticalStacked", "id": "bar", "label": "Bar", + "labelAngle": 0, + "legendPosition": "v", + "lineWidth": 1, + "mode": "group", "name": "bar", "orientation": "v", "selection": Object { "dataLoss": "nothing", }, "seriesAxis": "yaxis", + "showLegend": "show", "type": "bar", "visConfig": Object { "config": Object { @@ -350,14 +495,11 @@ exports[`Bar component Renders bar component 1`] = ` Array [ Object { "marker": Object { - "color": Array [ - "#3CA1C7", - "#8C55A3", - "#DB748A", - "#F2BE4B", - "#68CCC2", - "#2A7866", - ], + "color": "rgba(60,161,199,0.4)", + "line": Object { + "color": "#3CA1C7", + "width": 1, + }, }, "name": "count()", "orientation": "v", @@ -383,11 +525,16 @@ exports[`Bar component Renders bar component 1`] = ` } layout={ Object { - "barmode": "", + "bargap": 0.30000000000000004, + "bargroupgap": 0.030000000000000027, + "barmode": "group", "colorway": Array [ "#8C55A3", ], "height": 220, + "legend": Object { + "orientation": "v", + }, "margin": Object { "b": 15, "l": 60, @@ -397,6 +544,10 @@ exports[`Bar component Renders bar component 1`] = ` }, "showlegend": true, "title": "", + "xaxis": Object { + "automargin": true, + "tickangle": 0, + }, } } > @@ -410,14 +561,11 @@ exports[`Bar component Renders bar component 1`] = ` Array [ Object { "marker": Object { - "color": Array [ - "#3CA1C7", - "#8C55A3", - "#DB748A", - "#F2BE4B", - "#68CCC2", - "#2A7866", - ], + "color": "rgba(60,161,199,0.4)", + "line": Object { + "color": "#3CA1C7", + "width": 1, + }, }, "name": "count()", "orientation": "v", @@ -446,15 +594,16 @@ exports[`Bar component Renders bar component 1`] = ` layout={ Object { "autosize": true, - "barmode": "", + "bargap": 0.30000000000000004, + "bargroupgap": 0.030000000000000027, + "barmode": "group", "colorway": Array [ "#8C55A3", ], "height": 220, "hovermode": "closest", "legend": Object { - "orientation": "h", - "traceorder": "normal", + "orientation": "v", }, "margin": Object { "b": 15, @@ -467,9 +616,7 @@ exports[`Bar component Renders bar component 1`] = ` "title": "", "xaxis": Object { "automargin": true, - "rangemode": "normal", - "showgrid": true, - "zeroline": false, + "tickangle": 0, }, "yaxis": Object { "rangemode": "normal", diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/data_table.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/data_table.test.tsx.snap index f6f8243c1..6031f476d 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/data_table.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/data_table.test.tsx.snap @@ -185,6 +185,7 @@ exports[`Data table component Renders data table component 1`] = ` "userConfigs": Object {}, }, "vis": Object { + "barWidth": 0.97, "category": "Visualizations", "categoryAxis": "xaxis", "component": [Function], @@ -218,60 +219,197 @@ exports[`Data table component Renders data table component 1`] = ` }, Object { "editor": [Function], - "id": "chart_options", - "mapTo": "chartOptions", - "name": "Chart options", + "id": "legend", + "mapTo": "legend", + "name": "Legend", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, + "mapTo": "showLegend", + "name": "Show Legend", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, + }, + Object { + "component": null, + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, + }, + ], + }, + Object { + "editor": [Function], + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", + "schemas": Array [ + Object { + "component": [Function], + "eleType": "buttons", "mapTo": "orientation", "name": "Orientation", "props": Object { "defaultSelections": Array [ Object { + "id": "v", "name": "Vertical", - "orientationId": "v", }, ], - "dropdownList": Array [ + "options": Array [ Object { + "id": "v", "name": "Vertical", - "orientationId": "v", }, Object { + "id": "h", "name": "Horizontal", - "orientationId": "h", }, ], }, }, Object { - "component": null, - "isSingleSelection": true, + "component": [Function], + "eleType": "buttons", "mapTo": "mode", "name": "Mode", "props": Object { "defaultSelections": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, Object { - "modeId": "stack", + "id": "stack", "name": "Stack", }, ], }, }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateBarLabels", + "name": "Rotate bar labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, + Object { + "component": [Function], + "defaultState": 0.7, + "eleType": "slider", + "mapTo": "groupWidth", + "name": "Group width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 0.97, + "eleType": "slider", + "mapTo": "barWidth", + "name": "Bar width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 1, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 80, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill Opacity", + "props": Object { + "max": 100, + }, + }, ], }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color Theme", + "schemas": Array [], + }, ], }, Object { @@ -289,17 +427,24 @@ exports[`Data table component Renders data table component 1`] = ` }, ], }, + "fillOpacity": 80, "fullLabel": "Bar", + "groupWidth": 0.7, "icon": [Function], "iconType": "visBarVerticalStacked", "id": "bar", "label": "Bar", + "labelAngle": 0, + "legendPosition": "v", + "lineWidth": 1, + "mode": "group", "name": "bar", "orientation": "v", "selection": Object { "dataLoss": "nothing", }, "seriesAxis": "yaxis", + "showLegend": "show", "type": "bar", "visConfig": Object { "config": Object { diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/heatmap.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/heatmap.test.tsx.snap index 56fc05d26..edcc736fb 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/heatmap.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/heatmap.test.tsx.snap @@ -202,6 +202,7 @@ exports[`Heatmap component Renders heatmap component 1`] = ` "userConfigs": Object {}, }, "vis": Object { + "barWidth": 0.97, "category": "Visualizations", "categoryAxis": "xaxis", "component": [Function], @@ -235,60 +236,197 @@ exports[`Heatmap component Renders heatmap component 1`] = ` }, Object { "editor": [Function], - "id": "chart_options", - "mapTo": "chartOptions", - "name": "Chart options", + "id": "legend", + "mapTo": "legend", + "name": "Legend", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, + "mapTo": "showLegend", + "name": "Show Legend", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, + }, + Object { + "component": null, + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, + }, + ], + }, + Object { + "editor": [Function], + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", + "schemas": Array [ + Object { + "component": [Function], + "eleType": "buttons", "mapTo": "orientation", "name": "Orientation", "props": Object { "defaultSelections": Array [ Object { + "id": "v", "name": "Vertical", - "orientationId": "v", }, ], - "dropdownList": Array [ + "options": Array [ Object { + "id": "v", "name": "Vertical", - "orientationId": "v", }, Object { + "id": "h", "name": "Horizontal", - "orientationId": "h", }, ], }, }, Object { - "component": null, - "isSingleSelection": true, + "component": [Function], + "eleType": "buttons", "mapTo": "mode", "name": "Mode", "props": Object { "defaultSelections": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, Object { - "modeId": "stack", + "id": "stack", "name": "Stack", }, ], }, }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateBarLabels", + "name": "Rotate bar labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, + Object { + "component": [Function], + "defaultState": 0.7, + "eleType": "slider", + "mapTo": "groupWidth", + "name": "Group width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 0.97, + "eleType": "slider", + "mapTo": "barWidth", + "name": "Bar width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 1, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 80, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill Opacity", + "props": Object { + "max": 100, + }, + }, ], }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color Theme", + "schemas": Array [], + }, ], }, Object { @@ -306,17 +444,24 @@ exports[`Heatmap component Renders heatmap component 1`] = ` }, ], }, + "fillOpacity": 80, "fullLabel": "Bar", + "groupWidth": 0.7, "icon": [Function], "iconType": "visBarVerticalStacked", "id": "bar", "label": "Bar", + "labelAngle": 0, + "legendPosition": "v", + "lineWidth": 1, + "mode": "group", "name": "bar", "orientation": "v", "selection": Object { "dataLoss": "nothing", }, "seriesAxis": "yaxis", + "showLegend": "show", "type": "bar", "visConfig": Object { "config": Object { diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap index d767d0c93..8a061dfd6 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap @@ -202,6 +202,7 @@ exports[`Line component Renders line component 1`] = ` "userConfigs": Object {}, }, "vis": Object { + "barWidth": 0.97, "category": "Visualizations", "categoryAxis": "xaxis", "component": [Function], @@ -235,60 +236,197 @@ exports[`Line component Renders line component 1`] = ` }, Object { "editor": [Function], - "id": "chart_options", - "mapTo": "chartOptions", - "name": "Chart options", + "id": "legend", + "mapTo": "legend", + "name": "Legend", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, + "mapTo": "showLegend", + "name": "Show Legend", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, + }, + Object { + "component": null, + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, + }, + ], + }, + Object { + "editor": [Function], + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", + "schemas": Array [ + Object { + "component": [Function], + "eleType": "buttons", "mapTo": "orientation", "name": "Orientation", "props": Object { "defaultSelections": Array [ Object { + "id": "v", "name": "Vertical", - "orientationId": "v", }, ], - "dropdownList": Array [ + "options": Array [ Object { + "id": "v", "name": "Vertical", - "orientationId": "v", }, Object { + "id": "h", "name": "Horizontal", - "orientationId": "h", }, ], }, }, Object { - "component": null, - "isSingleSelection": true, + "component": [Function], + "eleType": "buttons", "mapTo": "mode", "name": "Mode", "props": Object { "defaultSelections": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, Object { - "modeId": "stack", + "id": "stack", "name": "Stack", }, ], }, }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateBarLabels", + "name": "Rotate bar labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, + Object { + "component": [Function], + "defaultState": 0.7, + "eleType": "slider", + "mapTo": "groupWidth", + "name": "Group width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 0.97, + "eleType": "slider", + "mapTo": "barWidth", + "name": "Bar width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 1, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 80, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill Opacity", + "props": Object { + "max": 100, + }, + }, ], }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color Theme", + "schemas": Array [], + }, ], }, Object { @@ -306,17 +444,24 @@ exports[`Line component Renders line component 1`] = ` }, ], }, + "fillOpacity": 80, "fullLabel": "Bar", + "groupWidth": 0.7, "icon": [Function], "iconType": "visBarVerticalStacked", "id": "bar", "label": "Bar", + "labelAngle": 0, + "legendPosition": "v", + "lineWidth": 1, + "mode": "group", "name": "bar", "orientation": "v", "selection": Object { "dataLoss": "nothing", }, "seriesAxis": "yaxis", + "showLegend": "show", "type": "bar", "visConfig": Object { "config": Object { diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap index e2d0c49b7..a60d291d9 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap @@ -202,6 +202,7 @@ exports[`Pie component Renders pie component 1`] = ` "userConfigs": Object {}, }, "vis": Object { + "barWidth": 0.97, "category": "Visualizations", "categoryAxis": "xaxis", "component": [Function], @@ -235,60 +236,197 @@ exports[`Pie component Renders pie component 1`] = ` }, Object { "editor": [Function], - "id": "chart_options", - "mapTo": "chartOptions", - "name": "Chart options", + "id": "legend", + "mapTo": "legend", + "name": "Legend", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, + "mapTo": "showLegend", + "name": "Show Legend", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, + }, + Object { + "component": null, + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, + }, + ], + }, + Object { + "editor": [Function], + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", + "schemas": Array [ + Object { + "component": [Function], + "eleType": "buttons", "mapTo": "orientation", "name": "Orientation", "props": Object { "defaultSelections": Array [ Object { + "id": "v", "name": "Vertical", - "orientationId": "v", }, ], - "dropdownList": Array [ + "options": Array [ Object { + "id": "v", "name": "Vertical", - "orientationId": "v", }, Object { + "id": "h", "name": "Horizontal", - "orientationId": "h", }, ], }, }, Object { - "component": null, - "isSingleSelection": true, + "component": [Function], + "eleType": "buttons", "mapTo": "mode", "name": "Mode", "props": Object { "defaultSelections": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, Object { - "modeId": "stack", + "id": "stack", "name": "Stack", }, ], }, }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateBarLabels", + "name": "Rotate bar labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, + Object { + "component": [Function], + "defaultState": 0.7, + "eleType": "slider", + "mapTo": "groupWidth", + "name": "Group width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 0.97, + "eleType": "slider", + "mapTo": "barWidth", + "name": "Bar width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 1, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 80, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill Opacity", + "props": Object { + "max": 100, + }, + }, ], }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color Theme", + "schemas": Array [], + }, ], }, Object { @@ -306,17 +444,24 @@ exports[`Pie component Renders pie component 1`] = ` }, ], }, + "fillOpacity": 80, "fullLabel": "Bar", + "groupWidth": 0.7, "icon": [Function], "iconType": "visBarVerticalStacked", "id": "bar", "label": "Bar", + "labelAngle": 0, + "legendPosition": "v", + "lineWidth": 1, + "mode": "group", "name": "bar", "orientation": "v", "selection": Object { "dataLoss": "nothing", }, "seriesAxis": "yaxis", + "showLegend": "show", "type": "bar", "visConfig": Object { "config": Object { @@ -382,7 +527,7 @@ exports[`Pie component Renders pie component 1`] = ` "font": Object { "size": undefined, }, - "orientation": undefined, + "orientation": "v", }, "margin": Object { "b": 15, @@ -391,7 +536,7 @@ exports[`Pie component Renders pie component 1`] = ` "r": 10, "t": 30, }, - "showlegend": undefined, + "showlegend": "show", "title": "", } } @@ -443,7 +588,7 @@ exports[`Pie component Renders pie component 1`] = ` "font": Object { "size": undefined, }, - "orientation": undefined, + "orientation": "v", }, "margin": Object { "b": 15, @@ -452,7 +597,7 @@ exports[`Pie component Renders pie component 1`] = ` "r": 10, "t": 30, }, - "showlegend": undefined, + "showlegend": "show", "title": "", "xaxis": Object { "automargin": true, diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/text.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/text.test.tsx.snap index d52e5c2d9..1438329ba 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/text.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/text.test.tsx.snap @@ -185,6 +185,7 @@ exports[`Text component Renders text component 1`] = ` "userConfigs": Object {}, }, "vis": Object { + "barWidth": 0.97, "category": "Visualizations", "categoryAxis": "xaxis", "component": [Function], @@ -218,60 +219,197 @@ exports[`Text component Renders text component 1`] = ` }, Object { "editor": [Function], - "id": "chart_options", - "mapTo": "chartOptions", - "name": "Chart options", + "id": "legend", + "mapTo": "legend", + "name": "Legend", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, + "mapTo": "showLegend", + "name": "Show Legend", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, + }, + Object { + "component": null, + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, + }, + ], + }, + Object { + "editor": [Function], + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", + "schemas": Array [ + Object { + "component": [Function], + "eleType": "buttons", "mapTo": "orientation", "name": "Orientation", "props": Object { "defaultSelections": Array [ Object { + "id": "v", "name": "Vertical", - "orientationId": "v", }, ], - "dropdownList": Array [ + "options": Array [ Object { + "id": "v", "name": "Vertical", - "orientationId": "v", }, Object { + "id": "h", "name": "Horizontal", - "orientationId": "h", }, ], }, }, Object { - "component": null, - "isSingleSelection": true, + "component": [Function], + "eleType": "buttons", "mapTo": "mode", "name": "Mode", "props": Object { "defaultSelections": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, Object { - "modeId": "stack", + "id": "stack", "name": "Stack", }, ], }, }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateBarLabels", + "name": "Rotate bar labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, + Object { + "component": [Function], + "defaultState": 0.7, + "eleType": "slider", + "mapTo": "groupWidth", + "name": "Group width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 0.97, + "eleType": "slider", + "mapTo": "barWidth", + "name": "Bar width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 1, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 80, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill Opacity", + "props": Object { + "max": 100, + }, + }, ], }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color Theme", + "schemas": Array [], + }, ], }, Object { @@ -289,17 +427,24 @@ exports[`Text component Renders text component 1`] = ` }, ], }, + "fillOpacity": 80, "fullLabel": "Bar", + "groupWidth": 0.7, "icon": [Function], "iconType": "visBarVerticalStacked", "id": "bar", "label": "Bar", + "labelAngle": 0, + "legendPosition": "v", + "lineWidth": 1, + "mode": "group", "name": "bar", "orientation": "v", "selection": Object { "dataLoss": "nothing", }, "seriesAxis": "yaxis", + "showLegend": "show", "type": "bar", "visConfig": Object { "config": Object { diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/treemap.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/treemap.test.tsx.snap index 2f8495c0d..97b53ae9b 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/treemap.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/treemap.test.tsx.snap @@ -202,6 +202,7 @@ exports[`Treemap component Renders treemap component 1`] = ` "userConfigs": Object {}, }, "vis": Object { + "barWidth": 0.97, "category": "Visualizations", "categoryAxis": "xaxis", "component": [Function], @@ -235,60 +236,197 @@ exports[`Treemap component Renders treemap component 1`] = ` }, Object { "editor": [Function], - "id": "chart_options", - "mapTo": "chartOptions", - "name": "Chart options", + "id": "legend", + "mapTo": "legend", + "name": "Legend", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, + "mapTo": "showLegend", + "name": "Show Legend", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, + }, + Object { + "component": null, + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, + }, + ], + }, + Object { + "editor": [Function], + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", + "schemas": Array [ + Object { + "component": [Function], + "eleType": "buttons", "mapTo": "orientation", "name": "Orientation", "props": Object { "defaultSelections": Array [ Object { + "id": "v", "name": "Vertical", - "orientationId": "v", }, ], - "dropdownList": Array [ + "options": Array [ Object { + "id": "v", "name": "Vertical", - "orientationId": "v", }, Object { + "id": "h", "name": "Horizontal", - "orientationId": "h", }, ], }, }, Object { - "component": null, - "isSingleSelection": true, + "component": [Function], + "eleType": "buttons", "mapTo": "mode", "name": "Mode", "props": Object { "defaultSelections": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, Object { - "modeId": "stack", + "id": "stack", "name": "Stack", }, ], }, }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateBarLabels", + "name": "Rotate bar labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, + Object { + "component": [Function], + "defaultState": 0.7, + "eleType": "slider", + "mapTo": "groupWidth", + "name": "Group width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 0.97, + "eleType": "slider", + "mapTo": "barWidth", + "name": "Bar width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 1, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 80, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill Opacity", + "props": Object { + "max": 100, + }, + }, ], }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color Theme", + "schemas": Array [], + }, ], }, Object { @@ -306,17 +444,24 @@ exports[`Treemap component Renders treemap component 1`] = ` }, ], }, + "fillOpacity": 80, "fullLabel": "Bar", + "groupWidth": 0.7, "icon": [Function], "iconType": "visBarVerticalStacked", "id": "bar", "label": "Bar", + "labelAngle": 0, + "legendPosition": "v", + "lineWidth": 1, + "mode": "group", "name": "bar", "orientation": "v", "selection": Object { "dataLoss": "nothing", }, "seriesAxis": "yaxis", + "showLegend": "show", "type": "bar", "visConfig": Object { "config": Object { diff --git a/dashboards-observability/public/components/visualizations/charts/bar/bar.tsx b/dashboards-observability/public/components/visualizations/charts/bar/bar.tsx index 83ec1e393..1969401fa 100644 --- a/dashboards-observability/public/components/visualizations/charts/bar/bar.tsx +++ b/dashboards-observability/public/components/visualizations/charts/bar/bar.tsx @@ -9,6 +9,8 @@ import { Plt } from '../../plotly/plot'; import { LONG_CHART_COLOR, PLOTLY_COLOR } from '../../../../../common/constants/shared'; import { AvailabilityUnitType } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_availability'; import { ThresholdUnitType } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_thresholds'; +import { hexToRgb } from '../../../event_analytics/utils/utils'; +import { FILLOPACITY_DIV_FACTOR } from '../../../../../common/constants/shared'; export const Bar = ({ visualizations, layout, config }: any) => { const { vis } = visualizations; @@ -27,27 +29,19 @@ export const Bar = ({ visualizations, layout, config }: any) => { dataConfig.valueOptions && dataConfig.valueOptions.xaxis ? dataConfig.valueOptions.xaxis : []; const yaxis = dataConfig.valueOptions && dataConfig.valueOptions.xaxis ? dataConfig?.valueOptions.yaxis : []; - const barOrientation = - dataConfig?.chartOptions?.orientation && - dataConfig.chartOptions.orientation[0] && - dataConfig.chartOptions.orientation[0].orientationId - ? dataConfig.chartOptions.orientation[0].orientationId - : visualizations.vis.orientation; + const barOrientation = dataConfig?.chartStyles?.orientation || vis.orientation; const { defaultAxes } = visualizations.data; + const tickAngle = dataConfig?.chartStyles?.rotateBarLabels || vis.labelAngle + const lineWidth = dataConfig?.chartStyles?.lineWidth || vis.lineWidth; + const fillOpacity = dataConfig?.chartStyles?.fillOpacity !== undefined ? dataConfig?.chartStyles?.fillOpacity / FILLOPACITY_DIV_FACTOR : vis.fillOpacity / FILLOPACITY_DIV_FACTOR; + const barWidth = 1 - (dataConfig?.chartStyles?.barWidth || vis.barWidth); + const groupWidth = 1 - (dataConfig?.chartStyles?.groupWidth || vis.groupWidth); + const isVertical = barOrientation === vis.orientation; + const showLegend = !(dataConfig?.legend?.showLegend && dataConfig.legend.showLegend !== vis.showLegend); + const legendPosition = dataConfig?.legend?.position || vis.legendPosition; - const isVertical = barOrientation === 'v'; - - // Individual bars have different colors - // when: stackLength = 1 and length of result buckets < 16 and chart is not unicolor - // Else each stacked bar has its own color using colorway - let marker = {}; - if (lastIndex === 1 && data[fields[lastIndex].name].length < 16 && !isUniColor) { - marker = { - color: data[fields[lastIndex].name].map((_: string, index: number) => { - return PLOTLY_COLOR[index % PLOTLY_COLOR.length]; - }), - }; - } + const getSelectedColorTheme = (field: any, index: number) => dataConfig?.colorTheme?.length > 0 && dataConfig.colorTheme.find( + (colorSelected) => colorSelected.name.name === field.name)?.color || PLOTLY_COLOR[index % PLOTLY_COLOR.length]; let valueSeries; if (!isEmpty(xaxis) && !isEmpty(yaxis)) { @@ -57,7 +51,8 @@ export const Bar = ({ visualizations, layout, config }: any) => { } // determine category axis - let bars = valueSeries.map((field: any) => { + let bars = valueSeries.map((field: any, index: number) => { + const selectedColor = getSelectedColorTheme(field, index); return { x: isVertical ? data[!isEmpty(xaxis) ? xaxis[0].label : fields[lastIndex].name] @@ -66,7 +61,13 @@ export const Bar = ({ visualizations, layout, config }: any) => { ? data[field.name] : data[!isEmpty(yaxis) ? yaxis[0]?.label : fields[lastIndex].name], type: vis.type, - marker, + marker: { + color: hexToRgb(selectedColor, fillOpacity), + line: { + color: selectedColor, + width: lineWidth + } + }, name: field.name, orientation: barOrientation, }; @@ -82,12 +83,18 @@ export const Bar = ({ visualizations, layout, config }: any) => { ...layout, ...(layoutConfig.layout && layoutConfig.layout), title: dataConfig?.panelOptions?.title || layoutConfig.layout?.title || '', - barmode: - dataConfig?.chartOptions?.mode && - dataConfig.chartOptions.mode[0] && - dataConfig.chartOptions.mode[0].modeId - ? dataConfig.chartOptions.mode[0].modeId - : '', + barmode: dataConfig?.chartStyles?.mode || visualizations.vis.mode, + xaxis: { + tickangle: tickAngle, + automargin: true, + }, + bargap: groupWidth, + bargroupgap: barWidth, + legend: { + ...layout.legend, + orientation: legendPosition, + }, + showlegend: showLegend, }; if (dataConfig.thresholds || availabilityConfig.level) { diff --git a/dashboards-observability/public/components/visualizations/charts/bar/bar_type.ts b/dashboards-observability/public/components/visualizations/charts/bar/bar_type.ts index fb89427bc..eef1db58c 100644 --- a/dashboards-observability/public/components/visualizations/charts/bar/bar_type.ts +++ b/dashboards-observability/public/components/visualizations/charts/bar/bar_type.ts @@ -10,10 +10,16 @@ import { VizDataPanel } from '../../../event_analytics/explorer/visualizations/c import { ConfigEditor } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/json_editor'; import { ConfigValueOptions } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls'; import { ConfigAvailability } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_availability'; - +import { ButtonGroupItem } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_button_group'; +import { ConfigBarChartStyles } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_bar_chart_styles'; +import { SliderConfig } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_style_slider'; +import { ConfigLegend } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_legend'; +import { DefaultChartStyles } from '../../../../../common/constants/shared'; +import { ConfigColorTheme } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_color_theme'; const sharedConfigs = getPlotlySharedConfigs(); const VIS_CATEGORY = getPlotlyCategory(); +const { LegendPosition, ShowLegend } = DefaultChartStyles; export const createBarTypeDefinition = (params: any) => ({ name: 'bar', type: 'bar', @@ -29,6 +35,14 @@ export const createBarTypeDefinition = (params: any) => ({ categoryAxis: 'xaxis', seriesAxis: 'yaxis', orientation: 'v', + mode: 'group', + labelAngle: 0, + lineWidth: 1, + fillOpacity: 80, + groupWidth: 0.7, + barWidth: 0.97, + showLegend: ShowLegend, + legendPosition: LegendPosition, component: Bar, editorConfig: { panelTabs: [ @@ -59,39 +73,140 @@ export const createBarTypeDefinition = (params: any) => ({ ], }, { - id: 'chart_options', - name: 'Chart options', - editor: ConfigValueOptions, - mapTo: 'chartOptions', + id: 'legend', + name: 'Legend', + editor: ConfigLegend, + mapTo: 'legend', schemas: [ { - name: 'Orientation', - isSingleSelection: true, + name: 'Show Legend', + mapTo: 'showLegend', component: null, + props: { + options: [ + { name: 'Show', id: "show" }, + { name: 'Hidden', id: "hidden" }, + ], + defaultSelections: [{ name: 'Show', id: ShowLegend }], + }, + }, + { + name: 'Position', + mapTo: 'position', + component: null, + props: { + options: [ + { name: 'Right', id: 'v' }, + { name: 'Bottom', id: 'h' }, + ], + defaultSelections: [{ name: 'Right', id: LegendPosition }], + }, + }, + ], + }, + { + id: 'chart_styles', + name: 'Chart styles', + editor: ConfigBarChartStyles, + mapTo: 'chartStyles', + schemas: [ + { + name: 'Orientation', + component: ButtonGroupItem, mapTo: 'orientation', + eleType: 'buttons', props: { - dropdownList: [ - { name: 'Vertical', orientationId: 'v' }, - { name: 'Horizontal', orientationId: 'h' }, + options: [ + { name: 'Vertical', id: 'v' }, + { name: 'Horizontal', id: 'h' }, ], - defaultSelections: [{ name: 'Vertical', orientationId: 'v' }], + defaultSelections: [{ name: 'Vertical', id: 'v' }], }, }, { name: 'Mode', - isSingleSelection: true, - component: null, + component: ButtonGroupItem, mapTo: 'mode', + eleType: 'buttons', props: { - dropdownList: [ - { name: 'Group', modeId: 'group' }, - { name: 'Stack', modeId: 'stack' }, + options: [ + { name: 'Group', id: 'group' }, + { name: 'Stack', id: 'stack' }, ], - defaultSelections: [{ name: 'Group', modeId: 'group' }], + defaultSelections: [{ name: 'Group', id: 'group' }], + }, + }, + { + name: 'Rotate bar labels', + component: SliderConfig, + mapTo: 'rotateBarLabels', + eleType: 'slider', + defaultState: 0, + props: { + ticks: + [ + { label: '-90°', value: -90 }, + { label: '-45°', value: -45 }, + { label: '0°', value: 0 }, + { label: '45°', value: 45 }, + { label: '90°', value: 90 }, + ], + showTicks: true, + min: -90, + max: 90 + }, + }, + { + name: 'Group width', + component: SliderConfig, + mapTo: 'groupWidth', + defaultState: 0.7, + props: { + max: 1, + step: 0.01, + }, + eleType: 'slider', + }, + { + name: 'Bar width', + component: SliderConfig, + mapTo: 'barWidth', + defaultState: 0.97, + props: { + max: 1, + step: 0.01, }, + eleType: 'slider', + }, + { + name: 'Line width', + component: SliderConfig, + mapTo: 'lineWidth', + defaultState: 1, + props: { + max: 10, + }, + eleType: 'slider', + }, + { + name: 'Fill Opacity', + component: SliderConfig, + mapTo: 'fillOpacity', + defaultState: 80, + props: { + max: 100, + }, + eleType: 'slider', }, ], }, + { + id: 'color-theme', + name: 'Color Theme', + editor: ConfigColorTheme, + mapTo: 'colorTheme', + schemas: [], + }, ], }, { From c136532e5ef8423aafb4d05829e81c29f26c9d85 Mon Sep 17 00:00:00 2001 From: Kavitha Conjeevaram Mohan Date: Thu, 23 Jun 2022 14:39:13 -0700 Subject: [PATCH 028/135] bump version to 2.1.0 and bump gradle to 7.4.2 (#817) Signed-off-by: Kavitha Conjeevaram Mohan --- .../dashboards-observability-test-and-build-workflow.yml | 2 +- dashboards-observability/opensearch_dashboards.json | 4 ++-- dashboards-observability/package.json | 2 +- opensearch-observability/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../kotlin/org/opensearch/observability/PluginRestTestCase.kt | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/dashboards-observability-test-and-build-workflow.yml b/.github/workflows/dashboards-observability-test-and-build-workflow.yml index 608f6d1fb..e052febae 100644 --- a/.github/workflows/dashboards-observability-test-and-build-workflow.yml +++ b/.github/workflows/dashboards-observability-test-and-build-workflow.yml @@ -6,7 +6,7 @@ on: [pull_request, push] env: PLUGIN_NAME: dashboards-observability OPENSEARCH_VERSION: 'main' - OPENSEARCH_PLUGIN_VERSION: 2.0.0.0 + OPENSEARCH_PLUGIN_VERSION: 2.1.0.0 jobs: diff --git a/dashboards-observability/opensearch_dashboards.json b/dashboards-observability/opensearch_dashboards.json index 2b9ffd295..b8d9645c6 100644 --- a/dashboards-observability/opensearch_dashboards.json +++ b/dashboards-observability/opensearch_dashboards.json @@ -1,7 +1,7 @@ { "id": "observabilityDashboards", - "version": "2.0.0.0", - "opensearchDashboardsVersion": "2.0.0", + "version": "2.1.0.0", + "opensearchDashboardsVersion": "2.1.0", "server": true, "ui": true, "requiredPlugins": [ diff --git a/dashboards-observability/package.json b/dashboards-observability/package.json index 4e422d934..1bfdf7b80 100644 --- a/dashboards-observability/package.json +++ b/dashboards-observability/package.json @@ -1,6 +1,6 @@ { "name": "observability-dashboards", - "version": "2.0.0.0", + "version": "2.1.0.0", "main": "index.ts", "license": "Apache-2.0", "scripts": { diff --git a/opensearch-observability/build.gradle b/opensearch-observability/build.gradle index 2f2f3b4a4..2a91f7263 100644 --- a/opensearch-observability/build.gradle +++ b/opensearch-observability/build.gradle @@ -10,7 +10,7 @@ import org.opensearch.gradle.testclusters.StandaloneRestIntegTestTask buildscript { ext { isSnapshot = "true" == System.getProperty("build.snapshot", "true") - opensearch_version = System.getProperty("opensearch.version", "2.0.0-SNAPSHOT") + opensearch_version = System.getProperty("opensearch.version", "2.1.0-SNAPSHOT") buildVersionQualifier = System.getProperty("build.version_qualifier", "") version_tokens = opensearch_version.tokenize('-') opensearch_build = version_tokens[0] + '.0' diff --git a/opensearch-observability/gradle/wrapper/gradle-wrapper.properties b/opensearch-observability/gradle/wrapper/gradle-wrapper.properties index 011069143..967533df8 100644 --- a/opensearch-observability/gradle/wrapper/gradle-wrapper.properties +++ b/opensearch-observability/gradle/wrapper/gradle-wrapper.properties @@ -4,7 +4,7 @@ ## #Wed Jul 29 13:30:55 PDT 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists diff --git a/opensearch-observability/src/test/kotlin/org/opensearch/observability/PluginRestTestCase.kt b/opensearch-observability/src/test/kotlin/org/opensearch/observability/PluginRestTestCase.kt index 759674eef..c5a1d4115 100644 --- a/opensearch-observability/src/test/kotlin/org/opensearch/observability/PluginRestTestCase.kt +++ b/opensearch-observability/src/test/kotlin/org/opensearch/observability/PluginRestTestCase.kt @@ -60,7 +60,7 @@ abstract class PluginRestTestCase : OpenSearchRestTestCase() { open fun wipeAllOpenSearchIndices() { if (preserveOpenSearchIndicesAfterTest()) return val response = client().performRequest(Request("GET", "/_cat/indices?format=json&expand_wildcards=all")) - val xContentType = XContentType.fromMediaTypeOrFormat(response.entity.contentType.value) + val xContentType = XContentType.fromMediaType(response.entity.contentType.value) xContentType.xContent().createParser( NamedXContentRegistry.EMPTY, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, response.entity.content From bf81e3518ab7e024176a6a571ecb0d927d89ca30 Mon Sep 17 00:00:00 2001 From: Kavitha Conjeevaram Mohan Date: Wed, 6 Jul 2022 16:19:55 -0700 Subject: [PATCH 029/135] 2.1 release notes (#839) Signed-off-by: Kavitha Conjeevaram Mohan --- ...ensearch-observability.release-notes-2.1.0.0.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 release-notes/opensearch-observability.release-notes-2.1.0.0.md diff --git a/release-notes/opensearch-observability.release-notes-2.1.0.0.md b/release-notes/opensearch-observability.release-notes-2.1.0.0.md new file mode 100644 index 000000000..ea38b04f3 --- /dev/null +++ b/release-notes/opensearch-observability.release-notes-2.1.0.0.md @@ -0,0 +1,14 @@ +## Version 2.1.0.0 Release Notes +Compatible with OpenSearch and OpenSearch Dashboards Version 2.1.0 + +### Enhancements +* Add availability help flyout ([#734](https://github.com/opensearch-project/observability/pull/734)) + +### Infrastructure +* bump version to 2.1.0 and gradle version to 7.4.2 ([#817](https://github.com/opensearch-project/observability/pull/817)) +* Uses custom plugin to publish zips to maven ([#786](https://github.com/opensearch-project/observability/pull/786)) + +### Refactoring +* Make common delete modal for components ([#766](https://github.com/opensearch-project/observability/pull/766)) +* Sync app and app list types ([#763](https://github.com/opensearch-project/observability/pull/763)) + From 4bebc60f49f6f4be41e726ace5dbe76cb7a077b1 Mon Sep 17 00:00:00 2001 From: Kavitha Conjeevaram Mohan Date: Wed, 6 Jul 2022 18:35:31 -0700 Subject: [PATCH 030/135] change 2.1 version bump PR under maintenance (#841) * change version bump to maintenance Signed-off-by: Kavitha Conjeevaram Mohan * change version bump to maintenance Signed-off-by: Kavitha Conjeevaram Mohan --- .../opensearch-observability.release-notes-2.1.0.0.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/release-notes/opensearch-observability.release-notes-2.1.0.0.md b/release-notes/opensearch-observability.release-notes-2.1.0.0.md index ea38b04f3..fc8bc299a 100644 --- a/release-notes/opensearch-observability.release-notes-2.1.0.0.md +++ b/release-notes/opensearch-observability.release-notes-2.1.0.0.md @@ -5,10 +5,11 @@ Compatible with OpenSearch and OpenSearch Dashboards Version 2.1.0 * Add availability help flyout ([#734](https://github.com/opensearch-project/observability/pull/734)) ### Infrastructure -* bump version to 2.1.0 and gradle version to 7.4.2 ([#817](https://github.com/opensearch-project/observability/pull/817)) * Uses custom plugin to publish zips to maven ([#786](https://github.com/opensearch-project/observability/pull/786)) ### Refactoring * Make common delete modal for components ([#766](https://github.com/opensearch-project/observability/pull/766)) * Sync app and app list types ([#763](https://github.com/opensearch-project/observability/pull/763)) +### Maintenance +* Bump version to 2.1.0 and gradle version to 7.4.2 ([#817](https://github.com/opensearch-project/observability/pull/817)) \ No newline at end of file From bf98d97b8d5150177462250a4a8b84023161e3a2 Mon Sep 17 00:00:00 2001 From: abasatwar Date: Fri, 8 Jul 2022 23:17:18 +0530 Subject: [PATCH 031/135] Sprint1 : combine PR for visualization from Sprint1 (#824) * graph style section UI schema Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/visualizations/charts/lines/line_type.ts * changes for style mode and interpolation Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/visualizations/charts/lines/line_type.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts * lineWidth integration for line mode Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/visualizations/charts/lines/line.tsx # dashboards-observability/public/components/visualizations/charts/lines/line_type.ts # Conflicts: # dashboards-observability/common/constants/shared.ts # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_style_slider.tsx * changes for Legend and Orientation in Line Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_button_group.tsx # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_legend.tsx # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_style_slider.tsx * point size and Bar Alignment changes Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/visualizations/charts/lines/line.tsx # dashboards-observability/public/components/visualizations/charts/lines/line_type.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/common/constants/shared.ts # dashboards-observability/common/types/explorer.ts * implemented fill opacity for line chart Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/visualizations/charts/lines/line.tsx # Conflicts: # dashboards-observability/public/components/event_analytics/utils/utils.tsx * changes for line width and fill opacity in bar mode and removed mode from chartOption Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/common/constants/shared.ts * updated bar mode opacity in line chart Signed-off-by: rinku-kumar-psl * refactored the config chart style code Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/common/constants/shared.ts # dashboards-observability/common/types/explorer.ts * snapshot updated and code refactored Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/utils/utils.tsx * type added to new component Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_button_group.tsx # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_style_slider.tsx * review comments addressed Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/utils/utils.tsx # Conflicts: # dashboards-observability/common/constants/shared.ts * cypress test case added and resolve button label wraping issue Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/.cypress/integration/1_event_analytics.spec.js # dashboards-observability/.cypress/utils/event_constants.js # Conflicts: # dashboards-observability/.cypress/integration/1_event_analytics.spec.js # Conflicts: # dashboards-observability/.cypress/integration/1_event_analytics.spec.js * multi matrices changes for Line Signed-off-by: rinku-kumar-psl * dimensions and metrics UI changes for time-series Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/common/constants/explorer.ts # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx # dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx * made data config pannel collapsable and initial fields render Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx # dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx * code refactored Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx * snapshot updated and handled corner cases Signed-off-by: rinku-kumar-psl * code styling fixes and added TODO comment Signed-off-by: rinku-kumar-psl * table view: eui table replaced with ag-grid Signed-off-by: Ramneet Chopra * drag-drop issue fixed Signed-off-by: Ramneet Chopra * test case of data_table updated Signed-off-by: Ramneet Chopra * feedback comments resolved Signed-off-by: Ramneet Chopra * grid height issue:fixed Signed-off-by: Ramneet Chopra * column height, value getter for type double Signed-off-by: Ramneet Chopra * data_table elements moved to separate Signed-off-by: Ramneet Chopra * footer components Signed-off-by: Ramneet Chopra * cypress test cases for table view Signed-off-by: Ramneet Chopra * data config reviewed code added Signed-off-by: Deepak Nevde * Text correction Signed-off-by: Deepak Nevde * Conflicts resolved Signed-off-by: Deepak Nevde * enhancement for heatmap with new UI Signed-off-by: Shankha Das * line chart test cases Signed-off-by: Shankha Das * console logs removed Signed-off-by: Shankha Das * updated value options ui for treemap Signed-off-by: Mrunal Zambre * removed console Signed-off-by: Mrunal Zambre * sprint1-visualization-fixes. Signed-off-by: abasatwar * initialize default params for DimensonComponent and formatted the codes Signed-off-by: rinku-kumar-psl * code review changes done Signed-off-by: rinku-kumar-psl * added empty line at end. Signed-off-by: abasatwar Co-authored-by: rinku-kumar-psl Co-authored-by: Ramneet Chopra Co-authored-by: Deepak Nevde Co-authored-by: Shankha Das Co-authored-by: Mrunal Zambre --- .../integration/1_event_analytics.spec.js | 529 +++--- .../.cypress/utils/event_constants.js | 28 + .../common/constants/explorer.ts | 13 + .../common/types/explorer.ts | 14 + dashboards-observability/package.json | 3 + .../__snapshots__/utils.test.tsx.snap | 486 +++++- .../__snapshots__/config_panel.test.tsx.snap | 1490 +++++++++-------- .../config_controls/config_button_group.tsx | 35 +- .../config_line_chart_styles.tsx | 95 ++ .../config_treemap_parents.tsx | 50 +- .../data_config_panel_item.tsx | 123 +- .../config_panes/config_controls/index.ts | 1 + .../treemap_config_panel_item.tsx | 160 ++ .../explorer/visualizations/index.tsx | 35 +- .../workspace_panel/workspace_panel.tsx | 16 +- .../event_analytics/utils/utils.tsx | 4 +- .../__snapshots__/data_table.test.tsx.snap | 730 ++------ .../__snapshots__/line.test.tsx.snap | 76 +- .../charts/__tests__/data_table.test.tsx | 17 +- .../charts/data_table/data_table.scss | 63 + .../charts/data_table/data_table.tsx | 277 ++- .../charts/data_table/data_table_footer.tsx | 126 ++ .../charts/data_table/data_table_header.tsx | 195 +++ .../visualizations/charts/lines/line.tsx | 80 +- .../visualizations/charts/lines/line_type.ts | 109 +- .../visualizations/charts/maps/heatmap.tsx | 30 +- .../charts/maps/heatmap_type.ts | 14 - .../charts/maps/treemap_type.ts | 27 - .../visualizations/charts/maps/treemaps.tsx | 19 +- .../visualizations/charts/pie/pie.tsx | 8 +- dashboards-observability/yarn.lock | 30 +- 31 files changed, 2832 insertions(+), 2051 deletions(-) create mode 100644 dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_line_chart_styles.tsx create mode 100644 dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/treemap_config_panel_item.tsx create mode 100644 dashboards-observability/public/components/visualizations/charts/data_table/data_table.scss create mode 100644 dashboards-observability/public/components/visualizations/charts/data_table/data_table_footer.tsx create mode 100644 dashboards-observability/public/components/visualizations/charts/data_table/data_table_header.tsx diff --git a/dashboards-observability/.cypress/integration/1_event_analytics.spec.js b/dashboards-observability/.cypress/integration/1_event_analytics.spec.js index e237252c3..f2b0db3c6 100644 --- a/dashboards-observability/.cypress/integration/1_event_analytics.spec.js +++ b/dashboards-observability/.cypress/integration/1_event_analytics.spec.js @@ -19,33 +19,47 @@ import { landOnEventVisualizations, landOnPanels, renderTreeMapchart, - renderPieChart + renderPieChart, + renderLineChartForDataConfig, + DataConfigLineChart } from '../utils/event_constants'; import { supressResizeObserverIssue } from '../utils/constants'; const vis_name_sub_string = Math.floor(Math.random() * 100); const saveVisualizationAndVerify = () => { cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').click(); - cy.get('[data-test-subj="eventExplorer__querySaveComboBox"]').click() + cy.get('[data-test-subj="eventExplorer__querySaveComboBox"]').click(); cy.get('.euiComboBoxOptionsList__rowWrap .euiFilterSelectItem').eq(0).click(); - cy.get('.euiPopover__panel .euiFormControlLayoutIcons [data-test-subj="comboBoxToggleListButton"]').eq(0).click(); - cy.get('.euiPopover__panel input').eq(1).type(`Test visualization` + vis_name_sub_string); + cy.get( + '.euiPopover__panel .euiFormControlLayoutIcons [data-test-subj="comboBoxToggleListButton"]' + ) + .eq(0) + .click(); + cy.get('.euiPopover__panel input') + .eq(1) + .type(`Test visualization` + vis_name_sub_string); cy.get('[data-test-subj="eventExplorer__querySaveConfirm"]').click(); cy.wait(delay); cy.get('.euiHeaderBreadcrumbs a').eq(1).click(); - cy.get('.euiFlexGroup .euiFormControlLayout__childrenWrapper input').eq(0).type(`Test visualization` + vis_name_sub_string).type('{enter}'); + cy.get('.euiFlexGroup .euiFormControlLayout__childrenWrapper input') + .eq(0) + .type(`Test visualization` + vis_name_sub_string) + .type('{enter}'); cy.get('.euiBasicTable .euiTableCellContent button').eq(0).click(); -} +}; const deleteVisualization = () => { cy.get('a[href = "#/event_analytics"]').click(); - cy.get('.euiFlexGroup .euiFormControlLayout__childrenWrapper input').eq(0).type(`Test visualization`).type('{enter}'); + cy.get('.euiFlexGroup .euiFormControlLayout__childrenWrapper input') + .eq(0) + .type(`Test visualization`) + .type('{enter}'); cy.get('input[data-test-subj = "checkboxSelectAll"]').click(); cy.get('.euiButtonContent.euiButtonContent--iconRight.euiButton__content').click(); cy.get('.euiContextMenuItem .euiContextMenuItem__text').eq(0).click(); cy.get('input[placeholder = "delete"]').clear().type('delete'); cy.get('button[data-test-subj = "popoverModal__deleteButton"]').click(); cy.get('.euiToastHeader').should('exist'); -} +}; describe('Adding sample data and visualization', () => { it('Adds sample flights data for event analytics', () => { cy.visit(`${Cypress.env('opensearchDashboards')}/app/home#/tutorial_directory/sampleData`); @@ -80,11 +94,14 @@ describe('Search a query on event home', () => { cy.get('[data-test-subj="superDatePickerToggleQuickMenuButton"]').click(); cy.get('[data-test-subj="superDatePickerCommonlyUsed_Year_to date"]').click(); cy.get('[data-test-subj="superDatePickerApplyTimeButton"]').contains('Refresh').click(); - cy.window().its('store').invoke('getState').then((state) => { - expect(Object.values(state.queries)[0]['rawQuery'].trim()).equal(TEST_QUERIES[0].query) - expect(Object.values(state.queries)[0]['selectedDateRange'][0]).equal("now/y"); - expect(Object.values(state.queries)[0]['selectedDateRange'][1]).equal("now"); - }); + cy.window() + .its('store') + .invoke('getState') + .then((state) => { + expect(Object.values(state.queries)[0]['rawQuery'].trim()).equal(TEST_QUERIES[0].query); + expect(Object.values(state.queries)[0]['selectedDateRange'][0]).equal('now/y'); + expect(Object.values(state.queries)[0]['selectedDateRange'][1]).equal('now'); + }); cy.wait(delay); cy.url().should('contain', '#/event_analytics/explorer'); @@ -101,16 +118,26 @@ describe('Open flyout for a data row to see details', () => { it('Should be able to open flyout and see data, json and traces', () => { cy.get('[data-test-subj="docTable"] tbody tr button.euiButtonIcon').first().click(); cy.get('.observability-flyout').should('exist'); - cy.get('.observability-flyout .osdDocViewer .euiTabs span.euiTab__content').contains('JSON').click(); - cy.get('.observability-flyout .osdDocViewer .euiTabs span.euiTab__content').contains('Traces').click(); - cy.get('.observability-flyout .osdDocViewer .euiTabs span.euiTab__content').contains('Table').click(); + cy.get('.observability-flyout .osdDocViewer .euiTabs span.euiTab__content') + .contains('JSON') + .click(); + cy.get('.observability-flyout .osdDocViewer .euiTabs span.euiTab__content') + .contains('Traces') + .click(); + cy.get('.observability-flyout .osdDocViewer .euiTabs span.euiTab__content') + .contains('Table') + .click(); }); it('Should be able to see srrounding docs', () => { cy.get('[data-test-subj="docTable"] tbody tr button.euiButtonIcon').first().click(); cy.get('.observability-flyout').should('exist'); - cy.get('.observability-flyout span.euiButton__text').contains('View surrounding events').click(); - cy.get('.observability-flyout #surroundingFyout').contains('View surrounding events').should('exist'); + cy.get('.observability-flyout span.euiButton__text') + .contains('View surrounding events') + .click(); + cy.get('.observability-flyout #surroundingFyout') + .contains('View surrounding events') + .should('exist'); }); }); @@ -277,7 +304,9 @@ describe('Saves a query on explorer page', () => { cy.get('button[id="main-content-vis"]').contains('Visualizations').click(); cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').click(); cy.wait(delay * 2); - cy.get('[data-test-subj="eventExplorer__querySaveComboBox"] [data-test-subj="comboBoxToggleListButton"]').click(); + cy.get( + '[data-test-subj="eventExplorer__querySaveComboBox"] [data-test-subj="comboBoxToggleListButton"]' + ).click(); cy.get('[data-test-subj="eventExplorer__querySaveName"]').type(SAVE_QUERY2); cy.get('[data-test-subj="eventExplorer__querySaveConfirm"]').click(); cy.wait(delay * 2); @@ -307,11 +336,15 @@ describe('Saves a query on explorer page', () => { cy.get('button[id="main-content-vis"]').contains('Visualizations').click(); cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').click(); cy.wait(delay * 2); - cy.get('[data-test-subj="eventExplorer__querySaveComboBox"] [data-test-subj="comboBoxToggleListButton"]').click(); + cy.get( + '[data-test-subj="eventExplorer__querySaveComboBox"] [data-test-subj="comboBoxToggleListButton"]' + ).click(); cy.get('[data-test-subj="eventExplorer__querySaveName"]').type(SAVE_QUERY3); cy.get('[data-test-subj="eventExplorer__querySaveComboBox"]').type(TESTING_PANEL); cy.get(`input[value="${TESTING_PANEL}"]`).click(); - cy.get('[data-test-subj="eventExplorer__querySaveComboBox"] [data-test-subj="comboBoxToggleListButton"]').click(); + cy.get( + '[data-test-subj="eventExplorer__querySaveComboBox"] [data-test-subj="comboBoxToggleListButton"]' + ).click(); cy.get('[data-test-subj="eventExplorer__querySaveConfirm"]').click(); cy.wait(delay); @@ -330,8 +363,11 @@ describe('Override timestamp for an index', () => { cy.wait(delay); cy.get('[data-attr-field="utc_time"] [data-test-subj="eventFields__default-timestamp-mark"') - .contains('Default Timestamp').should('exist'); - cy.get('[data-attr-field="timestamp"] [data-test-subj="eventFields__default-timestamp-mark"').should('not.exist'); + .contains('Default Timestamp') + .should('exist'); + cy.get( + '[data-attr-field="timestamp"] [data-test-subj="eventFields__default-timestamp-mark"' + ).should('not.exist'); }); }); @@ -394,20 +430,40 @@ describe('Click to view field insights', () => { it('Click a numerical field to view field insights', () => { cy.get('[data-test-subj="field-bytes-showDetails"]').click(); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Top values').should('exist'); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Rare values').should('exist'); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Average overtime').should('exist'); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Maximum overtime').should('exist'); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Minimum overtime').should('exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Top values') + .should('exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Rare values') + .should('exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Average overtime') + .should('exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Maximum overtime') + .should('exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Minimum overtime') + .should('exist'); }); it('Click a non-numerical field to view insights', () => { cy.get('[data-test-subj="field-host-showDetails"]').click(); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Top values').should('exist'); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Rare values').should('exist'); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Average overtime').should('not.exist'); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Maximum overtime').should('not.exist'); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Minimum overtime').should('not.exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Top values') + .should('exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Rare values') + .should('exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Average overtime') + .should('not.exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Maximum overtime') + .should('not.exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Minimum overtime') + .should('not.exist'); }); }); @@ -494,7 +550,9 @@ describe('Renders noresult chart', () => { }); it('It should render no result when there is no data', () => { - cy.get('[data-test-subj="vizWorkspace__noData"] p').contains('No results found').should('exist'); + cy.get('[data-test-subj="vizWorkspace__noData"] p') + .contains('No results found') + .should('exist'); }); }); @@ -505,7 +563,9 @@ describe('Renders bar charts', () => { it('Renders vertical bar chart', () => { querySearch(TEST_QUERIES[3].query, TEST_QUERIES[3].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get( + '[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]' + ).click(); cy.wait(delay * 2); cy.get('[data-test-subj="comboBoxOptionsList "] span').contains('Bar').click(); cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]').first().click(); @@ -522,12 +582,16 @@ describe('Renders bar charts', () => { cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Group').click(); cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); cy.wait(delay * 2); - cy.get('g.xaxislayer-above > g.xtick text[data-unformatted|="artifacts.opensearch.org"]').should('exist'); + cy.get( + 'g.xaxislayer-above > g.xtick text[data-unformatted|="artifacts.opensearch.org"]' + ).should('exist'); }); it('Renders horiztontal bar chart', () => { querySearch(TEST_QUERIES[3].query, TEST_QUERIES[3].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get( + '[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]' + ).click(); cy.wait(delay * 2); cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Bar').click(); cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]').first().click(); @@ -544,7 +608,9 @@ describe('Renders bar charts', () => { cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Group').click(); cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); cy.wait(delay * 2); - cy.get('g.yaxislayer-above > g.ytick text[data-unformatted|="artifacts.opensearch.org"]').should('exist'); + cy.get( + 'g.yaxislayer-above > g.ytick text[data-unformatted|="artifacts.opensearch.org"]' + ).should('exist'); }); }); @@ -555,7 +621,9 @@ describe('Renders line charts', () => { it('Renders line chart with threshold', () => { querySearch(TEST_QUERIES[3].query, TEST_QUERIES[3].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get( + '[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]' + ).click(); cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Line').click(); cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]').first().click(); cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('host').click(); @@ -569,7 +637,9 @@ describe('Renders line charts', () => { cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); cy.wait(delay * 2); cy.get('g.text > g.textpoint text[data-unformatted|="Max"]').should('exist'); - cy.get('g.xaxislayer-above > g.xtick text[data-unformatted|="artifacts.opensearch.org"]').should('exist'); + cy.get( + 'g.xaxislayer-above > g.xtick text[data-unformatted|="artifacts.opensearch.org"]' + ).should('exist'); }); }); @@ -580,7 +650,9 @@ describe('Renders pie charts', () => { it('Renders pie chart', () => { querySearch(TEST_QUERIES[3].query, TEST_QUERIES[3].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get( + '[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]' + ).click(); cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Pie').click(); cy.wait(delay); cy.get('g.pielayer').should('exist'); @@ -594,7 +666,9 @@ describe('Renders heatmap chart', () => { it('Renders heatmap chart with different z-axes', () => { querySearch(TEST_QUERIES[4].query, TEST_QUERIES[4].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get( + '[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]' + ).click(); cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Heatmap').click(); cy.wait(delay * 2); cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]').click(); @@ -618,13 +692,17 @@ describe('Renders markdown chart', () => { it('Renders markdown chart with test title', () => { querySearch(TEST_QUERIES[3].query, TEST_QUERIES[3].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get( + '[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]' + ).click(); cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Text').click(); cy.get('[data-test-subj="workspace__viz_markdown"] h2').contains('Text').should('exist'); cy.get('textarea.euiMarkdownEditorTextArea').type('## testing title'); cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); cy.wait(delay * 2); - cy.get('[data-test-subj="workspace__viz_markdown"] h2').contains('testing title').should('exist'); + cy.get('[data-test-subj="workspace__viz_markdown"] h2') + .contains('testing title') + .should('exist'); }); }); @@ -652,15 +730,25 @@ describe('Renders chart and verify Toast message if X-axis and Y-axis values are .type('Bar') .type('{enter}'); cy.wait(delay); - cy.get('#configPanel__value_options [data-test-subj="comboBoxClearButton"]').eq(0).click({ force: true }); + cy.get('#configPanel__value_options [data-test-subj="comboBoxClearButton"]') + .eq(0) + .click({ force: true }); cy.get('#configPanel__value_options [data-test-subj="comboBoxToggleListButton"]').eq(0).click(); - cy.wait(delay) - cy.get('#configPanel__value_options [data-test-subj="comboBoxClearButton"]').click({ multiple: true }); + cy.wait(delay); + cy.get('#configPanel__value_options [data-test-subj="comboBoxClearButton"]').click({ + multiple: true, + }); cy.get('#configPanel__value_options [data-test-subj="comboBoxToggleListButton"]').eq(1).click(); - cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]').eq(0).should('have.value', ''); - cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]').eq(1).should('have.value', ''); + cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]') + .eq(0) + .should('have.value', ''); + cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]') + .eq(1) + .should('have.value', ''); cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); - cy.get('[data-test-subj="euiToastHeader"]').contains('Invalid value options configuration selected.').should('exist'); + cy.get('[data-test-subj="euiToastHeader"]') + .contains('Invalid value options configuration selected.') + .should('exist'); }); it('Renders chart, clear X-axis and Y-axis value and try to save visulization, Toast message should display with error message', () => { @@ -669,304 +757,103 @@ describe('Renders chart and verify Toast message if X-axis and Y-axis values are .type('Bar') .type('{enter}'); cy.wait(delay); - cy.get('#configPanel__value_options [data-test-subj="comboBoxClearButton"]').eq(0).click({ force: true }); + cy.get('#configPanel__value_options [data-test-subj="comboBoxClearButton"]') + .eq(0) + .click({ force: true }); cy.get('#configPanel__value_options [data-test-subj="comboBoxToggleListButton"]').eq(0).click(); - cy.wait(delay) - cy.get('#configPanel__value_options [data-test-subj="comboBoxClearButton"]').click({ multiple: true }); - cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]').eq(0).should('have.value', ''); - cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]').eq(1).should('have.value', ''); + cy.wait(delay); + cy.get('#configPanel__value_options [data-test-subj="comboBoxClearButton"]').click({ + multiple: true, + }); + cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]') + .eq(0) + .should('have.value', ''); + cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]') + .eq(1) + .should('have.value', ''); cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').click(); cy.get('[data-test-subj="eventExplorer__querySaveComboBox"]').click(); cy.get('.euiComboBoxOptionsList__rowWrap .euiFilterSelectItem').eq(0).click(); - cy.get('.euiPopover__panel .euiFormControlLayoutIcons [data-test-subj="comboBoxToggleListButton"]').eq(0).click(); + cy.get( + '.euiPopover__panel .euiFormControlLayoutIcons [data-test-subj="comboBoxToggleListButton"]' + ) + .eq(0) + .click(); cy.get('.euiPopover__panel input').eq(1).type(`Test visulization_`); cy.get('[data-test-subj="eventExplorer__querySaveConfirm"]').click(); - cy.get('[data-test-subj="euiToastHeader"]').contains('Invalid value options configuration selected.').should('exist'); + cy.get('[data-test-subj="euiToastHeader"]') + .contains('Invalid value options configuration selected.') + .should('exist'); }); }); -describe('Renders Tree Map', () => { +describe('Render Table View', () => { beforeEach(() => { landOnEventVisualizations(); + querySearch(TEST_QUERIES[3].query, TEST_QUERIES[3].dateRangeDOM); + cy.get('[data-test-subj="workspace__dataTableViewSwitch"]').click(); }); - it('Renders Tree Map', () => { - renderTreeMapchart(); - cy.get('.euiFlexItem.euiFlexItem--flexGrowZero .euiButton__text').eq(2).click(); - cy.get('path.surface').should('have.length', 176); + it('Switch visualization for table view and verify table data', () => { + cy.get('.ag-header-cell-text').contains('max(AvgTicketPrice)').should('exist'); + cy.get('.ag-header-cell-text').contains('DestCountry').should('exist'); + cy.get('.ag-header-cell-text').contains('DestCityName').should('exist'); + cy.get('.ag-header-cell-text').contains('Carrier').should('exist'); }); - it('Renders Tree Map, add value parameters and verify Reset button click is working', () => { - renderTreeMapchart(); - cy.get('.euiFlexItem.euiFlexItem--flexGrowZero .euiButton__text').eq(2).click(); - cy.get('[data-test-subj="visualizeEditorResetButton"]').click(); - cy.get('#configPanel__panelOptions .euiFieldText').should('have.value', ''); - cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').should('have.value', ''); - cy.get('.euiComboBox__inputWrap.euiComboBox__inputWrap-isClearable').eq(1).should('have.value', ''); - cy.get('.euiComboBox__inputWrap.euiComboBox__inputWrap-isClearable').eq(2).should('have.value', ''); - cy.get('.euiComboBox__inputWrap.euiComboBox__inputWrap-isClearable').eq(3).should('have.value', ''); + it('Switch visualization for table view and change data table density', () => { + cy.get('.euiButtonEmpty__text').contains('Density').click(); + cy.get('.euiButtonIcon.euiButtonIcon--primary.euiButtonIcon--xSmall').eq(1).click(); + cy.get('.euiButtonIcon.euiButtonIcon--primary.euiButtonIcon--xSmall').eq(2).click(); }); - it('Renders Tree Map, Save and Delete Visualization', () => { - renderTreeMapchart(); - cy.get('.euiFlexItem.euiFlexItem--flexGrowZero .euiButton__text').eq(2).click(); - saveVisualizationAndVerify(); - cy.wait(delay * 4); - deleteVisualization(); - }); - - it('Render Tree Map chart and verify color theme under Chart styles options', () => { - renderTreeMapchart(); - cy.get('.euiTitle.euiTitle--xxsmall').contains('Color Theme').should('exist'); - cy.get('.euiSuperSelectControl').contains('Default').click(); - cy.get('.euiContextMenuItem__text .euiColorPalettePicker__item').eq(1).contains('Single color').click(); - cy.get('.euiFieldText.euiColorPicker__input.euiFieldText--withIcon').click(); - cy.get('[aria-label="Select #D36086 as the color"]').click(); - cy.get('.euiButton__text').contains('Preview').should('exist').click(); - cy.get('path[style*="rgb(29, 30, 36)"]').eq(0).should('exist'); - cy.get('.euiSuperSelectControl').click(); - cy.get('.euiColorPalettePicker__itemTitle').eq(1).contains('Reds').click(); - cy.get('.euiButton__text').contains('Preview').should('exist').click(); - cy.get('path[style*="rgb(68, 68, 68)"]').eq(0).should('exist'); - }); - - it('Traverse between root and parent node in Tree Map chart', () => { - querySearch(TEST_QUERIES[5].query, TEST_QUERIES[5].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Tree Map').type('{enter}'); - cy.get('#configPanel__panelOptions .euiFieldText').click().type('Tree Map'); - cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Tree Map'); - cy.get('.euiComboBox__inputWrap.euiComboBox__inputWrap-isClearable').eq(0).click(); - cy.get('.euiFormControlLayoutIcons [data-test-subj ="comboBoxToggleListButton"]').eq(1).click(); - cy.get('.euiComboBoxOption__content').eq(2).click(); - cy.get('.euiFormControlLayoutIcons [data-test-subj ="comboBoxToggleListButton"]').eq(2).click(); - cy.get('.euiComboBoxOption__content').eq(1).click(); - cy.get('.euiFormControlLayoutIcons [data-test-subj ="comboBoxToggleListButton"]').eq(3).click(); - cy.get('.euiComboBoxOption__content').eq(0).click(); - cy.wait(delay); - cy.get('.euiSuperSelectControl').click(); - cy.get('.euiColorPalettePicker__itemTitle').eq(1).contains('Reds').click(); - cy.get('.euiButton__text').contains('Preview').should('exist').click(); - cy.get('.slicetext[data-unformatted="US"]').click({ force: true }); - cy.wait(delay); - cy.get('.slicetext[data-unformatted*="Cleveland"]').click({ force: true }); - cy.get('text.slicetext').contains('100% of entry').should('exist'); - cy.get('.pathbar.cursor-pointer .slicetext[data-unformatted="US"]').click({ force: true }); - cy.wait(delay); - cy.get('.pathbar.cursor-pointer .slicetext[data-unformatted=" "]').click({ force: true }); - }); - - it('"No results found" message when user fails to select proper fields', () => { - querySearch(TEST_QUERIES[5].query, TEST_QUERIES[5].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Tree Map').type('{enter}'); - cy.get('#configPanel__panelOptions .euiFieldText').click().type('Tree Map'); - cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Tree Map'); - cy.get('.euiComboBox__inputWrap.euiComboBox__inputWrap-isClearable').eq(0).click(); - cy.get('.euiFormControlLayoutIcons [data-test-subj ="comboBoxToggleListButton"]').eq(3).click(); - cy.get('.euiComboBoxOption__content').eq(1).click(); - cy.wait(delay); - cy.get('.euiSuperSelectControl').click(); - cy.get('.euiColorPalettePicker__itemTitle').eq(1).contains('Reds').click(); - cy.get('.euiButton__text').contains('Preview').should('exist').click(); - cy.get('.euiTextColor.euiTextColor--subdued').contains('No results found').should('exist'); - }); - - it('Verify multicolored option under color theme', () => { - renderTreeMapchart(); - cy.get('.euiTitle.euiTitle--xxsmall').contains('Color Theme').should('exist'); - cy.get('.euiSuperSelectControl').contains('Default').click(); - cy.get('.euiContextMenuItem__text .euiColorPalettePicker__item').eq(1).contains('Single color').click(); - cy.get('.euiFieldText.euiColorPicker__input.euiFieldText--withIcon').click(); - cy.get('[aria-label="Select #54B399 as the color"]').should('exist').click(); - cy.get('.euiButton__text').contains('Preview').click(); - cy.get('.euiSuperSelectControl').click(); - cy.get('.euiContextMenuItem__text .euiColorPalettePicker__item').eq(2).contains('Multicolored').click(); - cy.wait(delay); - cy.get('.euiFormHelpText.euiFormRow__text').eq(1).contains('Child field').should('exist'); - cy.get('.euiFieldText.euiColorPicker__input.euiFieldText--withIcon').eq(0).click(); - cy.get('[aria-label="Select #D36086 as the color"]').click(); - cy.get('.euiFormHelpText.euiFormRow__text').eq(2).contains('Parent field').should('exist'); - cy.get('.euiFieldText.euiColorPicker__input.euiFieldText--withIcon').eq(1).click(); - cy.get('[aria-label="Select #CA8EAE as the color"]').click(); - cy.get('.euiButton__text').contains('Preview').click(); - cy.get('.trace.treemap path[style*="rgb(202, 142, 174)"]').should('exist'); - }); - - it('Parent field not available under color theme', () => { - querySearch(TEST_QUERIES[5].query, TEST_QUERIES[5].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Tree Map').type('{enter}'); - cy.get('#configPanel__panelOptions .euiFieldText').click().type('Tree Map'); - cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Tree Map'); - cy.get('.euiTitle.euiTitle--xxsmall').contains('Color Theme').should('exist'); - cy.get('.euiSuperSelectControl').contains('Default').click(); - cy.get('.euiContextMenuItem__text .euiColorPalettePicker__item').eq(1).contains('Single color').click(); - cy.get('.euiFieldText.euiColorPicker__input.euiFieldText--withIcon').click(); - cy.get('[aria-label="Select #54B399 as the color"]').should('exist').click(); - cy.get('.euiButton__text').contains('Preview').click(); - cy.get('.euiSuperSelectControl').click(); - cy.get('.euiContextMenuItem__text .euiColorPalettePicker__item').eq(2).contains('Multicolored').click(); - cy.wait(delay); - cy.get('.euiFormHelpText.euiFormRow__text').eq(1).contains('Child field').should('exist'); - cy.get('.euiFieldText.euiColorPicker__input.euiFieldText--withIcon').eq(0).click(); - cy.get('[aria-label="Select #D36086 as the color"]').click(); - cy.get('.euiFormHelpText.euiFormRow__text').contains('Parent field').should('not.exist'); - cy.get('.euiButton__text').contains('Preview').click(); - cy.get('.trace.treemap path[style*="rgb(211, 96, 134)"]').should('exist'); - }) -}); - -describe('Render Pie chart for Legend and single color contrast change', () => { - beforeEach(() => { - landOnEventVisualizations(); - }); - it('Render Pie chart and verify legends for Position Right and Bottom', () => { - renderPieChart(); - cy.get('[data-text="Right"]').should('have.text', 'Right'); - cy.get('[data-text="Right"] [data-test-subj="v"]').should('have.attr', 'checked'); - cy.get('[data-text="Bottom"]').should('have.text', 'Bottom').click(); - cy.get('[data-text="Bottom"] [data-test-subj="h"]').should('not.have.attr', 'checked'); - cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true }); - }); - - it('Render Pie chart and verify legends for Show and Hidden', () => { - renderPieChart(); - cy.get('[data-text="Show"]').should('have.text', 'Show'); - cy.get('[data-text="Show"] [data-test-subj="show"]').should('have.attr', 'checked'); - cy.get('[data-text="Hidden"]').should('have.text', 'Hidden').click(); - cy.get('[data-text="Hidden"] [data-test-subj="hidden"]').should('not.have.attr', 'checked'); - cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true }); + it('Switch visualization for table view and show and hide column', () => { + cy.get('.euiButtonEmpty__text').contains('Columns').click(); + cy.get('.euiSwitch__label').contains('DestCountry').click(); + cy.get('.ag-header-cell-text').contains('DestCountry').should('not.exist'); + cy.get('.euiSwitch__label').contains('Carrier').click(); + cy.get('.ag-header-cell-text').contains('Carrier').should('not.exist'); + cy.get('.euiSwitch__label').contains('DestCountry').click(); + cy.get('.ag-header-cell-text').contains('DestCountry').should('exist'); }); - it('Renders Pie chart with single color', () => { - renderPieChart(); - cy.get('.euiIEFlexWrapFix').eq(3).contains('Chart Styles').should('exist'); - cy.get('[data-test-subj="comboBoxInput"]').eq(3).click(); - cy.get('[name="Pie"]').click(); - cy.get('.euiSuperSelectControl').click(); - cy.get('.euiContextMenuItem.euiSuperSelect__item.euiSuperSelect__item--hasDividers').eq(1).click(); - cy.get('.euiFlexItem.euiFlexItem--flexGrowZero .euiButton__text').eq(2).click(); + it('Switch visualization for table view and see data in full screen', () => { + cy.get('.ag-header-cell-text').contains('max(AvgTicketPrice)').should('exist'); + cy.get('.ag-header-cell-text').contains('DestCountry').should('exist'); + cy.get('.ag-header-cell-text').contains('DestCityName').should('exist'); + cy.get('.ag-header-cell-text').contains('Carrier').should('exist'); + cy.get('.euiButtonEmpty__text').contains('Full screen').click(); + cy.wait(delay); + cy.get('body').type('{esc}'); cy.wait(delay); - }); -}); - -describe('Renders heatmap chart for Chart Style', () => { - beforeEach(() => { - landOnEventVisualizations(); - querySearch(TEST_QUERIES[4].query, TEST_QUERIES[4].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); - cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Heatmap').click(); - }); - - it('Renders heatmap chart with default Color Mode and Scheme', () => { - cy.get('.ewdrag.drag.cursor-ew-resize').should('be.visible'); - cy.get('g.g-gtitle text[data-unformatted|="avg(bytes)"]').should('exist'); - }); - - it('Renders heatmap chart with default Chart Style and Z-axis count()', () => { - cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]').click(); - cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('count()').click(); - cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); - cy.get('.ewdrag.drag.cursor-ew-resize').should('be.visible'); - cy.get('g.g-gtitle text[data-unformatted|="count()"]').should('exist'); - }); - - it('Renders heatmap chart with default Chart Style and Z-axis avg(bytes)', () => { - cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]').click(); - cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('avg(bytes)').click(); - cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); - cy.get('.ewdrag.drag.cursor-ew-resize').should('be.visible'); - cy.get('g.g-gtitle text[data-unformatted|="avg(bytes)"]').should('exist'); - }); - - it('Renders heatmap chart and Verify if Color Mode is Spectrum then by default Scheme is Reds', () => { - cy.get('[data-test-subj="comboBoxInput"]').eq(2).should('contain', 'Spectrum'); - cy.get('[aria-haspopup="true"]').eq(1).should('contain', 'Reds'); - cy.get('stop[stop-color="rgb(178, 10, 28)"]').should('exist'); - cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); - }); - - it('Renders heatmap chart and Verify if Color Mode is opacity then by default Scheme is Color', () => { - cy.get('[data-test-subj="comboBoxInput"]').eq(2).click(); - cy.get('.euiComboBoxOption__content').contains('opacity').click(); - cy.get('.euiTitle.euiTitle--xxsmall').eq(2).should('contain', 'Color'); - cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); - cy.get('stop[stop-color="rgb(19, 19, 19)"]').should('exist'); }); - it('Renders heatmap chart for Color Mode Spectrum and Change color of Scheme', () => { - cy.get('[aria-haspopup="true"]').eq(1).click(); - cy.get('.euiColorPalettePicker__itemTitle').contains('Red-Blue').click(); - cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); - cy.get('stop[stop-color="rgb(5, 10, 172)"]').should('exist'); - cy.get('stop[stop-color="rgb(178, 10, 28)"]').should('exist'); + it('Switch visualization for table view and sort the column data', () => { + cy.get('.ag-header-cell-text').contains('max(AvgTicketPrice)').click(); + cy.get('.ag-cell-value').contains('125.49737').should('exist'); + cy.get('.ag-header-cell-text').contains('max(AvgTicketPrice)').click(); + cy.get('.ag-cell-value').contains('1199.729').should('exist'); + cy.get('.ag-header-cell-text').contains('DestCountry').click(); + cy.get('.ag-cell-value').contains('AE').should('exist'); }); - it('Renders heatmap chart for Color Mode opacity and Change color', () => { - cy.get('[data-test-subj="comboBoxInput"]').eq(2).click(); - cy.get('.euiComboBoxOption__content').contains('opacity').click(); - cy.get('[data-test-subj="euiColorPickerAnchor"]').click(); - cy.get('.euiTitle.euiTitle--xxsmall').eq(2).should('contain', 'Color'); - cy.get('[aria-label="Select #D6BF57 as the color"]').click(); - cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); - cy.get('stop[stop-color="rgb(255, 255, 214)"]').should('exist'); - cy.get('stop[stop-color="rgb(214, 191, 87)"]').should('exist'); + it('Switch visualization for table view and verify pagination link', () => { + cy.get('[aria-label="Next page"]').click(); + cy.get('.ag-cell-value').contains('Vienna').should('exist'); + cy.get('[aria-label="Previous page"]').click(); + cy.get('.ag-cell-value').contains('Dubai').should('exist'); + cy.get('[aria-label="Page 4"]').contains('4').click(); + cy.get('.ag-cell-value').contains('Edmonton').should('exist'); }); -}); - -describe('Renders Tree Map for Parent Fields ', () => { - beforeEach(() => { - landOnEventVisualizations(); - }); - - it('Renders Tree Map and Add Multiple Parent', () => { - querySearch(TEST_QUERIES[7].query, TEST_QUERIES[7].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Tree Map').type('{enter}'); - cy.get('.euiButton__text').contains('+ Add Parent').click(); - cy.get('.euiComboBoxPlaceholder').contains('Select a field').should('exist'); - cy.get('.euiFormControlLayoutIcons [data-test-subj ="comboBoxToggleListButton"]').eq(3).click(); - cy.get('.euiComboBoxOption__content').eq(2).click(); - cy.get('.euiButton__text').contains('+ Add Parent').click(); - cy.get('.euiComboBoxPlaceholder').contains('Select a field').should('exist'); - cy.get('.euiFormControlLayoutIcons [data-test-subj ="comboBoxToggleListButton"]').eq(4).click(); - cy.get('.euiComboBoxOption__content').eq(1).click(); - cy.get('.euiButton__text').contains('+ Add Parent').click(); - cy.get('.euiComboBoxPlaceholder').contains('Select a field').should('exist'); - cy.get('.euiFormControlLayoutIcons [data-test-subj ="comboBoxToggleListButton"]').eq(5).click(); - cy.get('.euiComboBoxOption__content').eq(3).click(); - cy.get('.euiButton__text').contains('Preview').click(); - }); - - it('Renders Tree Map and Check Add/Delete Parent', () => { - querySearch(TEST_QUERIES[7].query, TEST_QUERIES[7].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Tree Map').type('{enter}'); - cy.get('.euiButton__text').contains('+ Add Parent').click(); - cy.get('.euiComboBoxPlaceholder').contains('Select a field').should('exist'); - cy.get('.euiButton__text').contains('+ Add Parent').click(); - cy.get('.euiComboBoxPlaceholder').eq(1).contains('Select a field').should('exist'); - cy.get('.euiFormRow__fieldWrapper .euiFlexItem').eq(1).click(); - cy.get('.euiFormRow__fieldWrapper .euiFlexItem').eq(1).click(); + it('Switch visualization for table view and rows per page data', () => { + cy.get('.euiButtonEmpty__text').eq('6').click(); + cy.get('.euiContextMenuItem__text').eq(1).click(); }); }); -describe('Renders Tree Map for Parent Fields Multicolor Option', () => { - beforeEach(() => { - landOnEventVisualizations(); - }); - - it('Renders Tree Map For Multiple Parent and Check Color Theme', () => { - querySearch(TEST_QUERIES[7].query, TEST_QUERIES[7].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Tree Map').type('{enter}'); - cy.get('.euiButton__text').contains('+ Add Parent').click(); - cy.get('.euiComboBoxPlaceholder').contains('Select a field').should('exist'); - cy.get('.euiFormControlLayoutIcons [data-test-subj ="comboBoxToggleListButton"]').eq(3).click(); - cy.get('.euiComboBoxOption__content').eq(2).click(); - cy.get('.euiButton__text').contains('+ Add Parent').click(); - cy.get('.euiComboBoxPlaceholder').contains('Select a field').should('exist'); - cy.get('.euiFormControlLayoutIcons [data-test-subj ="comboBoxToggleListButton"]').eq(4).click(); - cy.get('.euiComboBoxOption__content').eq(1).click(); - cy.get('.euiSuperSelectControl').contains('Default').click(); - cy.get('.euiColorPalettePicker__item').contains('Multicolored').click(); - cy.get('.euiButton__text').contains('Preview').click(); - cy.get('.euiFormHelpText.euiFormRow__text').contains('Parent 1 field').should('exist'); - cy.get('.euiFormHelpText.euiFormRow__text').contains('Parent 2 field').should('exist'); +describe('Render Time series chart/Line chart and verify Data configurations UI ', () => { + it('Render line chart and verify Data Configuration Panel', () => { + renderLineChartForDataConfig(); + DataConfigLineChart(); }); }); diff --git a/dashboards-observability/.cypress/utils/event_constants.js b/dashboards-observability/.cypress/utils/event_constants.js index 0dd369922..4db8c146d 100644 --- a/dashboards-observability/.cypress/utils/event_constants.js +++ b/dashboards-observability/.cypress/utils/event_constants.js @@ -110,3 +110,31 @@ export const renderPieChart = () => { cy.get('[data-test-subj="comboBoxToggleListButton"]').eq(0).click(); cy.get('[data-test-subj="comboBoxInput"]').eq(2).click(); }; + +export const renderDataConfig = () => { + cy.get('.euiResizablePanel.euiResizablePanel--middle').contains('Data Cofigurations'); + cy.get('.euiTitle.euiTitle--xxsmall').eq(1).contains('Dimensions').should('exist'); + cy.get('.first-division .euiFormLabel.euiFormRow__label').eq(0).contains('Aggregation'); + cy.get('[data-test-subj="comboBoxSearchInput"]').eq(0).click(); + cy.get('.euiComboBoxOption__content').eq(2).click(); + cy.get('.first-division .euiFormLabel.euiFormRow__label').eq(1).contains('Field'); + cy.get('[data-test-subj="comboBoxSearchInput"]').eq(1).click(); + cy.get('.euiComboBoxOption__content').eq(1).click(); + cy.get('.euiFieldText[placeholder="Custom label"]').eq(0).type('Average field'); + cy.get('.euiTitle.euiTitle--xxsmall').eq(2).contains('Metrics').should('exist'); + cy.get('.first-division .euiFormLabel.euiFormRow__label').eq(0).contains('Aggregation'); + cy.get('.euiFormRow__fieldWrapper .euiComboBox').eq(2).click(); + cy.get('.euiComboBoxOption__content').eq(4).click(); + cy.get('.first-division .euiFormLabel.euiFormRow__label').eq(4).click(); + cy.get('.euiComboBoxOption__content').eq(0).click(); + cy.get('.euiFieldText[placeholder="Custom label"]').eq(1).type('Min field'); + cy.get('.euiButton__text').contains('Right').click(); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').contains('Update chart').click(); + cy.get('.js-plotly-plot').should('exist'); +}; + +export const renderLineChart = () => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[5].query, TEST_QUERIES[5].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Line').type('{enter}'); +}; diff --git a/dashboards-observability/common/constants/explorer.ts b/dashboards-observability/common/constants/explorer.ts index 056007d9c..17612c44e 100644 --- a/dashboards-observability/common/constants/explorer.ts +++ b/dashboards-observability/common/constants/explorer.ts @@ -117,3 +117,16 @@ export const AGGREGATION_OPTIONS = [ label: 'STDDEV_POP', }, ]; + +// Data table constants +export const GRID_HEADER_COLUMN_MAX_WIDTH = '150px'; +export const GRID_PAGE_RANGE_DISPLAY = 5; +export const COLUMN_DEFAULT_MIN_WIDTH = 100; +export const GRID_PAGE_SIZES = [10, 50, 100]; +export const ROW_DENSITIES = [ + { icon: 'tableDensityExpanded', height: 55, selected: false }, + { icon: 'tableDensityNormal', height: 45, selected: false }, + { icon: 'tableDensityCompact', height: 35, selected: true }, +]; + +export const HEADER_HEIGHT = 35; diff --git a/dashboards-observability/common/types/explorer.ts b/dashboards-observability/common/types/explorer.ts index 3220add36..2204a6ab4 100644 --- a/dashboards-observability/common/types/explorer.ts +++ b/dashboards-observability/common/types/explorer.ts @@ -230,3 +230,17 @@ export interface LiveTailProps { isLiveTailPopoverOpen: boolean; dataTestSubj: string; } + +export interface ConfigListEntry { + label: string; + aggregation: string; + custom_label: string; + name: string; + side: string; + type: string; +} + +export interface ConfigList { + dimensions?: ConfigListEntry[]; + metrics?: ConfigListEntry[]; +} diff --git a/dashboards-observability/package.json b/dashboards-observability/package.json index 1bfdf7b80..d068e60c4 100644 --- a/dashboards-observability/package.json +++ b/dashboards-observability/package.json @@ -17,8 +17,11 @@ "@nteract/outputs": "^3.0.11", "@nteract/presentational-components": "^3.4.3", "@reduxjs/toolkit": "^1.6.1", + "ag-grid-community": "^27.3.0", + "ag-grid-react": "^27.3.0", "plotly.js-dist": "^2.2.0", "react-graph-vis": "^1.0.5", + "react-paginate": "^8.1.3", "react-plotly.js": "^2.5.1" }, "devDependencies": { diff --git a/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap b/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap index 126ff2374..3feb1f635 100644 --- a/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap +++ b/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap @@ -1377,58 +1377,158 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "sections": Array [ Object { "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", + "id": "legend", + "mapTo": "legend", + "name": "Legend", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, - "mapTo": "xaxis", - "name": "X-axis", + "mapTo": "showLegend", + "name": "Show Legend", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, }, Object { "component": null, - "isSingleSelection": false, - "mapTo": "yaxis", - "name": "Y-axis", + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, }, ], }, Object { "editor": [Function], - "id": "chart_options", - "mapTo": "chartOptions", - "name": "Chart options", + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", "schemas": Array [ Object { - "component": null, - "isSingleSelection": true, - "mapTo": "mode", + "component": [Function], + "eleType": "buttons", + "mapTo": "style", "name": "Mode", "props": Object { "defaultSelections": Array [ Object { - "modeId": "lines", + "id": "lines", "name": "Lines", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "modeId": "markers", - "name": "Markers", + "id": "lines", + "name": "Lines", }, Object { - "modeId": "lines", - "name": "Lines", + "id": "bar", + "name": "Bars", + }, + Object { + "id": "markers", + "name": "Points", + }, + Object { + "id": "lines+markers", + "name": "Lines + Points", + }, + ], + }, + }, + Object { + "component": [Function], + "eleType": "buttons", + "mapTo": "interpolation", + "name": "Interpolation", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "spline", + "name": "Smooth", + }, + ], + "options": Array [ + Object { + "id": "linear", + "name": "Linear", + }, + Object { + "id": "spline", + "name": "Smooth", + }, + Object { + "id": "hv", + "name": "Step before", }, Object { - "modeId": "lines+markers", - "name": "Lines + Markers", + "id": "vh", + "name": "Step after", }, ], }, }, + Object { + "component": [Function], + "defaultState": 2, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 40, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill Opacity", + "props": Object { + "max": 100, + }, + }, + Object { + "component": [Function], + "defaultState": 5, + "eleType": "slider", + "mapTo": "pointSize", + "name": "Point Size", + "props": Object { + "max": 40, + }, + }, ], }, Object { @@ -1610,58 +1710,158 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "sections": Array [ Object { "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", + "id": "legend", + "mapTo": "legend", + "name": "Legend", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, - "mapTo": "xaxis", - "name": "X-axis", + "mapTo": "showLegend", + "name": "Show Legend", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, }, Object { "component": null, - "isSingleSelection": false, - "mapTo": "yaxis", - "name": "Y-axis", + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, }, ], }, Object { "editor": [Function], - "id": "chart_options", - "mapTo": "chartOptions", - "name": "Chart options", + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", "schemas": Array [ Object { - "component": null, - "isSingleSelection": true, - "mapTo": "mode", + "component": [Function], + "eleType": "buttons", + "mapTo": "style", "name": "Mode", "props": Object { "defaultSelections": Array [ Object { - "modeId": "lines", + "id": "lines", "name": "Lines", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "modeId": "markers", - "name": "Markers", + "id": "lines", + "name": "Lines", }, Object { - "modeId": "lines", - "name": "Lines", + "id": "bar", + "name": "Bars", + }, + Object { + "id": "markers", + "name": "Points", }, Object { - "modeId": "lines+markers", - "name": "Lines + Markers", + "id": "lines+markers", + "name": "Lines + Points", }, ], }, }, + Object { + "component": [Function], + "eleType": "buttons", + "mapTo": "interpolation", + "name": "Interpolation", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "spline", + "name": "Smooth", + }, + ], + "options": Array [ + Object { + "id": "linear", + "name": "Linear", + }, + Object { + "id": "spline", + "name": "Smooth", + }, + Object { + "id": "hv", + "name": "Step before", + }, + Object { + "id": "vh", + "name": "Step after", + }, + ], + }, + }, + Object { + "component": [Function], + "defaultState": 2, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 40, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill Opacity", + "props": Object { + "max": 100, + }, + }, + Object { + "component": [Function], + "defaultState": 5, + "eleType": "slider", + "mapTo": "pointSize", + "name": "Point Size", + "props": Object { + "max": 40, + }, + }, ], }, Object { @@ -1897,58 +2097,158 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "sections": Array [ Object { "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", + "id": "legend", + "mapTo": "legend", + "name": "Legend", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, - "mapTo": "xaxis", - "name": "X-axis", + "mapTo": "showLegend", + "name": "Show Legend", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, }, Object { "component": null, - "isSingleSelection": false, - "mapTo": "yaxis", - "name": "Y-axis", + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, }, ], }, Object { "editor": [Function], - "id": "chart_options", - "mapTo": "chartOptions", - "name": "Chart options", + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", "schemas": Array [ Object { - "component": null, - "isSingleSelection": true, - "mapTo": "mode", + "component": [Function], + "eleType": "buttons", + "mapTo": "style", "name": "Mode", "props": Object { "defaultSelections": Array [ Object { - "modeId": "lines", + "id": "lines", "name": "Lines", }, ], - "dropdownList": Array [ + "options": Array [ + Object { + "id": "lines", + "name": "Lines", + }, Object { - "modeId": "markers", - "name": "Markers", + "id": "bar", + "name": "Bars", }, Object { - "modeId": "lines", - "name": "Lines", + "id": "markers", + "name": "Points", + }, + Object { + "id": "lines+markers", + "name": "Lines + Points", + }, + ], + }, + }, + Object { + "component": [Function], + "eleType": "buttons", + "mapTo": "interpolation", + "name": "Interpolation", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "spline", + "name": "Smooth", + }, + ], + "options": Array [ + Object { + "id": "linear", + "name": "Linear", }, Object { - "modeId": "lines+markers", - "name": "Lines + Markers", + "id": "spline", + "name": "Smooth", + }, + Object { + "id": "hv", + "name": "Step before", + }, + Object { + "id": "vh", + "name": "Step after", }, ], }, }, + Object { + "component": [Function], + "defaultState": 2, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 40, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill Opacity", + "props": Object { + "max": 100, + }, + }, + Object { + "component": [Function], + "defaultState": 5, + "eleType": "slider", + "mapTo": "pointSize", + "name": "Point Size", + "props": Object { + "max": 40, + }, + }, ], }, Object { @@ -2060,9 +2360,19 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` data={ Array [ Object { - "mode": "line", + "fill": "tozeroy", + "fillcolor": "rgba(60,161,199,0.2)", + "line": Object { + "color": "#3CA1C7", + "shape": "spline", + "width": 2, + }, + "marker": Object { + "size": 5, + }, + "mode": "lines", "name": "avg(FlightDelayMin)", - "type": "line", + "type": "scatter", "x": Array [ "BeatsWest", "Logstash Airways", @@ -2118,6 +2428,16 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "showgrid": false, "visible": true, }, + "yaxis1": Object { + "overlaying": "y", + "side": "left", + "tickfont": Object { + "color": "#3CA1C7", + }, + "titlefont": Object { + "color": "#3CA1C7", + }, + }, } } > @@ -2139,9 +2459,19 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` data={ Array [ Object { - "mode": "line", + "fill": "tozeroy", + "fillcolor": "rgba(60,161,199,0.2)", + "line": Object { + "color": "#3CA1C7", + "shape": "spline", + "width": 2, + }, + "marker": Object { + "size": 5, + }, + "mode": "lines", "name": "avg(FlightDelayMin)", - "type": "line", + "type": "scatter", "x": Array [ "BeatsWest", "Logstash Airways", @@ -2202,6 +2532,16 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "showgrid": false, "visible": true, }, + "yaxis1": Object { + "overlaying": "y", + "side": "left", + "tickfont": Object { + "color": "#3CA1C7", + }, + "titlefont": Object { + "color": "#3CA1C7", + }, + }, } } style={ diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap index f09eccbdf..435c32d43 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap @@ -820,58 +820,158 @@ exports[`Config panel component Renders config panel with visualization data 1`] "sections": Array [ Object { "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", + "id": "legend", + "mapTo": "legend", + "name": "Legend", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, - "mapTo": "xaxis", - "name": "X-axis", + "mapTo": "showLegend", + "name": "Show Legend", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, }, Object { "component": null, - "isSingleSelection": false, - "mapTo": "yaxis", - "name": "Y-axis", + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, }, ], }, Object { "editor": [Function], - "id": "chart_options", - "mapTo": "chartOptions", - "name": "Chart options", + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", "schemas": Array [ Object { - "component": null, - "isSingleSelection": true, - "mapTo": "mode", + "component": [Function], + "eleType": "buttons", + "mapTo": "style", "name": "Mode", "props": Object { "defaultSelections": Array [ Object { - "modeId": "lines", + "id": "lines", "name": "Lines", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "modeId": "markers", - "name": "Markers", + "id": "lines", + "name": "Lines", }, Object { - "modeId": "lines", - "name": "Lines", + "id": "bar", + "name": "Bars", + }, + Object { + "id": "markers", + "name": "Points", + }, + Object { + "id": "lines+markers", + "name": "Lines + Points", + }, + ], + }, + }, + Object { + "component": [Function], + "eleType": "buttons", + "mapTo": "interpolation", + "name": "Interpolation", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "spline", + "name": "Smooth", + }, + ], + "options": Array [ + Object { + "id": "linear", + "name": "Linear", + }, + Object { + "id": "spline", + "name": "Smooth", + }, + Object { + "id": "hv", + "name": "Step before", }, Object { - "modeId": "lines+markers", - "name": "Lines + Markers", + "id": "vh", + "name": "Step after", }, ], }, }, + Object { + "component": [Function], + "defaultState": 2, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 40, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill Opacity", + "props": Object { + "max": 100, + }, + }, + Object { + "component": [Function], + "defaultState": 5, + "eleType": "slider", + "mapTo": "pointSize", + "name": "Point Size", + "props": Object { + "max": 40, + }, + }, ], }, Object { @@ -1177,20 +1277,6 @@ exports[`Config panel component Renders config panel with visualization data 1`] "mapTo": "dataConfig", "name": "Data", "sections": Array [ - Object { - "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", - "schemas": Array [ - Object { - "component": null, - "isSingleSelection": true, - "mapTo": "zaxis", - "name": "Z-axis", - }, - ], - }, Object { "editor": [Function], "id": "chart_styles", @@ -1606,32 +1692,6 @@ exports[`Config panel component Renders config panel with visualization data 1`] "mapTo": "dataConfig", "name": "Data", "sections": Array [ - Object { - "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", - "schemas": Array [ - Object { - "component": null, - "isSingleSelection": true, - "mapTo": "valueField", - "name": "Value Field", - }, - Object { - "component": null, - "isSingleSelection": true, - "mapTo": "childField", - "name": "Child Field", - }, - Object { - "component": [Function], - "defaultState": Array [], - "mapTo": "parentFields", - "name": "Parent Fields", - }, - ], - }, Object { "editor": [Function], "id": "treemap_options", @@ -9274,207 +9334,215 @@ exports[`Config panel component Renders config panel with visualization data 1`] className="euiSpacer euiSpacer--s" /> - -
- - - Show Legend - - -
- + + Show Legend + + +
- - - - - - + + + + - - - -
-
-
+ + + + +
+ + +
- -
- - - Position - - -
- + + Position + + +
- - - - - - + + + + - - - -
-
-
+ + + + +
+ + +
- -
- - - Orientation - - -
- + + Orientation + + +
- - - - - - + + + + - - - -
-
-
+ + + + +
+ + +
- -
- - - Mode - - -
- + + Mode + + +
- - - - - - + + + + - - - -
-
-
+ + + + +
+ + +
= ({ title, legend, groupOptions, idSelected, handleButtonChange }) => ( - <> - -

{title}

-
- - - - + <> + +

{title}

+
+ +
+ +
+ ); diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_line_chart_styles.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_line_chart_styles.tsx new file mode 100644 index 000000000..23fde5433 --- /dev/null +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_line_chart_styles.tsx @@ -0,0 +1,95 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useMemo, useCallback } from 'react'; +import { EuiAccordion, EuiSpacer } from '@elastic/eui'; +import { ButtonGroupItem } from './config_button_group'; +import { IConfigPanelOptionSection } from '../../../../../../../../common/types/explorer'; + +export const ConfigLineChartStyles = ({ + visualizations, + schemas, + vizState, + handleConfigChange, + sectionName, + sectionId = 'chartStyles' +}: any) => { + const { data } = visualizations; + const { data: vizData = {}, metadata: { fields = [] } = {} } = data?.rawVizData; + + const handleConfigurationChange = useCallback( + (stateFiledName) => { + return (changes) => { + handleConfigChange({ + ...vizState, + [stateFiledName]: changes, + }); + }; + }, + [handleConfigChange, vizState] + ); + + /* To update the schema options based on current style mode selection */ + const currentSchemas = useMemo(() => { + if (!vizState?.style || vizState?.style === "lines") { + return schemas.filter((schema: IConfigPanelOptionSection) => schema.mapTo !== 'pointSize'); + } + if (vizState?.style === "bar") { + return schemas.filter((schema: IConfigPanelOptionSection) => !["interpolation", "pointSize"].includes(schema.mapTo)); + } + if (vizState?.style === "markers") { + return schemas.filter((schema: IConfigPanelOptionSection) => ["style", "pointSize"].includes(schema.mapTo)); + } + if (vizState?.style === 'lines+markers') { + return schemas.filter((schema: IConfigPanelOptionSection) => schema.mapTo !== 'interpolation'); + } + }, [vizState]); + + const dimensions = useMemo(() => + currentSchemas && currentSchemas.map((schema: IConfigPanelOptionSection, index: string) => { + const DimensionComponent = schema.component || ButtonGroupItem; + let params = { + title: schema.name, + vizState, + ...schema.props + }; + + if (schema.eleType === 'buttons') { + params = { + ...params, + legend: schema.name, + groupOptions: schema?.props?.options.map((btn: { name: string }) => ({ ...btn, label: btn.name })), + idSelected: vizState[schema.mapTo] || schema?.props?.defaultSelections[0]?.id, + handleButtonChange: handleConfigurationChange(schema.mapTo), + }; + } else if (schema.eleType === 'slider') { + params = { + ...params, + maxRange: schema?.props?.max, + currentRange: vizState[schema.mapTo] || schema?.defaultState, + handleSliderChange: handleConfigurationChange(schema.mapTo), + }; + } + + return ( + <> + + + + ) + }) + , [currentSchemas, vizState, handleConfigurationChange]); + + return ( + + {dimensions} + + ); +}; diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_treemap_parents.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_treemap_parents.tsx index b5bcaf16f..f92f9db1b 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_treemap_parents.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_treemap_parents.tsx @@ -9,11 +9,9 @@ import { EuiFormRow, EuiSpacer, EuiIcon, - EuiFlexGroup, - EuiFlexItem, - EuiTitle, EuiComboBox, EuiComboBoxOptionOption, + EuiText, } from '@elastic/eui'; import { isEmpty, uniqueId } from 'lodash'; @@ -61,37 +59,33 @@ export const ConfigTreemapParentFields = ({ dropdownList, selectedAxis, onSelect return ( <> - -

{`Parent Fields`}

-
- {!isEmpty(selectedAxis) && selectedAxis.map((_, index: number) => { return ( <> - -

{`Parent ${index + 1}`}

-
- - - - handleParentChange(options, index)} - aria-label="Use aria labels when no actual label is in use" + + handleParentDelete(index)} /> - - - handleParentDelete(index)} /> - - + + } + > + handleParentChange(options, index)} + aria-label="Use aria labels when no actual label is in use" + /> ); diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx index f5df572fe..865164c5e 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useContext } from 'react'; import { EuiTitle, EuiComboBox, @@ -24,39 +24,68 @@ import { import { AGGREGATION_OPTIONS } from '../../../../../../../../common/constants/explorer'; import { ButtonGroupItem } from './config_button_group'; import { visChartTypes } from '../../../../../../../../common/constants/shared'; +import { ConfigList } from '../../../../../../../../common/types/explorer'; +import { TabContext } from '../../../../../hooks'; -export const DataConfigPanelItem = ({ fieldOptionList, visualizations, tabID }: any) => { +export const DataConfigPanelItem = ({ fieldOptionList, visualizations }: any) => { const dispatch = useDispatch(); - const explorerVisualizations = useSelector(selectExplorerVisualization)[tabID]; - + const { tabId } = useContext(TabContext); + const explorerVisualizations = useSelector(selectExplorerVisualization)[tabId]; const { data } = visualizations; + const { data: vizData = {}, metadata: { fields = [] } = {} } = data?.rawVizData; - const newEntry = { label: '', aggregation: '', custom_label: '', name: '', side: 'right' }; + const initialConfigEntry = { + label: '', + aggregation: '', + custom_label: '', + name: '', + side: 'right', + type: '', + }; - const [configList, setConfigList] = useState({ - dimensions: [{ ...newEntry }], - metrics: [{ ...newEntry }], - }); + const [configList, setConfigList] = useState({}); useEffect(() => { - if (data.rawVizData?.dataConfig) { + if ( + data.rawVizData?.[visualizations.vis.name] && + data.rawVizData?.[visualizations.vis.name].dataConfig + ) { setConfigList({ - ...data.rawVizData?.dataConfig, + ...data.rawVizData[visualizations.vis.name].dataConfig, }); - } else if (data.defaultAxes.xaxis || data.defaultAxes.yaxis) { + } else if ( + visualizations.vis.name !== visChartTypes.HeatMap && + (data.defaultAxes.xaxis || data.defaultAxes.yaxis) + ) { const { xaxis, yaxis } = data.defaultAxes; setConfigList({ dimensions: [...(xaxis && xaxis)], metrics: [...(yaxis && yaxis)], }); + } else { + setConfigList({ + dimensions: [initialConfigEntry, initialConfigEntry], + metrics: [initialConfigEntry], + }); } - }, [data.defaultAxes, data.rawVizData?.dataConfig]); + }, [ + data.defaultAxes, + data.rawVizData?.[visualizations.vis.name]?.dataConfig, + visualizations.vis.name, + ]); const updateList = (value: string, index: number, name: string, field: string) => { let list = { ...configList }; let listItem = { ...list[name][index] }; - listItem = { ...listItem, [field]: value }; + listItem = { + ...listItem, + [field]: value, + }; + if (field === 'label') { + listItem.type = value !== '' ? fields.find((x) => x.name === value)?.type : ''; + listItem.name = value; + } const newList = { ...list, [name]: [ @@ -68,59 +97,57 @@ export const DataConfigPanelItem = ({ fieldOptionList, visualizations, tabID }: setConfigList(newList); }; - const onFieldOptionChange = (e, index: number, name: string) => { - updateList(e[0]?.label, index, name, 'label'); - }; - - const onAggregationChange = (e, index: number, name: string) => { - updateList(e[0]?.label, index, name, 'aggregation'); - }; - - const onCustomLabelChange = (e, index: number, name: string) => { - updateList(e.target.value, index, name, 'custom_label'); - }; - - const handleSideChange = (id, value, index: number, name: string) => { - updateList(id, index, name, 'side'); - }; - const handleServiceRemove = (index: number, name: string) => { const list = { ...configList }; const arr = [...list[name]]; arr.splice(index, 1); - const newList = { ...list, [name]: arr }; - setConfigList(newList); + const y = { ...list, [name]: arr }; + setConfigList(y); }; const handleServiceAdd = (name: string) => { - let newList = { ...configList, [name]: [...configList[name], newEntry] }; + let newList = { ...configList, [name]: [...configList[name], initialConfigEntry] }; setConfigList(newList); }; const updateChart = () => { dispatch( renderExplorerVis({ - tabId: tabID, + tabId, data: { ...explorerVisualizations, - dataConfig: { - metrics: configList.metrics, - dimensions: configList.dimensions, + [visualizations.vis.name]: { + dataConfig: { + metrics: configList.metrics, + dimensions: configList.dimensions, + }, }, }, }) ); }; + const isPositionButtonVisible = (sectionName: string) => + sectionName === 'metrics' && + (visualizations.vis.name === visChartTypes.Line || + visualizations.vis.name === visChartTypes.Bar); + const getCommonUI = (lists, sectionName: string) => + lists && lists.map((singleField, index: number) => ( <>
+ {sectionName === 'dimensions' && visualizations.vis.name === visChartTypes.HeatMap && ( + +
{index === 0 ? 'X-Axis' : 'Y-Axis'}
+
+ )} onAggregationChange(e, index, sectionName)} + onChange={(e) => + updateList(e.length > 0 ? e[0].label : '', index, sectionName, 'aggregation') + } /> @@ -150,7 +179,9 @@ export const DataConfigPanelItem = ({ fieldOptionList, visualizations, tabID }: singleSelection={{ asPlainText: true }} options={fieldOptionList} selectedOptions={singleField.label ? [{ label: singleField.label }] : []} - onChange={(e) => onFieldOptionChange(e, index, sectionName)} + onChange={(e) => + updateList(e.length > 0 ? e[0].label : '', index, sectionName, 'label') + } /> @@ -158,12 +189,12 @@ export const DataConfigPanelItem = ({ fieldOptionList, visualizations, tabID }: onCustomLabelChange(e, index, sectionName)} + onChange={(e) => updateList(e.target.value, index, sectionName, 'custom_label')} aria-label="Use aria labels when no actual label is in use" /> - {sectionName === 'metrics' && visualizations.vis.name === visChartTypes.Line && ( + {isPositionButtonVisible(sectionName) && ( - handleSideChange(id, value, index, sectionName) - } + idSelected={singleField.side || 'right'} + handleButtonChange={(id: string) => updateList(id, index, sectionName, 'side')} /> )} - {lists.length - 1 === index && ( - + {visualizations.vis.name !== visChartTypes.HeatMap && lists.length - 1 === index && ( + { + const dispatch = useDispatch(); + const explorerVisualizations = useSelector(selectExplorerVisualization)[tabID]; + + const { data } = visualizations; + const { data: vizData = {}, metadata: { fields = [] } = {} } = data?.rawVizData; + + const newEntry = { label: '', name: '' }; + + const [configList, setConfigList] = useState({ + dimensions: [{ childField: { ...newEntry }, parentFields: [] }], + metrics: [{ valueField: { ...newEntry } }], + }); + + useEffect(() => { + if ( + data.rawVizData?.[visualizations.vis.name] && + data.rawVizData?.[visualizations.vis.name].dataConfig + ) { + setConfigList({ + ...data.rawVizData[visualizations.vis.name].dataConfig, + }); + } else if (data.defaultAxes.xaxis || data.defaultAxes.yaxis) { + const { xaxis, yaxis } = data.defaultAxes; + setConfigList({ + dimensions: [{ childField: { ...xaxis[0] }, parentFields: [] }], + metrics: [{ valueField: { ...yaxis[0] } }], + }); + } + }, [ + data.defaultAxes, + data.rawVizData?.[visualizations.vis.name]?.dataConfig, + visualizations.vis.name, + ]); + + const updateList = (configName: string, fieldName: string, value: string | any[]) => { + let list = { ...configList }; + let listItem = { ...list[configName][0] }; + + const newField = { + label: value, + name: value, + type: value !== '' ? fields.find((x) => x.name === value)?.type : '', + }; + listItem = { ...listItem, [fieldName]: typeof value === 'string' ? newField : value }; + const newList = { + ...list, + [configName]: [listItem], + }; + setConfigList(newList); + }; + + const updateChart = () => { + dispatch( + renderExplorerVis({ + tabId: tabID, + data: { + ...explorerVisualizations, + [visualizations.vis.name]: { + dataConfig: { + metrics: configList.metrics, + dimensions: configList.dimensions, + }, + }, + }, + }) + ); + }; + + return ( + <> + +

Data Configurations

+
+ + +

Dimensions

+
+
+ + + + updateList('dimensions', 'childField', val.length > 0 ? val[0].label : '') + } + /> + + + + ({ label: opt.label, name: opt.label }))} + selectedAxis={configList.dimensions[0].parentFields} + onSelectChange={(val) => updateList('dimensions', 'parentFields', val)} + /> + + +
+ +

Metrics

+
+
+ + + + updateList('metrics', 'valueField', val.length > 0 ? val[0].label : '') + } + /> + + +
+ + + + + Update chart + + + + ); +}; diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx index c5763cbb0..8c4163647 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx @@ -16,6 +16,8 @@ import { ConfigPanel } from './config_panel'; import { Sidebar } from '../sidebar'; import { DataConfigPanelItem } from './config_panel/config_panes/config_controls/data_config_panel_item'; import { TabContext } from '../../hooks'; +import { visChartTypes } from '../../../../../common/constants/shared'; +import { TreemapConfigPanelItem } from './config_panel/config_panes/config_controls/treemap_config_panel_item'; interface IExplorerVisualizationsProps { query: IQuery; curVisId: string; @@ -51,13 +53,13 @@ export const ExplorerVisualizations = ({ const fieldOptionList = fields.map((name) => { return { label: name.name }; - }); + }) return ( {(EuiResizablePanel, EuiResizableButton) => ( <> - +
- +
- + {curVisId === visChartTypes.TreeMap ? ( + + ) : ( + + )}
diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/workspace_panel/workspace_panel.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/workspace_panel/workspace_panel.tsx index aaf9a4542..0fa68e4eb 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/workspace_panel/workspace_panel.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/workspace_panel/workspace_panel.tsx @@ -21,7 +21,9 @@ interface IWorkSpacePanel { export function WorkspacePanel({ visualizations }: IWorkSpacePanel) { const [isTableViewOn, setIsTableViewOn] = useState(false); const VisualizationPanel = useMemo(() => { - return ; + return ( + + ); }, [visualizations]); return ( @@ -64,11 +66,13 @@ export function WorkspacePanel({ visualizations }: IWorkSpacePanel) { - - {isTableViewOn ? - : VisualizationPanel} + + {isTableViewOn ? : VisualizationPanel} diff --git a/dashboards-observability/public/components/event_analytics/utils/utils.tsx b/dashboards-observability/public/components/event_analytics/utils/utils.tsx index 35272fe5a..dd16885aa 100644 --- a/dashboards-observability/public/components/event_analytics/utils/utils.tsx +++ b/dashboards-observability/public/components/event_analytics/utils/utils.tsx @@ -120,8 +120,8 @@ export const populateDataGrid = ( )} {explorerFields?.queriedFields && - explorerFields?.queriedFields?.length > 0 && - explorerFields.selectedFields?.length === 0 ? null : ( + explorerFields?.queriedFields?.length > 0 && + explorerFields.selectedFields?.length === 0 ? null : ( {header2}{body2} diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/data_table.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/data_table.test.tsx.snap index 6031f476d..d9be0e68b 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/data_table.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/data_table.test.tsx.snap @@ -1,649 +1,127 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Data table component Renders data table component 1`] = ` - + - + - - - - -
- -
-
-
- -
- - - - - - + rowHeight={35} + suppressPaginationPanel={true} + /> + + `; diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap index 8a061dfd6..94701f7db 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap @@ -494,9 +494,19 @@ exports[`Line component Renders line component 1`] = ` data={ Array [ Object { - "mode": "line", - "name": "count()", - "type": "line", + "fill": "tozeroy", + "fillcolor": "rgba(60,161,199,0.2)", + "line": Object { + "color": "#3CA1C7", + "shape": "spline", + "width": 2, + }, + "marker": Object { + "size": 5, + }, + "mode": "lines", + "name": undefined, + "type": "scatter", "x": Array [ "error", "info", @@ -505,14 +515,7 @@ exports[`Line component Renders line component 1`] = ` "success", "warning", ], - "y": Array [ - 154, - 1753, - 116, - 468, - 1964, - 219, - ], + "y": undefined, }, ] } @@ -522,6 +525,9 @@ exports[`Line component Renders line component 1`] = ` "#8C55A3", ], "height": 220, + "legend": Object { + "orientation": "v", + }, "margin": Object { "b": 15, "l": 60, @@ -531,6 +537,16 @@ exports[`Line component Renders line component 1`] = ` }, "showlegend": true, "title": "", + "yaxis1": Object { + "overlaying": "y", + "side": "left", + "tickfont": Object { + "color": "#3CA1C7", + }, + "titlefont": Object { + "color": "#3CA1C7", + }, + }, } } > @@ -543,9 +559,19 @@ exports[`Line component Renders line component 1`] = ` data={ Array [ Object { - "mode": "line", - "name": "count()", - "type": "line", + "fill": "tozeroy", + "fillcolor": "rgba(60,161,199,0.2)", + "line": Object { + "color": "#3CA1C7", + "shape": "spline", + "width": 2, + }, + "marker": Object { + "size": 5, + }, + "mode": "lines", + "name": undefined, + "type": "scatter", "x": Array [ "error", "info", @@ -554,14 +580,7 @@ exports[`Line component Renders line component 1`] = ` "success", "warning", ], - "y": Array [ - 154, - 1753, - 116, - 468, - 1964, - 219, - ], + "y": undefined, }, ] } @@ -577,8 +596,7 @@ exports[`Line component Renders line component 1`] = ` "height": 220, "hovermode": "closest", "legend": Object { - "orientation": "h", - "traceorder": "normal", + "orientation": "v", }, "margin": Object { "b": 15, @@ -600,6 +618,16 @@ exports[`Line component Renders line component 1`] = ` "showgrid": true, "zeroline": false, }, + "yaxis1": Object { + "overlaying": "y", + "side": "left", + "tickfont": Object { + "color": "#3CA1C7", + }, + "titlefont": Object { + "color": "#3CA1C7", + }, + }, } } style={ diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/data_table.test.tsx b/dashboards-observability/public/components/visualizations/charts/__tests__/data_table.test.tsx index e6826ac2a..216a3fa29 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/data_table.test.tsx +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/data_table.test.tsx @@ -3,27 +3,24 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { configure, mount } from 'enzyme'; +import { configure, mount, shallow } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import React from 'react'; import { waitFor } from '@testing-library/react'; import { DataTable } from '../data_table/data_table'; import { TEST_VISUALIZATIONS_DATA } from '../../../../../test/event_analytics_constants'; +import { AgGridReact } from 'ag-grid-react'; describe('Data table component', () => { configure({ adapter: new Adapter() }); it('Renders data table component', async () => { - const wrapper = mount( - - ); - - wrapper.update(); - + const gridWrapper = shallow(); + const agGridReactObj = gridWrapper.find(AgGridReact); + agGridReactObj.simulate('gridReady'); + expect(agGridReactObj).toBeTruthy(); await waitFor(() => { - expect(wrapper).toMatchSnapshot(); + expect(gridWrapper).toMatchSnapshot(); }); }); }); diff --git a/dashboards-observability/public/components/visualizations/charts/data_table/data_table.scss b/dashboards-observability/public/components/visualizations/charts/data_table/data_table.scss new file mode 100644 index 000000000..86cb0b90e --- /dev/null +++ b/dashboards-observability/public/components/visualizations/charts/data_table/data_table.scss @@ -0,0 +1,63 @@ +.pagination-wrapper { + display: flex; + justify-content: space-between; +} +.custom-pagination-container { + display: inline-flex; + padding: 10px 5px; +} + +.custom-pagination-container li { + font-size: 14px; + padding: 0 5px; + display: flex; + align-items: flex-end; + font-weight: 600; +} + +.custom-pagination-container .euiIcon:focus { + background: transparent !important; +} +.custom-pagination-container a:focus { + animation: none !important; +} +.custom-pagination-container a { + cursor: pointer; +} + +.custom-pagination-container .disabled { + cursor: default; +} +.custom-pagination-container a { + color: #343741; +} +.dark-theme a { + color: #dfe5ef; +} +.custom-pagination-container .selected a { + color: #1ba9f5; + text-decoration: underline; + cursor: default; +} + +.custom-overlay { + display: flex; + align-items: flex-start; + padding: 10px; + position: fixed; + top: 100px; + left: 0; + right: 0; + bottom: 0; + z-index: 99999 !important; + overflow-y: scroll; + overflow-x: hidden; +} + +.custom-overlay-light { + background-color: #fff; +} + +.custom-overlay-dark { + background-color: #16171c; +} diff --git a/dashboards-observability/public/components/visualizations/charts/data_table/data_table.tsx b/dashboards-observability/public/components/visualizations/charts/data_table/data_table.tsx index 912a9dc2c..e6121c21f 100644 --- a/dashboards-observability/public/components/visualizations/charts/data_table/data_table.tsx +++ b/dashboards-observability/public/components/visualizations/charts/data_table/data_table.tsx @@ -3,102 +3,225 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useState, useMemo, useCallback } from 'react'; -import { EuiInMemoryTable, EuiDataGrid } from '@elastic/eui'; +import React, { + useState, + useMemo, + useCallback, + useRef, + useEffect, +} from 'react'; +import { + EuiFlexGroup, + EuiFlexItem, +} from '@elastic/eui'; -export const DataTable = ({ visualizations }: any) => { +// ag-data-grid +import { AgGridReact } from 'ag-grid-react'; +import 'ag-grid-community/dist/styles/ag-grid.css'; +import 'ag-grid-community/dist/styles/ag-theme-alpine.css'; +import 'ag-grid-community/dist/styles/ag-theme-alpine-dark.css'; + +// styles +import './data_table.scss'; + +// grid elements +import { CustomOverlay, RowConfigType, GridHeader, } from "./data_table_header" +import { GridFooter } from "./data_table_footer" + +// constants +import { + COLUMN_DEFAULT_MIN_WIDTH, + HEADER_HEIGHT, +} from '../../../../../common/constants/explorer'; + + +const doubleValueGetter = (params) => { + return params.data[params.column.colId]; +}; + +export const DataTable = ({ visualizations, layout, config }: any) => { const { data: vizData, jsonData, metadata: { fields = [] }, } = visualizations.data.rawVizData; + useEffect(() => { + document.addEventListener('keydown', hideGridFullScreenHandler); + return () => { + document.removeEventListener('keydown', hideGridFullScreenHandler); + }; + }, []); + + // rows and columns const raw_data = [...jsonData]; - // Pagination - const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 }); - const onChangeItemsPerPage = useCallback( - (pageSize) => - setPagination((pagination) => ({ - ...pagination, - pageSize, - pageIndex: 0, - })), - [setPagination] - ); - const onChangePage = useCallback( - (pageIndex) => setPagination((pagination) => ({ ...pagination, pageIndex })), - [setPagination] - ); + const columns = fields.map((field: any) => { + return { + lockVisible: true, + columnsMenuParams: { + suppressColumnFilter: true, + suppressColumnSelectAll: true, + suppressColumnExpandAll: true, + }, + headerName: field.name, + field: field.name, + colId: field.name, + ...(field.type === 'double' && { + valueGetter: doubleValueGetter, + }), + }; + }); - // Sorting - const [sortingColumns, setSortingColumns] = useState([]); - const onSort = useCallback( - (sortingColumns) => { - setSortingColumns(sortingColumns); - }, - [setSortingColumns] - ); + // ag-grid-react bindings + const gridRef = useRef(); + const gridRefFullScreen = useRef(); + const [pageSize, setPageSize] = useState(10); + const [columnVisibility, setColumnVisibility] = useState([]); + const [isFullScreen, setIsFullScreen] = useState(false); + const [selectedRowDensity, setSelectedRowDensity] = useState({ + icon: 'tableDensityCompact', + height: 35, + selected: true, + }); + // pagination + const [activePage, setActivePage] = useState(0); + const pageCount = Math.ceil(raw_data.length / pageSize); - // Sort data - let data = useMemo(() => { - return [...raw_data].sort((a, b) => { - for (let i = 0; i < sortingColumns.length; i++) { - const column = sortingColumns[i]; - const aValue = a[column.id]; - const bValue = b[column.id]; + const defaultColDef = useMemo(() => { + return { + sortable: true, + resizable: true, + filter: true, + flex: 1, + suppressMenu: true, + minWidth: COLUMN_DEFAULT_MIN_WIDTH, + headerHeight: 400, + }; + }, []); - if (aValue < bValue) return column.direction === 'asc' ? -1 : 1; - if (aValue > bValue) return column.direction === 'asc' ? 1 : -1; + const onPageSizeChanged = useCallback( + (val: number) => { + setPageSize(val); + gridRef.current.api.paginationSetPageSize(val); + setActivePage(0); + gridRef.current.api.paginationGoToPage(0); + if (isFullScreen) { + gridRefFullScreen.current.api.paginationSetPageSize(val); + gridRefFullScreen.current.api.paginationGoToPage(0); } + }, + [isFullScreen] + ); - return 0; + const selectDensityHandler = useCallback((value: RowConfigType) => { + setSelectedRowDensity({ ...value }); + gridRef.current.api.forEachNode((rowNode) => { + if (rowNode.data) { + rowNode.setRowHeight(value.height); + } }); - }, [sortingColumns]); - - // Pagination - data = useMemo(() => { - const rowStart = pagination.pageIndex * pagination.pageSize; - const rowEnd = Math.min(rowStart + pagination.pageSize, data.length); - return data.slice(rowStart, rowEnd); - }, [data, pagination]); - - const columns = fields.map((field) => { - return { - id: field.name, - }; - }); - - // Column visibility - const [visibleColumns, setVisibleColumns] = useState(columns.map(({ id }) => id)); + gridRef.current.api.onRowHeightChanged(); + }, []); - const renderCellValue = useMemo(() => { - return ({ rowIndex, columnId }) => { - let adjustedRowIndex = rowIndex; + const columnVisiblityHandler = useCallback((visible: boolean, field: string) => { + const isExisting = columnVisibility.findIndex((column) => column === field); + if (visible) { + if (isExisting > -1) { + columnVisibility.splice(isExisting, 1); + gridRef?.current?.columnApi?.setColumnsVisible([field], true); + } + } else { + columnVisibility.push(field); + gridRef?.current?.columnApi?.setColumnsVisible([field], false); + } + setColumnVisibility([...columnVisibility]); + }, []); - // If we are doing the pagination (instead of leaving that to the grid) - // then the row index must be adjusted as `data` has already been pruned to the page size - adjustedRowIndex = rowIndex - pagination.pageIndex * pagination.pageSize; + const goToPage = ({ selected }: { selected: number }) => { + setActivePage(selected); + gridRef.current.api.paginationGoToPage(selected); + if (isFullScreen) { + gridRefFullScreen.current.api.paginationGoToPage(selected); + } + }; - return data.hasOwnProperty(adjustedRowIndex) ? data[adjustedRowIndex][columnId] : null; - }; - }, [data, pagination.pageIndex, pagination.pageSize]); + const setIsFullScreenHandler = (val: boolean) => { + setIsFullScreen(val); + }; + const hideGridFullScreenHandler = (e: any) => { + if (e.key === 'Escape') { + setIsFullScreen(false); + } + }; return ( - + <> + + { + gridRef?.current?.api.setHeaderHeight(HEADER_HEIGHT); + }} + /> + + {isFullScreen && ( + + + + { + gridRefFullScreen?.current?.api.setHeaderHeight(HEADER_HEIGHT); + }} + /> + + + + + + + )} + ); }; + + diff --git a/dashboards-observability/public/components/visualizations/charts/data_table/data_table_footer.tsx b/dashboards-observability/public/components/visualizations/charts/data_table/data_table_footer.tsx new file mode 100644 index 000000000..f8350ddd2 --- /dev/null +++ b/dashboards-observability/public/components/visualizations/charts/data_table/data_table_footer.tsx @@ -0,0 +1,126 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useState } from 'react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiPopover, + EuiIcon, + EuiContextMenuPanel, + EuiContextMenuItem, +} from '@elastic/eui'; + +// pagination +import ReactPaginate from 'react-paginate'; + +// theme +import { uiSettingsService } from '../../../../../common/utils'; + +// constants +import { + GRID_PAGE_RANGE_DISPLAY, + GRID_PAGE_SIZES, +} from '../../../../../common/constants/explorer'; + +export const GridFooter = ({ + onPageSizeChanged, + pageSize, + activePage, + goToPage, + pageCount, +}: { + onPageSizeChanged: (val: number) => void; + goToPage: ({ selected }: { selected: number }) => void; + pageSize: number; + activePage: number; + pageCount: number; +}) => { + return ( + + + + + + } + forcePage={activePage} + onPageChange={goToPage} + pageRangeDisplayed={GRID_PAGE_RANGE_DISPLAY} + pageCount={pageCount} + previousLabel={} + renderOnZeroPageCount={null} + /> + + + ); +}; + +export const PageSizePopover = ({ + onPageSizeChanged, + pageSize, +}: { + onPageSizeChanged: (size: number) => void; + pageSize: number; +}) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const onButtonClick = () => setIsPopoverOpen((isPopoverOpen) => !isPopoverOpen); + const closePopover = () => setIsPopoverOpen(false); + + const button = ( + + Rows per page: {pageSize} + + ); + + const items = () => { + return GRID_PAGE_SIZES.map((i: number) => ( + { + onPageSizeChanged(i); + closePopover(); + }} + > + {i} rows + + )); + }; + + return ( + + + + ); +}; diff --git a/dashboards-observability/public/components/visualizations/charts/data_table/data_table_header.tsx b/dashboards-observability/public/components/visualizations/charts/data_table/data_table_header.tsx new file mode 100644 index 000000000..1c2d2b034 --- /dev/null +++ b/dashboards-observability/public/components/visualizations/charts/data_table/data_table_header.tsx @@ -0,0 +1,195 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useState, ReactChildren, ReactChild } from 'react'; +import { + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiPopover, + EuiSwitch, + EuiIcon, +} from '@elastic/eui'; + +// theme +import { uiSettingsService } from '../../../../../common/utils'; + +// constants +import { + GRID_HEADER_COLUMN_MAX_WIDTH, + ROW_DENSITIES, +} from '../../../../../common/constants/explorer'; + +export interface RowConfigType { + icon: string; + height: number; + selected: boolean; +} + +export const CustomOverlay = ({ + children, +}: { + children: ReactChild | ReactChild[] | ReactChildren | ReactChildren[]; +}) => { + return ( +
+ {children} +
+ ); +}; + +export const GridHeader = ({ + isFullScreen, + setIsFullScreenHandler, + selectedRowDensity, + selectDensityHandler, + columnVisiblityHandler, + columns, + columnVisibility, +}: { + isFullScreen: boolean; + setIsFullScreenHandler: (v: boolean) => void; + selectedRowDensity: any; + selectDensityHandler: (v: RowConfigType) => void; + columnVisiblityHandler: (visible: boolean, field: string) => void; + columns: any; + columnVisibility: any; +}) => { + return ( + + + + + + + + + setIsFullScreenHandler(true)} + > + Full screen + + + {isFullScreen && ( + setIsFullScreenHandler(false)} + style={{ position: 'absolute', right: 20, cursor: 'pointer', top: 20 }} + /> + )} + + ); +}; + +export const DensityPopover = ({ + selectDensityHandler, + selectedDensity, +}: { + selectedDensity: any; + selectDensityHandler: (data: RowConfigType) => void; +}) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const onButtonClick = () => setIsPopoverOpen((isPopoverOpen) => !isPopoverOpen); + const closePopover = () => setIsPopoverOpen(false); + + const button = ( + + Density + + ); + + return ( + + + {ROW_DENSITIES.map((i: RowConfigType, index: number) => ( + + selectDensityHandler(i)} + display={selectedDensity.icon === i.icon ? 'fill' : 'base'} + iconType={i.icon} + aria-label="Next" + /> + + ))} + + + ); +}; + +export const ColumnVisiblityPopover = ({ + columnVisibility, + columns, + columnVisiblityHandler, +}: { + columns: any; + columnVisibility: any; + columnVisiblityHandler: (visible: boolean, field: string) => void; +}) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const onButtonClick = () => setIsPopoverOpen((isPopoverOpen) => !isPopoverOpen); + const closePopover = () => setIsPopoverOpen(false); + + const button = ( + + Columns + + ); + + return ( + + + {columns.map((i: any, index: number) => { + return ( + + { + columnVisiblityHandler(e.target.checked, i.field); + }} + compressed + /> + + ); + })} + + + ); +}; diff --git a/dashboards-observability/public/components/visualizations/charts/lines/line.tsx b/dashboards-observability/public/components/visualizations/charts/lines/line.tsx index 4ce620efd..5d21eead1 100644 --- a/dashboards-observability/public/components/visualizations/charts/lines/line.tsx +++ b/dashboards-observability/public/components/visualizations/charts/lines/line.tsx @@ -8,8 +8,11 @@ import { take, isEmpty, last } from 'lodash'; import { Plt } from '../../plotly/plot'; import { AvailabilityUnitType } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_availability'; import { ThresholdUnitType } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_thresholds'; +import { DefaultChartStyles, FILLOPACITY_DIV_FACTOR, PLOTLY_COLOR } from '../../../../../common/constants/shared'; +import { hexToRgb } from '../../../../components/event_analytics/utils/utils'; export const Line = ({ visualizations, layout, config }: any) => { + const { DefaultMode, Interpolation, LineWidth, FillOpacity, MarkerSize, LegendPosition, ShowLegend } = DefaultChartStyles; const { data = {}, metadata: { fields }, @@ -20,15 +23,20 @@ export const Line = ({ visualizations, layout, config }: any) => { layoutConfig = {}, availabilityConfig = {}, } = visualizations?.data?.userConfigs; - const xaxis = - dataConfig?.valueOptions && dataConfig.valueOptions.xaxis ? dataConfig.valueOptions.xaxis : []; - const yaxis = - dataConfig?.valueOptions && dataConfig.valueOptions.xaxis ? dataConfig.valueOptions.yaxis : []; + + const dataConfigTab = visualizations.data?.rawVizData?.line?.dataConfig && visualizations.data.rawVizData.line.dataConfig; + const xaxis = dataConfigTab?.dimensions ? dataConfigTab?.dimensions : []; + const yaxis = dataConfigTab?.metrics ? dataConfigTab?.metrics : []; + const lastIndex = fields.length - 1; - const mode = - dataConfig?.chartOptions && dataConfig.chartOptions.mode && dataConfig.chartOptions.mode[0] - ? dataConfig.chartOptions.mode[0].modeId - : 'line'; + + const mode = dataConfig?.chartStyles?.style || DefaultMode; + const lineShape = dataConfig?.chartStyles?.interpolation || Interpolation; + const lineWidth = dataConfig?.chartStyles?.lineWidth || LineWidth; + const showLegend = !(dataConfig?.legend?.showLegend && dataConfig.legend.showLegend !== ShowLegend); + const legendPosition = dataConfig?.legend?.position || LegendPosition; + const markerSize = dataConfig?.chartStyles?.pointSize || MarkerSize; + const fillOpacity = dataConfig?.chartStyles?.fillOpacity !== undefined ? dataConfig?.chartStyles?.fillOpacity / FILLOPACITY_DIV_FACTOR : FillOpacity / FILLOPACITY_DIV_FACTOR; let valueSeries; if (!isEmpty(xaxis) && !isEmpty(yaxis)) { @@ -37,21 +45,69 @@ export const Line = ({ visualizations, layout, config }: any) => { valueSeries = defaultAxes.yaxis || take(fields, lastIndex > 0 ? lastIndex : 1); } + let multiMetrics = {}; const [calculatedLayout, lineValues] = useMemo(() => { - let calculatedLineValues = valueSeries.map((field: any) => { + const isBarMode = mode === 'bar'; + + let calculatedLineValues = valueSeries.map((field: any, index: number) => { + const fillColor = hexToRgb(PLOTLY_COLOR[index % PLOTLY_COLOR.length], fillOpacity); + const barMarker = { + color: fillColor, + line: { + color: PLOTLY_COLOR[index], + width: lineWidth + } + }; + const fillProperty = { + fill: 'tozeroy', + fillcolor: fillColor, + }; + const multiYaxis = { yaxis: `y${index + 1}` }; + multiMetrics = { + ...multiMetrics, + [`yaxis${index + 1}`]: { + // title: `yaxis${index + 1} title`, TODO: need to add title + titlefont: { color: PLOTLY_COLOR[index] }, + tickfont: { color: PLOTLY_COLOR[index] }, + overlaying: 'y', + side: index === 0 ? 'left' : field.side || "right" + } + } + return { x: data[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name], - y: data[field.name], - type: 'line', - name: field.name, + y: data[field.label], + type: isBarMode ? 'bar' : 'scatter', + name: field.label, mode, + ...!['bar', 'markers'].includes(mode) && fillProperty, + line: { + shape: lineShape, + width: lineWidth, + color: PLOTLY_COLOR[index], + }, + marker: { + size: markerSize, + ...isBarMode && barMarker, + }, + ...(index >= 1 && multiYaxis) }; }); + let layoutForBarMode = { + barmode: 'group', + }; const mergedLayout = { ...layout, ...layoutConfig.layout, title: dataConfig?.panelOptions?.title || layoutConfig.layout?.title || '', + legend: { + ...layout.legend, + orientation: legendPosition, + }, + showlegend: showLegend, + ...isBarMode && layoutForBarMode, + ...multiMetrics && multiMetrics, }; if (dataConfig.thresholds || availabilityConfig.level) { diff --git a/dashboards-observability/public/components/visualizations/charts/lines/line_type.ts b/dashboards-observability/public/components/visualizations/charts/lines/line_type.ts index 20e452388..ed8e1ed8d 100644 --- a/dashboards-observability/public/components/visualizations/charts/lines/line_type.ts +++ b/dashboards-observability/public/components/visualizations/charts/lines/line_type.ts @@ -12,11 +12,16 @@ import { ConfigEditor } from '../../../event_analytics/explorer/visualizations/c import { ConfigValueOptions, ConfigThresholds, + ConfigLineChartStyles, + ConfigLegend, } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls'; import { ConfigAvailability } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_availability'; - +import { DefaultChartStyles } from '../../../../../common/constants/shared'; +import { ButtonGroupItem } from '../../../../../public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_button_group'; +import { SliderConfig } from '../../../../../public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_style_slider'; const sharedConfigs = getPlotlySharedConfigs(); const VIS_CATEGORY = getPlotlyCategory(); +const { DefaultMode, Interpolation, LineWidth, FillOpacity, MarkerSize, LegendPosition, ShowLegend } = DefaultChartStyles; export const createLineTypeDefinition = (params: any = {}) => ({ name: 'line', @@ -41,45 +46,103 @@ export const createLineTypeDefinition = (params: any = {}) => ({ editor: VizDataPanel, sections: [ { - id: 'value_options', - name: 'Value options', - editor: ConfigValueOptions, - mapTo: 'valueOptions', + id: 'legend', + name: 'Legend', + editor: ConfigLegend, + mapTo: 'legend', schemas: [ { - name: 'X-axis', - isSingleSelection: true, + name: 'Show Legend', + mapTo: 'showLegend', component: null, - mapTo: 'xaxis', + props: { + options: [ + { name: 'Show', id: "show" }, + { name: 'Hidden', id: "hidden" }, + ], + defaultSelections: [{ name: 'Show', id: ShowLegend }], + }, }, { - name: 'Y-axis', - isSingleSelection: false, + name: 'Position', + mapTo: 'position', component: null, - mapTo: 'yaxis', + props: { + options: [ + { name: 'Right', id: 'v' }, + { name: 'Bottom', id: 'h' }, + ], + defaultSelections: [{ name: 'Right', id: LegendPosition }], + }, }, ], }, { - id: 'chart_options', - name: 'Chart options', - editor: ConfigValueOptions, - mapTo: 'chartOptions', + id: 'chart_styles', + name: 'Chart styles', + editor: ConfigLineChartStyles, + mapTo: 'chartStyles', schemas: [ { name: 'Mode', - isSingleSelection: true, - component: null, - mapTo: 'mode', + component: ButtonGroupItem, + mapTo: 'style', + eleType: 'buttons', props: { - dropdownList: [ - { name: 'Markers', modeId: 'markers' }, - { name: 'Lines', modeId: 'lines' }, - { name: 'Lines + Markers', modeId: 'lines+markers' }, + options: [ + { name: 'Lines', id: 'lines' }, + { name: 'Bars', id: 'bar' }, + { name: 'Points', id: 'markers' }, + { name: 'Lines + Points', id: 'lines+markers' } ], - defaultSelections: [{ name: 'Lines', modeId: 'lines' }], + defaultSelections: [{ name: 'Lines', id: DefaultMode }], }, }, + { + name: 'Interpolation', + component: ButtonGroupItem, + mapTo: 'interpolation', + eleType: 'buttons', + props: { + options: [ + { name: 'Linear', id: 'linear' }, + { name: 'Smooth', id: 'spline' }, + { name: 'Step before', id: 'hv' }, + { name: 'Step after', id: 'vh' }, + ], + defaultSelections: [{ name: 'Smooth', id: Interpolation }], + }, + }, + { + name: 'Line width', + component: SliderConfig, + mapTo: 'lineWidth', + defaultState: LineWidth, + eleType: 'slider', + props:{ + max: 10, + } + }, + { + name: 'Fill Opacity', + component: SliderConfig, + mapTo: 'fillOpacity', + defaultState: FillOpacity, + eleType: 'slider', + props:{ + max: 100, + } + }, + { + name: 'Point Size', + component: SliderConfig, + mapTo: 'pointSize', + defaultState: MarkerSize, + eleType: 'slider', + props:{ + max: 40, + } + }, ], }, { diff --git a/dashboards-observability/public/components/visualizations/charts/maps/heatmap.tsx b/dashboards-observability/public/components/visualizations/charts/maps/heatmap.tsx index 9155dea04..176c5421d 100644 --- a/dashboards-observability/public/components/visualizations/charts/maps/heatmap.tsx +++ b/dashboards-observability/public/components/visualizations/charts/maps/heatmap.tsx @@ -29,30 +29,26 @@ export const HeatMap = ({ visualizations, layout, config }: any) => { if (fields.length < 3) return ; - const xaxisField = fields[fields.length - 2]; - const yaxisField = fields[fields.length - 1]; - const zMetrics = - dataConfig?.valueOptions && dataConfig?.valueOptions.zaxis - ? dataConfig?.valueOptions.zaxis[0] - : yaxis.length > 0 - ? yaxis[0] - : fields[fields.length - 3]; - const uniqueYaxis = uniq(data[yaxisField.name]); - const uniqueXaxis = uniq(data[xaxisField.name]); - const uniqueYaxisLength = uniqueYaxis.length; - const uniqueXaxisLength = uniqueXaxis.length; + const xaxisField = visualizations.data?.rawVizData?.heatmap?.dataConfig?.dimensions[0]; + const yaxisField = visualizations.data?.rawVizData?.heatmap?.dataConfig?.dimensions[1]; + const zMetrics = visualizations.data?.rawVizData?.heatmap?.dataConfig?.metrics[0]; if ( isEmpty(xaxisField) || isEmpty(yaxisField) || isEmpty(zMetrics) || - isEmpty(data[xaxisField.name]) || - isEmpty(data[yaxisField.name]) || - isEmpty(data[zMetrics.name]) || + isEmpty(data[xaxisField.label]) || + isEmpty(data[yaxisField.label]) || + isEmpty(data[zMetrics.label]) || indexOf(NUMERICAL_FIELDS, zMetrics.type) < 0 ) return ; + const uniqueYaxis = uniq(data[yaxisField.label]); + const uniqueXaxis = uniq(data[xaxisField.label]); + const uniqueYaxisLength = uniqueYaxis.length; + const uniqueXaxisLength = uniqueXaxis.length; + const colorField = dataConfig?.chartStyles ? dataConfig?.chartStyles.colorMode && dataConfig?.chartStyles.colorMode[0].name === OPACITY ? dataConfig?.chartStyles.color ?? HEATMAP_SINGLE_COLOR @@ -75,8 +71,8 @@ export const HeatMap = ({ visualizations, layout, config }: any) => { const buckets = {}; // maps bukcets to metrics - for (let i = 0; i < data[xaxisField.name].length; i++) { - buckets[`${data[xaxisField.name][i]},${data[yaxisField.name][i]}`] = data[zMetrics.name][i]; + for (let i = 0; i < data[xaxisField.label].length; i++) { + buckets[`${data[xaxisField.label][i]},${data[yaxisField.label][i]}`] = data[zMetrics.label][i]; } // initialize empty 2 dimensional array, inner loop for each xaxis field, outer loop for yaxis diff --git a/dashboards-observability/public/components/visualizations/charts/maps/heatmap_type.ts b/dashboards-observability/public/components/visualizations/charts/maps/heatmap_type.ts index 4b8872931..8bcd56697 100644 --- a/dashboards-observability/public/components/visualizations/charts/maps/heatmap_type.ts +++ b/dashboards-observability/public/components/visualizations/charts/maps/heatmap_type.ts @@ -44,20 +44,6 @@ export const createMapsVisDefinition = () => ({ mapTo: 'dataConfig', editor: VizDataPanel, sections: [ - { - id: 'value_options', - name: 'Value options', - editor: ConfigValueOptions, - mapTo: 'valueOptions', - schemas: [ - { - name: 'Z-axis', - isSingleSelection: true, - component: null, - mapTo: 'zaxis', - }, - ], - }, { id: 'chart_styles', name: 'Chart Styles', diff --git a/dashboards-observability/public/components/visualizations/charts/maps/treemap_type.ts b/dashboards-observability/public/components/visualizations/charts/maps/treemap_type.ts index 80b8232b7..eb4bc7d6e 100644 --- a/dashboards-observability/public/components/visualizations/charts/maps/treemap_type.ts +++ b/dashboards-observability/public/components/visualizations/charts/maps/treemap_type.ts @@ -12,7 +12,6 @@ import { ConfigValueOptions, ColorPalettePicker, ConfigChartOptions, - ConfigTreemapParentFields, } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls'; import { DEFAULT_PALETTE, COLOR_PALETTES } from '../../../../../common/constants/colors'; @@ -45,32 +44,6 @@ export const createTreeMapDefinition = (params: BarTypeParams = {}) => ({ mapTo: 'dataConfig', editor: VizDataPanel, sections: [ - { - id: 'value_options', - name: 'Value options', - editor: ConfigValueOptions, - mapTo: 'valueOptions', - schemas: [ - { - name: 'Value Field', - isSingleSelection: true, - component: null, - mapTo: 'valueField', - }, - { - name: 'Child Field', - isSingleSelection: true, - component: null, - mapTo: 'childField', - }, - { - name: 'Parent Fields', - component: ConfigTreemapParentFields, - mapTo: 'parentFields', - defaultState: [], - }, - ], - }, { id: 'treemap_options', name: 'Treemap', diff --git a/dashboards-observability/public/components/visualizations/charts/maps/treemaps.tsx b/dashboards-observability/public/components/visualizations/charts/maps/treemaps.tsx index 6922f075c..f13c69ec5 100644 --- a/dashboards-observability/public/components/visualizations/charts/maps/treemaps.tsx +++ b/dashboards-observability/public/components/visualizations/charts/maps/treemaps.tsx @@ -23,22 +23,21 @@ export const TreeMap = ({ visualizations, layout, config }: any) => { const { dataConfig = {}, layoutConfig = {} } = visualizations?.data?.userConfigs; const childField = - dataConfig?.valueOptions && - dataConfig?.valueOptions.childField && - !isEmpty(dataConfig?.valueOptions.childField) - ? dataConfig?.valueOptions.childField[0] + visualizations.data?.rawVizData?.tree_map?.dataConfig?.dimensions && + visualizations.data?.rawVizData?.tree_map?.dataConfig?.dimensions[0].childField + ? visualizations.data?.rawVizData?.tree_map?.dataConfig?.dimensions[0].childField : fields[fields.length - 1]; const parentFields = - dataConfig?.valueOptions && dataConfig.valueOptions?.parentFields - ? dataConfig?.valueOptions.parentFields + visualizations.data?.rawVizData?.tree_map?.dataConfig?.dimensions && + visualizations.data?.rawVizData?.tree_map?.dataConfig.dimensions[0].parentFields + ? visualizations.data?.rawVizData?.tree_map?.dataConfig?.dimensions[0].parentFields : []; const valueField = - dataConfig?.valueOptions && - dataConfig.valueOptions?.valueField && - !isEmpty(dataConfig?.valueOptions.valueField) - ? dataConfig?.valueOptions.valueField[0] + visualizations.data?.rawVizData?.tree_map?.dataConfig?.metrics && + visualizations.data?.rawVizData?.tree_map?.dataConfig.metrics[0].valueField + ? visualizations.data?.rawVizData?.tree_map?.dataConfig?.metrics[0].valueField : fields[0]; const colorField = diff --git a/dashboards-observability/public/components/visualizations/charts/pie/pie.tsx b/dashboards-observability/public/components/visualizations/charts/pie/pie.tsx index 0a07e985a..5dc4dd204 100644 --- a/dashboards-observability/public/components/visualizations/charts/pie/pie.tsx +++ b/dashboards-observability/public/components/visualizations/charts/pie/pie.tsx @@ -16,11 +16,11 @@ export const Pie = ({ visualizations, layout, config }: any) => { } = visualizations.data.rawVizData; const { defaultAxes } = visualizations.data; const { dataConfig = {}, layoutConfig = {} } = visualizations?.data?.userConfigs; - const xaxis = visualizations.data?.rawVizData?.dataConfig?.dimensions - ? visualizations.data?.rawVizData?.dataConfig?.dimensions + const xaxis = visualizations.data?.rawVizData?.pie?.dataConfig?.dimensions + ? visualizations.data?.rawVizData?.pie?.dataConfig?.dimensions : []; - const yaxis = visualizations.data?.rawVizData?.dataConfig?.metrics - ? visualizations.data?.rawVizData?.dataConfig?.metrics + const yaxis = visualizations.data?.rawVizData?.pie?.dataConfig?.metrics + ? visualizations.data?.rawVizData?.pie?.dataConfig?.metrics : []; const type = dataConfig?.chartStyles?.mode ? dataConfig?.chartStyles?.mode[0]?.modeId : 'pie'; const lastIndex = fields.length - 1; diff --git a/dashboards-observability/yarn.lock b/dashboards-observability/yarn.lock index c205c9bf8..1bff758f9 100644 --- a/dashboards-observability/yarn.lock +++ b/dashboards-observability/yarn.lock @@ -325,6 +325,18 @@ acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== +ag-grid-community@^27.3.0: + version "27.3.0" + resolved "https://registry.yarnpkg.com/ag-grid-community/-/ag-grid-community-27.3.0.tgz#b1e94a58026aaf2f0cd7920e35833325b5e762c7" + integrity sha512-R5oZMXEHXnOLrmhn91J8lR0bv6IAnRcU6maO+wKLMJxffRWaAYFAuw1jt7bdmcKCv8c65F6LEBx4ykSOALa9vA== + +ag-grid-react@^27.3.0: + version "27.3.0" + resolved "https://registry.yarnpkg.com/ag-grid-react/-/ag-grid-react-27.3.0.tgz#fe06647653f8b0b349b8e613aab8ea2e07915562" + integrity sha512-2bs9YfJ/shvBZQLLjny4NFvht+ic6VtpTPO0r3bHHOhlL3Fjx2rGvS6AHSwfvu+kJacHCta30PjaEbX8T3UDyw== + dependencies: + prop-types "^15.8.1" + ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -2133,6 +2145,15 @@ prop-types@^15.5.10, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" +prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + property-information@^5.0.0: version "5.6.0" resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" @@ -2216,7 +2237,7 @@ react-graph-vis@^1.0.5: vis-data "^7.1.2" vis-network "^9.0.0" -react-is@^16.8.1, react-is@^16.8.6: +react-is@^16.13.1, react-is@^16.8.1, react-is@^16.8.6: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -2248,6 +2269,13 @@ react-markdown@^4.0.0: unist-util-visit "^1.3.0" xtend "^4.0.1" +react-paginate@^8.1.3: + version "8.1.3" + resolved "https://registry.yarnpkg.com/react-paginate/-/react-paginate-8.1.3.tgz#cd6f3cb8a56b47617a61a6a52e3d7c662ad9b91b" + integrity sha512-zBp80DBRcaeBnAeHUfbGKD0XHfbGNUolQ+S60Ymfs8o7rusYaJYZMAt1j93ADDNLlzRmJ0tMF/NeTlcdKf7dlQ== + dependencies: + prop-types "^15.6.1" + react-plotly.js@^2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/react-plotly.js/-/react-plotly.js-2.5.1.tgz#11182bf599ef11a0dbfcd171c6f5645535a2b486" From dd298c220ffbb1bab47b265e259448be60ec8fd9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Jul 2022 10:44:01 -0700 Subject: [PATCH 032/135] Bump moment from 2.29.2 to 2.29.4 in /dashboards-observability (#845) Bumps [moment](https://github.com/moment/moment) from 2.29.2 to 2.29.4. - [Release notes](https://github.com/moment/moment/releases) - [Changelog](https://github.com/moment/moment/blob/develop/CHANGELOG.md) - [Commits](https://github.com/moment/moment/compare/2.29.2...2.29.4) --- updated-dependencies: - dependency-name: moment dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- dashboards-observability/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dashboards-observability/yarn.lock b/dashboards-observability/yarn.lock index 1bff758f9..95f119e37 100644 --- a/dashboards-observability/yarn.lock +++ b/dashboards-observability/yarn.lock @@ -1913,9 +1913,9 @@ mkdirp@^0.5.4: minimist "^1.2.5" moment@^2.27.0: - version "2.29.2" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.2.tgz#00910c60b20843bcba52d37d58c628b47b1f20e4" - integrity sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg== + version "2.29.4" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" + integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== ms@2.0.0: version "2.0.0" From 1a95ba52bc72a2043510edd19284c2d36443b59e Mon Sep 17 00:00:00 2001 From: abasatwar Date: Tue, 26 Jul 2022 20:36:38 +0530 Subject: [PATCH 033/135] Sprint2 (#47) (#868) * graph style section UI schema Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/visualizations/charts/lines/line_type.ts * changes for style mode and interpolation Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/visualizations/charts/lines/line_type.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts * lineWidth integration for line mode Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/visualizations/charts/lines/line.tsx # dashboards-observability/public/components/visualizations/charts/lines/line_type.ts # Conflicts: # dashboards-observability/common/constants/shared.ts # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_style_slider.tsx * changes for Legend and Orientation in Line Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_button_group.tsx # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_legend.tsx # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_style_slider.tsx * point size and Bar Alignment changes Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/visualizations/charts/lines/line.tsx # dashboards-observability/public/components/visualizations/charts/lines/line_type.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/common/constants/shared.ts # dashboards-observability/common/types/explorer.ts * implemented fill opacity for line chart Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/visualizations/charts/lines/line.tsx # Conflicts: # dashboards-observability/public/components/event_analytics/utils/utils.tsx * changes for line width and fill opacity in bar mode and removed mode from chartOption Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/common/constants/shared.ts * updated bar mode opacity in line chart Signed-off-by: rinku-kumar-psl * refactored the config chart style code Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/common/constants/shared.ts # dashboards-observability/common/types/explorer.ts * snapshot updated and code refactored Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/utils/utils.tsx * type added to new component Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_button_group.tsx # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_style_slider.tsx * review comments addressed Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/utils/utils.tsx # Conflicts: # dashboards-observability/common/constants/shared.ts * cypress test case added and resolve button label wraping issue Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/.cypress/integration/1_event_analytics.spec.js # dashboards-observability/.cypress/utils/event_constants.js # Conflicts: # dashboards-observability/.cypress/integration/1_event_analytics.spec.js # Conflicts: # dashboards-observability/.cypress/integration/1_event_analytics.spec.js * multi matrices changes for Line Signed-off-by: rinku-kumar-psl * dimensions and metrics UI changes for time-series Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/common/constants/explorer.ts # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx # dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx * made data config pannel collapsable and initial fields render Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx # dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx * code refactored Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx * snapshot updated and handled corner cases Signed-off-by: rinku-kumar-psl * code styling fixes and added TODO comment Signed-off-by: rinku-kumar-psl * sequence change for dimensions and metrics Signed-off-by: Deepak Nevde * Sprint1 (#14) * graph style section UI schema Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/visualizations/charts/lines/line_type.ts * changes for style mode and interpolation Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/visualizations/charts/lines/line_type.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts * lineWidth integration for line mode Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/visualizations/charts/lines/line.tsx # dashboards-observability/public/components/visualizations/charts/lines/line_type.ts # Conflicts: # dashboards-observability/common/constants/shared.ts # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_style_slider.tsx * changes for Legend and Orientation in Line Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_button_group.tsx # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_legend.tsx # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_style_slider.tsx * point size and Bar Alignment changes Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/visualizations/charts/lines/line.tsx # dashboards-observability/public/components/visualizations/charts/lines/line_type.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/common/constants/shared.ts # dashboards-observability/common/types/explorer.ts * implemented fill opacity for line chart Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/visualizations/charts/lines/line.tsx # Conflicts: # dashboards-observability/public/components/event_analytics/utils/utils.tsx * changes for line width and fill opacity in bar mode and removed mode from chartOption Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/common/constants/shared.ts * updated bar mode opacity in line chart Signed-off-by: rinku-kumar-psl * refactored the config chart style code Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts # Conflicts: # dashboards-observability/common/constants/shared.ts # dashboards-observability/common/types/explorer.ts * snapshot updated and code refactored Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/utils/utils.tsx * type added to new component Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_button_group.tsx # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_style_slider.tsx * review comments addressed Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/utils/utils.tsx # Conflicts: # dashboards-observability/common/constants/shared.ts * cypress test case added and resolve button label wraping issue Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/.cypress/integration/1_event_analytics.spec.js # dashboards-observability/.cypress/utils/event_constants.js # Conflicts: # dashboards-observability/.cypress/integration/1_event_analytics.spec.js # Conflicts: # dashboards-observability/.cypress/integration/1_event_analytics.spec.js * multi matrices changes for Line Signed-off-by: rinku-kumar-psl * dimensions and metrics UI changes for time-series Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/common/constants/explorer.ts # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx # dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx * made data config pannel collapsable and initial fields render Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx # dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx * code refactored Signed-off-by: rinku-kumar-psl # Conflicts: # dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx * snapshot updated and handled corner cases Signed-off-by: rinku-kumar-psl * code styling fixes and added TODO comment Signed-off-by: rinku-kumar-psl * data config reviewed code added Signed-off-by: Deepak Nevde * Text correction Signed-off-by: Deepak Nevde * Conflicts resolved Signed-off-by: Deepak Nevde * table view: eui table replaced with ag-grid Signed-off-by: Ramneet Chopra * drag-drop issue fixed Signed-off-by: Ramneet Chopra * test case of data_table updated Signed-off-by: Ramneet Chopra * feedback comments resolved Signed-off-by: Ramneet Chopra * grid height issue:fixed Signed-off-by: Ramneet Chopra * column height, value getter for type double Signed-off-by: Ramneet Chopra * data_table elements moved to separate Signed-off-by: Ramneet Chopra * footer components Signed-off-by: Ramneet Chopra * cypress test cases for table view Signed-off-by: Ramneet Chopra * enhancement for heatmap with new UI Signed-off-by: Shankha Das * line chart test cases Signed-off-by: Shankha Das * console logs removed Signed-off-by: Shankha Das * updated value options ui for treemap Signed-off-by: Mrunal Zambre * removed console Signed-off-by: Mrunal Zambre * Fixes of sprint1 for new ui implementation (#12) Signed-off-by: ruchika-narang Co-authored-by: rinku-kumar-psl Co-authored-by: Deepak Nevde Co-authored-by: Ramneet Chopra Co-authored-by: Shankha Das Co-authored-by: Mrunal Zambre Co-authored-by: ruchika-narang <79983862+ruchika-narang@users.noreply.github.com> * Latest code added Signed-off-by: Deepak Nevde * Collapsapable button position change to top Signed-off-by: Deepak Nevde * table view: eui table replaced with ag-grid Signed-off-by: Ramneet Chopra * drag-drop issue fixed Signed-off-by: Ramneet Chopra * test case of data_table updated Signed-off-by: Ramneet Chopra * feedback comments resolved Signed-off-by: Ramneet Chopra * grid height issue:fixed Signed-off-by: Ramneet Chopra * column height, value getter for type double Signed-off-by: Ramneet Chopra * data_table elements moved to separate Signed-off-by: Ramneet Chopra * footer components Signed-off-by: Ramneet Chopra * cypress test cases for table view Signed-off-by: Ramneet Chopra * data config reviewed code added Signed-off-by: Deepak Nevde * Text correction Signed-off-by: Deepak Nevde * Conflicts resolved Signed-off-by: Deepak Nevde * enhancement for heatmap with new UI Signed-off-by: Shankha Das * line chart test cases Signed-off-by: Shankha Das * console logs removed Signed-off-by: Shankha Das * updated value options ui for treemap Signed-off-by: Mrunal Zambre * removed console Signed-off-by: Mrunal Zambre * sprint1-visualization-fixes. Signed-off-by: abasatwar * added colorscale config options for treemap Signed-off-by: Mrunal Zambre * code review comment resolved Signed-off-by: Deepak Nevde * added config option to sort treemap sectors Signed-off-by: Mrunal Zambre * changes to resctct duplicte options Signed-off-by: rinku-kumar-psl * Updated and Added test scripts for Treemap chart along with data config and worked on reassembling the event_constants.js file Signed-off-by: Pratibha Pandey * Removed unwanted code Signed-off-by: Pratibha Pandey * implementation of histogram with new UI Signed-off-by: ruchika-narang * gauge chart added Signed-off-by: Ramneet Chopra * Pie chart enhancement, multi labels change Signed-off-by: Deepak Nevde * threshold text fix Signed-off-by: Ramneet Chopra * cypress test cases added Signed-off-by: Ramneet Chopra * Code review comment resolved Signed-off-by: Deepak Nevde * reset fixed, unused imports removed, PR checks fixed Signed-off-by: Ramneet Chopra * single timestamp dimension, no data dsiplay, label rotate, label/legend size Signed-off-by: Ramneet Chopra * layout fixed for primary y axis Signed-off-by: Ramneet Chopra * change of screen size of no data found and visualization Signed-off-by: Shankha Das * changes for restriction of duplicate fields on Data Config and only numeric field selection to metrics Signed-off-by: rinku-kumar-psl * line label replaced with time series Signed-off-by: Ramneet Chopra * snapshot tests Signed-off-by: Ramneet Chopra * Removed unwanted spaces Signed-off-by: Pratibha Pandey * initialize default params for DimensonComponent and formatted the codes Signed-off-by: rinku-kumar-psl * pr review feedback Signed-off-by: Ramneet Chopra * updated preview functionality for charts Signed-off-by: Mrunal Zambre * updated snapshots Signed-off-by: Mrunal Zambre * Worked on review comments Signed-off-by: Pratibha Pandey * changed variable names Signed-off-by: Mrunal Zambre * code review changes done Signed-off-by: rinku-kumar-psl * added empty line at end. Signed-off-by: abasatwar * updated variable names Signed-off-by: Mrunal Zambre * updated snapshots Signed-off-by: Mrunal Zambre * Added pie chart test cases Signed-off-by: Pratibha Pandey * updated snapshots Signed-off-by: ruchika-narang * Removed consoles Signed-off-by: ruchika-narang * Worked on conflicts Signed-off-by: Pratibha Pandey * color selector added Signed-off-by: Ramneet Chopra * updated snapshots Signed-off-by: ruchika-narang * UI updated as recommended Signed-off-by: Shankha Das * Added legend to heatmap Signed-off-by: ruchika-narang * data_config_panel_item timeseries code removed Signed-off-by: Ramneet Chopra * bar chart with multiple dimension and metrics, timestamp Signed-off-by: abasatwar * limit no. of gauge option added Signed-off-by: Ramneet Chopra * threshold limit added, gauge default parameters moved to constants/explorer Signed-off-by: Ramneet Chopra * legend placement added Signed-off-by: Ramneet Chopra * yarn test Signed-off-by: Ramneet Chopra * snapshot updated Signed-off-by: abasatwar * snapshot updated Signed-off-by: abasatwar * Resolving issues after removal of preview functionality Signed-off-by: ruchika-narang * updated snapshots Signed-off-by: ruchika-narang * Updated snapshots Signed-off-by: ruchika-narang * changes for default timestamp data for time-series and corner cases Signed-off-by: rinku-kumar-psl * code review comment addressed Signed-off-by: rinku-kumar-psl * pr feedback Signed-off-by: Ramneet Chopra * dimensions, metrics length checks refined Signed-off-by: Ramneet Chopra * updated as per review comments Signed-off-by: abasatwar * fixing of data config corner cases Signed-off-by: rinku-kumar-psl * snapshot updated Signed-off-by: abasatwar * pr feedback Signed-off-by: Ramneet Chopra Co-authored-by: rinku-kumar-psl Co-authored-by: Deepak Nevde Co-authored-by: Ramneet Chopra Co-authored-by: Shankha Das Co-authored-by: Mrunal Zambre Co-authored-by: ruchika-narang <79983862+ruchika-narang@users.noreply.github.com> Co-authored-by: Pratibha Pandey Co-authored-by: ruchika-narang Co-authored-by: Shankha Das Co-authored-by: rinku-kumar-psl Co-authored-by: Deepak Nevde Co-authored-by: Ramneet Chopra Co-authored-by: Shankha Das Co-authored-by: Mrunal Zambre Co-authored-by: ruchika-narang <79983862+ruchika-narang@users.noreply.github.com> Co-authored-by: Pratibha Pandey Co-authored-by: ruchika-narang Co-authored-by: Shankha Das --- .../integration/1_event_analytics.spec.js | 181 +- .../.cypress/utils/event_constants.js | 117 +- .../common/constants/explorer.ts | 23 +- .../common/constants/shared.ts | 11 +- .../common/types/explorer.ts | 6 +- .../__snapshots__/utils.test.tsx.snap | 1382 +-- .../explorer/visualizations/app.scss | 29 + .../__snapshots__/config_panel.test.tsx.snap | 9929 ++++++++--------- .../config_panel/config_panel.scss | 15 +- .../config_panel/config_panel.tsx | 16 +- .../config_panel/config_panel_footer.tsx | 19 - .../config_bar_chart_styles.tsx | 27 +- .../config_controls/config_chart_options.tsx | 62 +- .../config_controls/config_color_theme.tsx | 210 +- .../config_controls/config_legend.tsx | 15 +- .../config_line_chart_styles.tsx | 101 +- .../config_controls/config_number_input.tsx | 39 +- .../config_panel_option_gauge.tsx | 52 + .../config_controls/config_panel_options.tsx | 58 +- .../config_controls/config_switch_button.tsx | 25 + .../config_controls/config_thresholds.tsx | 6 +- .../config_treemap_parents.tsx | 17 +- .../config_controls/config_value_options.tsx | 40 +- .../data_config_panel_item.tsx | 150 +- .../config_panes/config_controls/index.ts | 4 + .../treemap_config_panel_item.tsx | 62 +- .../config_panes/default_vis_editor.tsx | 1 + .../explorer/visualizations/index.tsx | 46 +- .../shared_components.test.tsx.snap | 10 +- .../shared_components/empty_placeholder.scss | 12 + .../shared_components/empty_placeholder.tsx | 7 +- .../__tests__/__snapshots__/bar.test.tsx.snap | 254 +- .../__snapshots__/heatmap.test.tsx.snap | 38 +- .../__snapshots__/histogram.test.tsx.snap | 612 + .../__snapshots__/line.test.tsx.snap | 259 +- .../__tests__/__snapshots__/pie.test.tsx.snap | 241 +- .../__snapshots__/text.test.tsx.snap | 28 +- .../__snapshots__/treemap.test.tsx.snap | 30 +- .../charts/__tests__/histogram.test.tsx | 30 + .../visualizations/charts/bar/bar.tsx | 170 +- .../visualizations/charts/bar/bar_type.ts | 53 +- .../charts/financial/gauge/gauge.tsx | 171 +- .../charts/financial/gauge/gauge_type.ts | 83 +- .../charts/helpers/viz_types.ts | 28 +- .../charts/histogram/histogram.tsx | 81 +- .../charts/histogram/histogram_type.ts | 80 +- .../visualizations/charts/lines/line.tsx | 116 +- .../visualizations/charts/lines/line_type.ts | 79 +- .../visualizations/charts/maps/heatmap.tsx | 5 +- .../charts/maps/heatmap_type.ts | 21 + .../charts/maps/treemap_type.ts | 38 + .../visualizations/charts/maps/treemaps.tsx | 14 +- .../visualizations/charts/pie/pie.tsx | 32 +- .../charts/shared/shared_configs.ts | 2 +- 54 files changed, 7671 insertions(+), 7466 deletions(-) create mode 100644 dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_panel_option_gauge.tsx create mode 100644 dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_switch_button.tsx create mode 100644 dashboards-observability/public/components/event_analytics/explorer/visualizations/shared_components/empty_placeholder.scss create mode 100644 dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/histogram.test.tsx.snap create mode 100644 dashboards-observability/public/components/visualizations/charts/__tests__/histogram.test.tsx diff --git a/dashboards-observability/.cypress/integration/1_event_analytics.spec.js b/dashboards-observability/.cypress/integration/1_event_analytics.spec.js index f2b0db3c6..3f43a6eb7 100644 --- a/dashboards-observability/.cypress/integration/1_event_analytics.spec.js +++ b/dashboards-observability/.cypress/integration/1_event_analytics.spec.js @@ -21,10 +21,29 @@ import { renderTreeMapchart, renderPieChart, renderLineChartForDataConfig, - DataConfigLineChart + renderDataConfig, + aggregationValues, + DataConfigLineChart, + renderAddParent, + renderGaugeChart, + renderAddParent } from '../utils/event_constants'; import { supressResizeObserverIssue } from '../utils/constants'; +const renderHistogramChart = () => { + querySearch(TEST_QUERIES[5].query, TEST_QUERIES[5].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Histogram').type('{enter}'); + cy.wait(delay); + cy.get('g.draglayer.cursor-crosshair').should('exist'); + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Histogram chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Histogram chart'); + cy.get('.euiIEFlexWrapFix').eq(1).contains('Chart Styles').should('exist'); + cy.get('.euiFormLabel.euiFormRow__label').eq(2).contains('Bucket Size'); + cy.get('.euiFieldNumber').eq(0).type('4'); + cy.get('.euiFormLabel.euiFormRow__label').eq(3).contains('Bucket Offset'); + cy.get('.euiFieldNumber').eq(0).type('6'); +}; + const vis_name_sub_string = Math.floor(Math.random() * 100); const saveVisualizationAndVerify = () => { cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').click(); @@ -857,3 +876,163 @@ describe('Render Time series chart/Line chart and verify Data configurations UI DataConfigLineChart(); }); }); + +describe('Renders Data Configurations section for Pie chart', () => { + beforeEach(() => { + landOnEventVisualizations(); + }); + + it('Renders Dimensions and Metrics under Data Configurations for Pie chart', () => { + renderPieChart(); + renderDataConfig(); + }); + + it('Validate "Add" and "X" buttons', () => { + renderPieChart(); + cy.get('.euiResizablePanel.euiResizablePanel--middle').contains('Data Configurations'); + cy.get('.euiButton.euiButton--primary.euiButton--fullWidth').contains('Add').click(); + cy.get('.euiFormRow__fieldWrapper .euiComboBox').eq(3).click(); + cy.get('.euiComboBoxOption__content').eq(2).click(); + cy.get('.first-division .euiFormLabel.euiFormRow__label').eq(4).click(); + cy.get('.euiComboBoxOption__content').eq(1).click(); + cy.get('.euiFieldText[placeholder="Custom label"]').eq(1).type('Demo field'); + cy.get('.euiIcon.euiIcon--medium.euiIcon--danger').eq(1).click(); + cy.get('.euiButton.euiButton--primary.euiButton--fullWidth').contains('Add').should('exist'); + }); + + it('Verify drop down values for Aggregation', () => { + renderPieChart(); + cy.get('.euiResizablePanel.euiResizablePanel--middle').contains('Data Configurations'); + cy.get('.euiTitle.euiTitle--xxsmall').eq(1).contains('Dimensions').should('exist'); + cy.get('.first-division .euiFormLabel.euiFormRow__label').eq(0).contains('Aggregation'); + cy.get('[data-test-subj="comboBoxSearchInput"]').eq(0).click(); + aggregationValues.forEach(function (value){ + cy.get('.euiComboBoxOption__content').contains(value); + }); + }); + + it('Collapsible mode for Data Configuration panel', () => { + renderPieChart(); + cy.get('.euiResizablePanel.euiResizablePanel--middle').contains('Data Configurations'); + cy.get('.euiResizableButton.euiResizableButton--horizontal').eq(1).click(); + cy.get('[data-test-subj="panel-1-toggle"]').click(); + cy.get('[class*="euiResizableToggleButton-isCollapsed"]').eq(1).should('exist'); +describe('Renders Histogram chart', () => { + beforeEach(() => { + landOnEventVisualizations(); +}); + +it('Renders Histogram chart and save visualization', () => { + renderHistogramChart(); + cy.get('.euiFlexItem.euiFlexItem--flexGrowZero .euiButton__text').eq(2).click(); + cy.wait(delay); + saveVisualizationAndVerify(); + }); + + it('Delete Visualization for Histogram chart from list of saved Visualizations on Event analytics page', () =>{ + deleteVisualization(); + }) + + it('Renders Histogram chart, add value parameters and verify Reset button click is working', () => { + renderHistogramChart(); + cy.get('[data-test-subj="visualizeEditorResetButton"]').click(); + }); +}); +describe('Render Gauge Chart and verify if data gets render', () => { + it('Render gauge chart and verify by default no data gets render', () => { + renderGaugeChart(); + cy.get('.main-svg').contains('BeatsWest').should('not.exist'); + }); + + it('Render gauge chart and verify data gets render after click on update chart', () => { + renderGaugeChart(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.main-svg').contains('BeatsWest').should('exist'); + }); +}); + +describe('Render Gauge Chart and work with chart styles', () => { + it('Render gauge chart and change orientation to vertical', () => { + renderGaugeChart(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.euiButton__text').contains('Vertical').click(); + cy.get('.euiButton__text').contains('Preview').click(); + }); + + it('Render gauge chart and change title size then verify the update on chart', () => { + renderGaugeChart(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(0).click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(0).type('30'); + cy.get('.euiButton__text').contains('Preview').click(); + }); + + it('Render gauge chart and change value size then verify the update on chart', () => { + renderGaugeChart(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(1).click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(1).type('20'); + cy.get('.euiButton__text').contains('Preview').click(); + }); +}); + +describe('Render Gauge Chart and work with threshold', () => { + it('Render gauge chart and add threshold then verify by default the threshold is not seen', () => { + renderGaugeChart(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.euiButton__text').contains('+ Add threshold').click(); + cy.get('[data-test-subj="nameFieldText"]').type('Gauge Threshold'); + cy.get('[data-test-subj="valueFieldNumber"]').eq(2).type('50'); + cy.get('.euiButton__text').contains('Preview').click(); + cy.get('[data-unformatted="Gauge Threshold"]').should('not.be.visible'); + }); + + it('Render gauge chart and add threshold then verify the threshold label are seen after show threshold button enabled ', () => { + renderGaugeChart(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.euiButton__text').contains('+ Add threshold').click(); + cy.get('[data-test-subj="nameFieldText"]').type('Gauge Threshold'); + cy.get('[data-test-subj="valueFieldNumber"]').eq(2).type('50'); + cy.get('.euiSwitch__label').contains('Show threshold labels').click(); + cy.get('.euiButton__text').contains('Preview').click(); + cy.get('[data-unformatted="Gauge Threshold"]').should('be.visible'); + }); + + it('Render gauge chart and add threshold then verify the threshold marker are seen after show threshold button enabled ', () => { + renderGaugeChart(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.euiButton__text').contains('+ Add threshold').click(); + cy.get('[data-test-subj="nameFieldText"]').type('Gauge Threshold'); + cy.get('[data-test-subj="valueFieldNumber"]').eq(2).type('50'); + cy.get('.euiSwitch__label').contains('Show threshold markers').click(); + cy.get('.euiButton__text').contains('Preview').click(); + cy.get('path[style*="rgb(252, 5, 5)"]').eq(1).should('exist'); + cy.get('.bg-arc').find('path[style*="rgb(252, 5, 5)"]').should('have.length',4); + }); +}); + +describe('Render gauge chart and verify if reset works properly', () => { + it('Render gauge chart with all feild data then click on reset and verify reset works properly', () => { + renderGaugeChart(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('input[placeholder="Title"]').type('Gauge Chart'); + cy.get('textarea[placeholder="Description"]').type('Description For Gauge Chart'); + cy.get('.euiButton__text').contains('Vertical').click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(0).click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(0).type('30'); + cy.get('[data-test-subj="valueFieldNumber"]').eq(1).click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(1).type('20'); + cy.get('.euiButton__text').contains('+ Add threshold').click(); + cy.get('[data-test-subj="nameFieldText"]').type('Gauge Threshold'); + cy.get('[data-test-subj="valueFieldNumber"]').eq(2).type('50'); + cy.get('.euiSwitch__label').contains('Show threshold labels').click(); + cy.get('.euiSwitch__label').contains('Show threshold markers').click(); + cy.get('.euiButton__text').contains('Preview').click(); + cy.get('.euiButtonEmpty__text').contains('Reset').click(); + cy.get('input[placeholder="Title"]').should('not.have.value','Gauge Chart'); + cy.get('textarea[placeholder="Description"]').should('not.have.value','Description For Gauge Chart') + cy.get('[data-test-subj="valueFieldNumber"]').eq(0).should('have.value',''); + cy.get('[data-test-subj="valueFieldNumber"]').eq(1).should('have.value',''); + cy.get('button.euiSwitch__button[aria-checked="false"]').should('exist').should('have.length',3); + }); +}); diff --git a/dashboards-observability/.cypress/utils/event_constants.js b/dashboards-observability/.cypress/utils/event_constants.js index 4db8c146d..6d7ebbb05 100644 --- a/dashboards-observability/.cypress/utils/event_constants.js +++ b/dashboards-observability/.cypress/utils/event_constants.js @@ -33,7 +33,11 @@ export const TEST_QUERIES = [ dateRangeDOM: YEAR_TO_DATE_DOM_ID }, { - query:"source = opensearch_dashboards_sample_data_logs | where response='503' or response='404' | stats count() by span(timestamp,1d)", + query: "source = opensearch_dashboards_sample_data_logs | where response='503' or response='404' | stats count() by span(timestamp,1d)", + dateRangeDOM: YEAR_TO_DATE_DOM_ID + }, + { + query: 'source=opensearch_dashboards_sample_data_flights |where FlightDelayMin > 0 | stats sum(FlightDelayMin) as total_delay_min, count() as total_delayed by Carrier |eval avg_delay=total_delay_min / total_delayed | sort - avg_delay', dateRangeDOM: YEAR_TO_DATE_DOM_ID }, ]; @@ -44,6 +48,8 @@ export const SAVE_QUERY2 = 'Mock Flight count by destination'; export const SAVE_QUERY3 = 'Mock Flight count by destination save to panel'; export const SAVE_QUERY4 = 'Mock Flight peek'; +export const aggregationValues = ["COUNT", "SUM", "AVERAGE", "MAX", "MIN", "VAR_SAMP", "VAR_POP", "STDDEV_SAMP", "STDDEV_POP"]; + export const querySearch = (query, rangeSelected) => { cy.get('[data-test-subj="searchAutocompleteTextArea"]').type(query); cy.get('[data-test-subj="superDatePickerToggleQuickMenuButton"]').click(); @@ -80,21 +86,54 @@ export const landOnPanels = () => { cy.wait(delay); }; +const vis_name_sub_string = Math.floor(Math.random() * 100); +export const saveVisualizationAndVerify = () => { + cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').click(); + cy.get('[data-test-subj="eventExplorer__querySaveComboBox"]').click(); + cy.get('.euiComboBoxOptionsList__rowWrap .euiFilterSelectItem').eq(0).click(); + cy.get('.euiPopover__panel .euiFormControlLayoutIcons [data-test-subj="comboBoxToggleListButton"]') + .eq(0) + .click(); + cy.get('.euiPopover__panel input') + .eq(1) + .type(`Test visualization` + vis_name_sub_string); + cy.get('[data-test-subj="eventExplorer__querySaveConfirm"]').click(); + cy.wait(delay); + cy.get('.euiHeaderBreadcrumbs a').eq(1).click(); + cy.get('.euiFlexGroup .euiFormControlLayout__childrenWrapper input') + .eq(0) + .type(`Test visualization` + vis_name_sub_string) + .type('{enter}'); + cy.get('.euiBasicTable .euiTableCellContent button').eq(0).click(); +}; + +export const deleteVisualization = () => { + cy.get('a[href = "#/event_analytics"]').click(); + cy.get('.euiFlexGroup .euiFormControlLayout__childrenWrapper input') + .eq(0) + .type(`Test visualization` + vis_name_sub_string) + .type('{enter}'); + cy.get('input[data-test-subj = "checkboxSelectAll"]').click(); + cy.get('.euiButtonContent.euiButtonContent--iconRight.euiButton__content').click(); + cy.get('.euiContextMenuItem .euiContextMenuItem__text').eq(0).click(); + cy.get('input[placeholder = "delete"]').clear().type('delete'); + cy.get('button[data-test-subj = "popoverModal__deleteButton"]').click(); + cy.get('.euiToastHeader').should('exist'); +}; + export const renderTreeMapchart = () => { - querySearch(TEST_QUERIES[5].query, TEST_QUERIES[5].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Tree Map').type('{enter}'); - cy.get('#configPanel__panelOptions .euiFieldText').click().type('Tree Map'); - cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Tree Map'); - cy.get('.euiFormControlLayoutIcons [data-test-subj ="comboBoxToggleListButton"]').eq(1).click(); - cy.get('.euiComboBoxOption__content').eq(2).click(); - cy.get('.euiFormControlLayoutIcons [data-test-subj ="comboBoxToggleListButton"]').eq(2).click(); - cy.get('.euiComboBoxOption__content').eq(1).click(); - cy.get('.euiFormControlLayoutIcons [data-test-subj ="comboBoxToggleListButton"]').eq(3).click(); - cy.get('.euiComboBoxOption__content').eq(0).click(); - cy.get('.euiIEFlexWrapFix').eq(2).contains('Treemap').should('exist'); - cy.get('#configPanel__treemap_options').contains('Tiling Algorithm').should('exist'); - cy.get('[data-test-subj = "comboBoxInput"]').eq(4).click(); - cy.get('button[name="Slice Dice"]').click(); + querySearch(TEST_QUERIES[7].query, TEST_QUERIES[7].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Tree Map') + .type('{enter}'); + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Tree Map'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]') + .click() + .type('This is the description for Tree Map'); + cy.get('.euiIEFlexWrapFix').eq(1).contains('Treemap').should('exist'); + cy.get('#configPanel__treemap_options').contains('Tiling Algorithm').should('exist'); + cy.get('[data-test-subj = "comboBoxInput"]').eq(3).click(); + cy.get('button[name="Slice Dice"]').click(); }; export const renderPieChart = () => { @@ -104,11 +143,25 @@ export const renderPieChart = () => { cy.wait(delay); cy.get('#configPanel__panelOptions .euiFieldText').click().type('Pie chart'); cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Pie chart'); - cy.get('.euiIEFlexWrapFix').eq(1).contains('Value options').should('exist'); - cy.get('[data-test-subj="comboBoxInput"]').eq(1).click(); - cy.get('[name="count()"]').eq(0).click(); - cy.get('[data-test-subj="comboBoxToggleListButton"]').eq(0).click(); - cy.get('[data-test-subj="comboBoxInput"]').eq(2).click(); + cy.get('[aria-controls="configPanel__legend"]').contains('Legend').should('exist'); + cy.get('#configPanel__legend .euiTitle.euiTitle--xxsmall').eq(0).contains('Show Legend'); + cy.get('span[data-text="Show"]').contains('Show').should('exist'); + cy.get('#configPanel__legend .euiTitle.euiTitle--xxsmall').eq(1).contains('Position'); + cy.get('span[data-text="Right"]').contains('Right').should('exist'); + cy.get('#configPanel__legend .euiTitle.euiTitle--xxsmall').eq(2).contains('Legend Size'); + cy.get('[aria-controls="configPanel__chartStyles"]').contains('Chart Styles').should('exist'); + cy.get('#configPanel__chartStyles .euiTitle.euiTitle--xxsmall').eq(0).contains('Mode').click(); + cy.get('#configPanel__chartStyles .euiComboBox__inputWrap.euiComboBox__inputWrap--noWrap.euiComboBox__inputWrap-isClearable').click(); + cy.get('.euiComboBoxOption__content').contains('Donut').click(); + cy.get('#configPanel__chartStyles .euiTitle.euiTitle--xxsmall').eq(1).contains('Label Size'); + cy.get('#configPanel__chartStyles input[type="number"]').click().type('10'); + cy.get('#configPanel__chartStyles .euiTitle.euiTitle--xxsmall').eq(2).contains('Color Theme'); + cy.get('.euiSuperSelectControl').click(); + cy.get('.euiColorPalettePicker__item').eq(1).contains('Single Color').click(); + cy.get('.euiFieldText.euiColorPicker__input.euiFieldText--withIcon').click(); + cy.get('[aria-label="Select #D36086 as the color"]').click(); + cy.get('.visEditorSidebar__controls [data-test-subj="visualizeEditorRenderButton"]').contains('Preview').click(); + cy.get('.plot-container.plotly').should('exist'); }; export const renderDataConfig = () => { @@ -136,5 +189,29 @@ export const renderDataConfig = () => { export const renderLineChart = () => { landOnEventVisualizations(); querySearch(TEST_QUERIES[5].query, TEST_QUERIES[5].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Line') + .type('{enter}'); +}; + +export const renderAddParent = () => { + cy.get(' [data-test-subj="addParentButton"] .euiButton__text').contains('+ Add Parent').click(); + cy.get('.first-division .euiFormLabel.euiFormRow__label').contains('Parent 1').should('exist'); + cy.get('p.euiComboBoxPlaceholder').eq(0).click({ force: true }); + cy.get('.euiComboBoxOption__content').eq(0).click(); + cy.get(' [data-test-subj="addParentButton"] .euiButton__text').contains('+ Add Parent').click(); + cy.get('.first-division .euiFormLabel.euiFormRow__label').contains('Parent 2').should('exist'); + cy.get('p.euiComboBoxPlaceholder').click({ force: true }); + cy.get('.euiComboBoxOption__content').eq(1).click(); + cy.get(' [data-test-subj="addParentButton"] .euiButton__text').contains('+ Add Parent').click(); + cy.get('.first-division .euiFormLabel.euiFormRow__label').contains('Parent 3').should('exist'); + cy.get('p.euiComboBoxPlaceholder').click({ force: true }); + cy.get('.euiComboBoxOption__content').eq(2).click(); cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Line').type('{enter}'); }; + +export const renderGaugeChart = () => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[1].query, TEST_QUERIES[1].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Gauge').type('{enter}'); +}; diff --git a/dashboards-observability/common/constants/explorer.ts b/dashboards-observability/common/constants/explorer.ts index 17612c44e..9199fc8cd 100644 --- a/dashboards-observability/common/constants/explorer.ts +++ b/dashboards-observability/common/constants/explorer.ts @@ -75,7 +75,7 @@ export const REDUX_EXPL_SLICE_FIELDS = 'fields'; export const REDUX_EXPL_SLICE_QUERY_TABS = 'queryTabs'; export const REDUX_EXPL_SLICE_VISUALIZATION = 'explorerVisualization'; export const REDUX_EXPL_SLICE_COUNT_DISTRIBUTION = 'countDistributionVisualization'; -export const PLOTLY_GAUGE_COLUMN_NUMBER = 5; +export const PLOTLY_GAUGE_COLUMN_NUMBER = 4; export const APP_ANALYTICS_TAB_ID_REGEX = /application-analytics-tab.+/; export const DEFAULT_AVAILABILITY_QUERY = 'stats count() by span( timestamp, 1h )'; export const ADD_BUTTON_TEXT = '+ Add color theme'; @@ -118,6 +118,8 @@ export const AGGREGATION_OPTIONS = [ }, ]; +// numeric fields type for metrics +export const numericalTypes = ['float', 'double', 'bigint', 'long', 'octet', 'short', 'byte', 'integer']; // Data table constants export const GRID_HEADER_COLUMN_MAX_WIDTH = '150px'; export const GRID_PAGE_RANGE_DISPLAY = 5; @@ -130,3 +132,22 @@ export const ROW_DENSITIES = [ ]; export const HEADER_HEIGHT = 35; + +// gauge chart default parameters +export interface DefaultGaugeChartParametersProps { + GaugeTitleSize: number, + DisplayDefaultGauges: number, + OrientationDefault: string, + TickLength: number, + LegendPlacement: string, + ThresholdsMaxLimit: number +}; + +export const DefaultGaugeChartParameters: DefaultGaugeChartParametersProps = { + GaugeTitleSize: 14, + DisplayDefaultGauges: 1, + OrientationDefault: 'h', + TickLength: 5, + LegendPlacement: 'center', + ThresholdsMaxLimit: 1 +} diff --git a/dashboards-observability/common/constants/shared.ts b/dashboards-observability/common/constants/shared.ts index 00b6b3f14..d4021c350 100644 --- a/dashboards-observability/common/constants/shared.ts +++ b/dashboards-observability/common/constants/shared.ts @@ -97,7 +97,7 @@ export interface ValueOptionsAxes { export const NUMERICAL_FIELDS = ['short', 'integer', 'long', 'float', 'double']; -export const ENABLED_VIS_TYPES = [visChartTypes.Bar, visChartTypes.HorizontalBar, visChartTypes.Line, visChartTypes.Pie, visChartTypes.HeatMap, visChartTypes.Text, visChartTypes.TreeMap]; +export const ENABLED_VIS_TYPES = [visChartTypes.Bar, visChartTypes.HorizontalBar, visChartTypes.Line, visChartTypes.Pie, visChartTypes.HeatMap, visChartTypes.Text, visChartTypes.TreeMap, visChartTypes.Gauge, visChartTypes.Histogram]; //Live tail constants export const LIVE_OPTIONS = [ @@ -156,7 +156,9 @@ export interface DefaultChartStylesProps { FillOpacity: number, MarkerSize: number, ShowLegend: string, - LegendPosition: string + LegendPosition: string, + LabelAngle: number, + DefaultSortSectors: string, }; export const DefaultChartStyles: DefaultChartStylesProps = { @@ -166,7 +168,10 @@ export const DefaultChartStyles: DefaultChartStylesProps = { FillOpacity: 40, MarkerSize: 5, ShowLegend: 'show', - LegendPosition: 'v' + LegendPosition: 'v', + LabelAngle: 0, + DefaultSortSectors: 'largest_to_smallest', } export const FILLOPACITY_DIV_FACTOR = 200; + diff --git a/dashboards-observability/common/types/explorer.ts b/dashboards-observability/common/types/explorer.ts index 2204a6ab4..ade2c6db3 100644 --- a/dashboards-observability/common/types/explorer.ts +++ b/dashboards-observability/common/types/explorer.ts @@ -239,8 +239,12 @@ export interface ConfigListEntry { side: string; type: string; } +export interface HistogramConfigList { + bucketSize: string; + bucketOffset: string; +} export interface ConfigList { - dimensions?: ConfigListEntry[]; + dimensions?: ConfigListEntry[] | HistogramConfigList[]; metrics?: ConfigListEntry[]; } diff --git a/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap b/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap index 3feb1f635..527aa9965 100644 --- a/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap +++ b/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap @@ -88,26 +88,6 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "mapTo": "dataConfig", "name": "Data", "sections": Array [ - Object { - "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", - "schemas": Array [ - Object { - "component": null, - "isSingleSelection": false, - "mapTo": "xaxis", - "name": "X-axis", - }, - Object { - "component": null, - "isSingleSelection": false, - "mapTo": "yaxis", - "name": "Y-axis", - }, - ], - }, Object { "editor": [Function], "id": "legend", @@ -216,6 +196,12 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` ], }, }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "labelSize", + "name": "Label Size", + }, Object { "component": [Function], "defaultState": 0, @@ -344,7 +330,7 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` }, "isUniColor": false, "layout": Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -449,26 +435,6 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "mapTo": "dataConfig", "name": "Data", "sections": Array [ - Object { - "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", - "schemas": Array [ - Object { - "component": null, - "isSingleSelection": false, - "mapTo": "xaxis", - "name": "X-axis", - }, - Object { - "component": null, - "isSingleSelection": false, - "mapTo": "yaxis", - "name": "Y-axis", - }, - ], - }, Object { "editor": [Function], "id": "legend", @@ -577,6 +543,12 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` ], }, }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "labelSize", + "name": "Label Size", + }, Object { "component": [Function], "defaultState": 0, @@ -705,7 +677,7 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` }, "isUniColor": false, "layout": Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -733,7 +705,7 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` } layout={ Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -833,26 +805,6 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "mapTo": "dataConfig", "name": "Data", "sections": Array [ - Object { - "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", - "schemas": Array [ - Object { - "component": null, - "isSingleSelection": false, - "mapTo": "xaxis", - "name": "X-axis", - }, - Object { - "component": null, - "isSingleSelection": false, - "mapTo": "yaxis", - "name": "Y-axis", - }, - ], - }, Object { "editor": [Function], "id": "legend", @@ -961,6 +913,12 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` ], }, }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "labelSize", + "name": "Label Size", + }, Object { "component": [Function], "defaultState": 0, @@ -1089,7 +1047,7 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` }, "isUniColor": false, "layout": Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -1108,180 +1066,83 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` } } > - -
- - + className="euiText euiText--extraSmall lnsChart__empty" + data-test-subj="vizWorkspace__noData" + > + +
+ +
+ + + + + + +
+ +

+ + + No results found + + +

+
+ +
+ +
+ + @@ -1296,19 +1157,18 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "data": Object { "appData": Object {}, "defaultAxes": Object { - "xaxis": Array [ - Object { - "label": "Carrier", - "name": "Carrier", - "type": "keyword", - }, - ], + "xaxis": Array [], "yaxis": Array [ Object { "label": "avg(FlightDelayMin)", "name": "avg(FlightDelayMin)", "type": "double", }, + Object { + "label": "Carrier", + "name": "Carrier", + "type": "keyword", + }, ], }, "indexFields": Object {}, @@ -1427,6 +1287,13 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` ], }, }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "legendSize", + "name": "Legend Size", + "title": "Legend Size", + }, ], }, Object { @@ -1452,17 +1319,13 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "id": "lines", "name": "Lines", }, - Object { - "id": "bar", - "name": "Bars", - }, Object { "id": "markers", - "name": "Points", + "name": "Marker", }, Object { "id": "lines+markers", - "name": "Lines + Points", + "name": "Lines + Markers", }, ], }, @@ -1529,8 +1392,56 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "max": 40, }, }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "labelSize", + "name": "Label Size", + "title": "Label Size", + }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateLabels", + "name": "Rotate labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, ], }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color theme", + "schemas": Array [], + }, Object { "defaultState": Array [], "editor": [Function], @@ -1556,11 +1467,11 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` }, ], }, - "fullLabel": "Line", + "fullLabel": "Time series", "icon": [Function], "iconType": "visLine", "id": "line", - "label": "Line", + "label": "Time series", "name": "line", "selection": Object { "dataLoss": "nothing", @@ -1592,7 +1503,7 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "#BD6F26", "#4C636F", ], - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -1629,19 +1540,18 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "data": Object { "appData": Object {}, "defaultAxes": Object { - "xaxis": Array [ - Object { - "label": "Carrier", - "name": "Carrier", - "type": "keyword", - }, - ], + "xaxis": Array [], "yaxis": Array [ Object { "label": "avg(FlightDelayMin)", "name": "avg(FlightDelayMin)", "type": "double", }, + Object { + "label": "Carrier", + "name": "Carrier", + "type": "keyword", + }, ], }, "indexFields": Object {}, @@ -1760,6 +1670,13 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` ], }, }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "legendSize", + "name": "Legend Size", + "title": "Legend Size", + }, ], }, Object { @@ -1785,17 +1702,13 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "id": "lines", "name": "Lines", }, - Object { - "id": "bar", - "name": "Bars", - }, Object { "id": "markers", - "name": "Points", + "name": "Marker", }, Object { "id": "lines+markers", - "name": "Lines + Points", + "name": "Lines + Markers", }, ], }, @@ -1862,19 +1775,67 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "max": 40, }, }, - ], - }, - Object { - "defaultState": Array [], - "editor": [Function], - "id": "thresholds", - "mapTo": "thresholds", - "name": "Thresholds", - "schemas": Array [], - }, - ], - }, - Object { + Object { + "component": [Function], + "eleType": "input", + "mapTo": "labelSize", + "name": "Label Size", + "title": "Label Size", + }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateLabels", + "name": "Rotate labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, + ], + }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color theme", + "schemas": Array [], + }, + Object { + "defaultState": Array [], + "editor": [Function], + "id": "thresholds", + "mapTo": "thresholds", + "name": "Thresholds", + "schemas": Array [], + }, + ], + }, + Object { "content": Array [], "editor": [Function], "id": "style-panel", @@ -1889,11 +1850,11 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` }, ], }, - "fullLabel": "Line", + "fullLabel": "Time series", "icon": [Function], "iconType": "visLine", "id": "line", - "label": "Line", + "label": "Time series", "name": "line", "selection": Object { "dataLoss": "nothing", @@ -1925,7 +1886,7 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "#BD6F26", "#4C636F", ], - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -1984,7 +1945,7 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "#BD6F26", "#4C636F", ], - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -2016,19 +1977,18 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "data": Object { "appData": Object {}, "defaultAxes": Object { - "xaxis": Array [ - Object { - "label": "Carrier", - "name": "Carrier", - "type": "keyword", - }, - ], + "xaxis": Array [], "yaxis": Array [ Object { "label": "avg(FlightDelayMin)", "name": "avg(FlightDelayMin)", "type": "double", }, + Object { + "label": "Carrier", + "name": "Carrier", + "type": "keyword", + }, ], }, "indexFields": Object {}, @@ -2147,6 +2107,13 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` ], }, }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "legendSize", + "name": "Legend Size", + "title": "Legend Size", + }, ], }, Object { @@ -2172,17 +2139,13 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "id": "lines", "name": "Lines", }, - Object { - "id": "bar", - "name": "Bars", - }, Object { "id": "markers", - "name": "Points", + "name": "Marker", }, Object { "id": "lines+markers", - "name": "Lines + Points", + "name": "Lines + Markers", }, ], }, @@ -2249,8 +2212,56 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "max": 40, }, }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "labelSize", + "name": "Label Size", + "title": "Label Size", + }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateLabels", + "name": "Rotate labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, ], }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color theme", + "schemas": Array [], + }, Object { "defaultState": Array [], "editor": [Function], @@ -2276,11 +2287,11 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` }, ], }, - "fullLabel": "Line", + "fullLabel": "Time series", "icon": [Function], "iconType": "visLine", "id": "line", - "label": "Line", + "label": "Time series", "name": "line", "selection": Object { "dataLoss": "nothing", @@ -2312,7 +2323,7 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "#BD6F26", "#4C636F", ], - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -2343,226 +2354,83 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` } } > - -
- - + className="euiText euiText--extraSmall lnsChart__empty" + data-test-subj="vizWorkspace__noData" + > + +
+ +
+ + + + + + +
+ +

+ + + No results found + + +

+
+ +
+ +
+ + @@ -2762,7 +2630,7 @@ exports[`Utils helper functions renders displayVisualization function 3`] = ` }, "isUniColor": false, "layout": Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -2972,7 +2840,7 @@ exports[`Utils helper functions renders displayVisualization function 3`] = ` }, "isUniColor": false, "layout": Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -3000,7 +2868,7 @@ exports[`Utils helper functions renders displayVisualization function 3`] = ` } layout={ Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -3205,7 +3073,7 @@ exports[`Utils helper functions renders displayVisualization function 3`] = ` }, "isUniColor": false, "layout": Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -3224,180 +3092,83 @@ exports[`Utils helper functions renders displayVisualization function 3`] = ` } } > - -
- - + className="euiText euiText--extraSmall lnsChart__empty" + data-test-subj="vizWorkspace__noData" + > + +
+ +
+ + + + + + +
+ +

+ + + No results found + + +

+
+ +
+ +
+ + @@ -3570,7 +3341,7 @@ exports[`Utils helper functions renders displayVisualization function 4`] = ` }, "isUniColor": false, "layout": Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -3753,7 +3524,7 @@ exports[`Utils helper functions renders displayVisualization function 4`] = ` }, "isUniColor": false, "layout": Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -3781,7 +3552,7 @@ exports[`Utils helper functions renders displayVisualization function 4`] = ` } layout={ Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -3959,7 +3730,7 @@ exports[`Utils helper functions renders displayVisualization function 4`] = ` }, "isUniColor": false, "layout": Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -3978,160 +3749,83 @@ exports[`Utils helper functions renders displayVisualization function 4`] = ` } } > - -
- - + className="euiText euiText--extraSmall lnsChart__empty" + data-test-subj="vizWorkspace__noData" + > + +
+ +
+ + + + + + +
+ +

+ + + No results found + + +

+
+ +
+ +
+ + diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/app.scss b/dashboards-observability/public/components/event_analytics/explorer/visualizations/app.scss index 2f01e1b8e..ef67ba34b 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/app.scss +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/app.scss @@ -32,6 +32,35 @@ flex-grow: 1; } +.containerPanel { + padding: 0 16px; +} + +.dataConfigContainer { + height: 1242px; + overflow: auto; +} + +::-webkit-scrollbar { + width: 10px; +} + +::-webkit-scrollbar-track { + background: #f1f1f1; +} + +::-webkit-scrollbar-thumb { + background: #c2c2c2; +} + +.euiResizableToggleButton:not(:focus):not(:active):not(.euiResizableToggleButton-isVisible):not(.euiResizableToggleButton-isCollapsed) { + height: 24px; + width: 24px; + top: 0; + left: inherit; + box-shadow: none; +} + .lensChartIcon__subdued { fill: $euiTextSubduedColor; diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap index 435c32d43..1cac93734 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap @@ -213,26 +213,6 @@ exports[`Config panel component Renders config panel with visualization data 1`] "mapTo": "dataConfig", "name": "Data", "sections": Array [ - Object { - "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", - "schemas": Array [ - Object { - "component": null, - "isSingleSelection": false, - "mapTo": "xaxis", - "name": "X-axis", - }, - Object { - "component": null, - "isSingleSelection": false, - "mapTo": "yaxis", - "name": "Y-axis", - }, - ], - }, Object { "editor": [Function], "id": "legend", @@ -341,6 +321,12 @@ exports[`Config panel component Renders config panel with visualization data 1`] ], }, }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "labelSize", + "name": "Label Size", + }, Object { "component": [Function], "defaultState": 0, @@ -469,7 +455,7 @@ exports[`Config panel component Renders config panel with visualization data 1`] }, "isUniColor": false, "layout": Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -517,7 +503,7 @@ exports[`Config panel component Renders config panel with visualization data 1`] async={false} compressed={false} fullWidth={true} - isClearable={true} + isClearable={false} onChange={[Function]} options={ Array [ @@ -534,26 +520,6 @@ exports[`Config panel component Renders config panel with visualization data 1`] "mapTo": "dataConfig", "name": "Data", "sections": Array [ - Object { - "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", - "schemas": Array [ - Object { - "component": null, - "isSingleSelection": false, - "mapTo": "xaxis", - "name": "X-axis", - }, - Object { - "component": null, - "isSingleSelection": false, - "mapTo": "yaxis", - "name": "Y-axis", - }, - ], - }, Object { "editor": [Function], "id": "legend", @@ -662,6 +628,12 @@ exports[`Config panel component Renders config panel with visualization data 1`] ], }, }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "labelSize", + "name": "Label Size", + }, Object { "component": [Function], "defaultState": 0, @@ -790,7 +762,7 @@ exports[`Config panel component Renders config panel with visualization data 1`] }, "isUniColor": false, "layout": Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -870,6 +842,13 @@ exports[`Config panel component Renders config panel with visualization data 1`] ], }, }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "legendSize", + "name": "Legend Size", + "title": "Legend Size", + }, ], }, Object { @@ -895,17 +874,13 @@ exports[`Config panel component Renders config panel with visualization data 1`] "id": "lines", "name": "Lines", }, - Object { - "id": "bar", - "name": "Bars", - }, Object { "id": "markers", - "name": "Points", + "name": "Marker", }, Object { "id": "lines+markers", - "name": "Lines + Points", + "name": "Lines + Markers", }, ], }, @@ -972,8 +947,56 @@ exports[`Config panel component Renders config panel with visualization data 1`] "max": 40, }, }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "labelSize", + "name": "Label Size", + "title": "Label Size", + }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateLabels", + "name": "Rotate labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, ], }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color theme", + "schemas": Array [], + }, Object { "defaultState": Array [], "editor": [Function], @@ -999,11 +1022,11 @@ exports[`Config panel component Renders config panel with visualization data 1`] }, ], }, - "fullLabel": "Line", + "fullLabel": "Time series", "icon": [Function], "iconType": "visLine", "id": "line", - "label": "Line", + "label": "Time series", "name": "line", "selection": Object { "dataLoss": "nothing", @@ -1035,7 +1058,7 @@ exports[`Config panel component Renders config panel with visualization data 1`] "#BD6F26", "#4C636F", ], - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -1238,7 +1261,7 @@ exports[`Config panel component Renders config panel with visualization data 1`] "#BD6F26", "#4C636F", ], - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -1277,6 +1300,37 @@ exports[`Config panel component Renders config panel with visualization data 1`] "mapTo": "dataConfig", "name": "Data", "sections": Array [ + Object { + "editor": [Function], + "id": "legend", + "mapTo": "legend", + "name": "Legend", + "schemas": Array [ + Object { + "component": null, + "mapTo": "showLegend", + "name": "Show Colorscale", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, + }, + ], + }, Object { "editor": [Function], "id": "chart_styles", @@ -1579,7 +1633,7 @@ exports[`Config panel component Renders config panel with visualization data 1`] "responsive": true, }, "layout": Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -1652,7 +1706,7 @@ exports[`Config panel component Renders config panel with visualization data 1`] "#BD6F26", "#4C636F", ], - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -1692,6 +1746,37 @@ exports[`Config panel component Renders config panel with visualization data 1`] "mapTo": "dataConfig", "name": "Data", "sections": Array [ + Object { + "editor": [Function], + "id": "legend", + "mapTo": "legend", + "name": "Legend", + "schemas": Array [ + Object { + "component": null, + "mapTo": "showLegend", + "name": "Show Colorscale", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, + }, + ], + }, Object { "editor": [Function], "id": "treemap_options", @@ -1740,6 +1825,30 @@ exports[`Config panel component Renders config panel with visualization data 1`] "isClearable": false, }, }, + Object { + "component": [Function], + "eleType": "buttons", + "mapTo": "sort_sectors", + "name": "Sort Sectors", + "props": Object { + "defaultSelections": Array [ + Object { + "id": undefined, + "name": "Largest to Smallest", + }, + ], + "options": Array [ + Object { + "id": "largest_to_smallest", + "name": "Largest to Smallest", + }, + Object { + "id": "random", + "name": "Random", + }, + ], + }, + }, ], }, Object { @@ -2023,7 +2132,7 @@ exports[`Config panel component Renders config panel with visualization data 1`] }, "isUniColor": false, "layout": Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -2039,16 +2148,8 @@ exports[`Config panel component Renders config panel with visualization data 1`] }, }, }, - ] - } - placeholder="Select a chart" - renderOption={[Function]} - selectedOptions={ - Array [ Object { - "barWidth": 0.97, "category": "Visualizations", - "categoryAxis": "xaxis", "component": [Function], "editorConfig": Object { "panelTabs": Array [ @@ -2060,81 +2161,7 @@ exports[`Config panel component Renders config panel with visualization data 1`] "sections": Array [ Object { "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", - "schemas": Array [ - Object { - "component": null, - "isSingleSelection": false, - "mapTo": "xaxis", - "name": "X-axis", - }, - Object { - "component": null, - "isSingleSelection": false, - "mapTo": "yaxis", - "name": "Y-axis", - }, - ], - }, - Object { - "editor": [Function], - "id": "legend", - "mapTo": "legend", - "name": "Legend", - "schemas": Array [ - Object { - "component": null, - "mapTo": "showLegend", - "name": "Show Legend", - "props": Object { - "defaultSelections": Array [ - Object { - "id": "show", - "name": "Show", - }, - ], - "options": Array [ - Object { - "id": "show", - "name": "Show", - }, - Object { - "id": "hidden", - "name": "Hidden", - }, - ], - }, - }, - Object { - "component": null, - "mapTo": "position", - "name": "Position", - "props": Object { - "defaultSelections": Array [ - Object { - "id": "v", - "name": "Right", - }, - ], - "options": Array [ - Object { - "id": "v", - "name": "Right", - }, - Object { - "id": "h", - "name": "Bottom", - }, - ], - }, - }, - ], - }, - Object { - "editor": [Function], - "id": "chart_styles", + "id": "chart-styles", "mapTo": "chartStyles", "name": "Chart styles", "schemas": Array [ @@ -2146,11 +2173,15 @@ exports[`Config panel component Renders config panel with visualization data 1`] "props": Object { "defaultSelections": Array [ Object { - "id": "v", - "name": "Vertical", + "id": "auto", + "name": "Auto", }, ], "options": Array [ + Object { + "id": "auto", + "name": "Auto", + }, Object { "id": "v", "name": "Vertical", @@ -2165,86 +2196,138 @@ exports[`Config panel component Renders config panel with visualization data 1`] Object { "component": [Function], "eleType": "buttons", - "mapTo": "mode", - "name": "Mode", + "mapTo": "legendPlacement", + "name": "Legend Placement", "props": Object { "defaultSelections": Array [ Object { - "id": "group", - "name": "Group", + "id": "center", + "name": "Center", }, ], "options": Array [ Object { - "id": "group", - "name": "Group", + "id": "center", + "name": "Center", }, Object { - "id": "stack", - "name": "Stack", + "id": "right", + "name": "Right", + }, + Object { + "id": "left", + "name": "Left", }, ], }, }, Object { "component": [Function], - "defaultState": 0, - "eleType": "slider", - "mapTo": "rotateBarLabels", - "name": "Rotate bar labels", - "props": Object { - "max": 90, - "min": -90, - "showTicks": true, - "ticks": Array [ - Object { - "label": "-90°", - "value": -90, - }, - Object { - "label": "-45°", - "value": -45, - }, - Object { - "label": "0°", - "value": 0, - }, - Object { - "label": "45°", - "value": 45, - }, - Object { - "label": "90°", - "value": 90, - }, - ], - }, + "eleType": "input", + "mapTo": "titleSize", + "name": "Title Size", + "title": "Title Size", }, Object { "component": [Function], - "defaultState": 0.7, - "eleType": "slider", - "mapTo": "groupWidth", - "name": "Group width", - "props": Object { - "max": 1, - "step": 0.01, - }, + "eleType": "input", + "mapTo": "valueSize", + "name": "Value Size", + "title": "Value Size", }, Object { "component": [Function], - "defaultState": 0.97, - "eleType": "slider", - "mapTo": "barWidth", - "name": "Bar width", - "props": Object { - "max": 1, - "step": 0.01, - }, + "currentValue": false, + "eleType": "switchButton", + "mapTo": "showThresholdLabels", + "name": "Show threshold labels", + "title": "Show threshold labels", }, Object { "component": [Function], - "defaultState": 1, + "currentValue": true, + "eleType": "switchButton", + "mapTo": "showThresholdMarkers", + "name": "Show threshold markers", + "title": "Show threshold markers", + }, + ], + }, + Object { + "defaultState": Array [], + "editor": [Function], + "id": "thresholds", + "mapTo": "thresholds", + "name": "Thresholds", + "props": Object { + "maxLimit": 1, + }, + "schemas": Array [], + }, + ], + }, + Object { + "content": Array [], + "editor": [Function], + "id": "style-panel", + "mapTo": "layoutConfig", + "name": "Layout", + }, + ], + }, + "fullLabel": "Gauge", + "icon": [Function], + "iconType": "visGauge", + "id": "gauge", + "label": "Gauge", + "name": "Gauge", + "selection": Object { + "dataLoss": "nothing", + }, + "type": "indicator", + "valueSeries": "yaxis", + "visConfig": Object { + "config": Object { + "displaylogo": false, + "responsive": true, + }, + "layout": Object { + "height": 1180, + "legend": Object { + "orientation": "v", + "traceorder": "normal", + }, + "margin": Object { + "b": 30, + "l": 60, + "pad": 0, + "r": 30, + "t": 50, + }, + "showlegend": true, + }, + }, + }, + Object { + "category": "Visualizations", + "component": [Function], + "editorConfig": Object { + "panelTabs": Array [ + Object { + "editor": [Function], + "id": "data-panel", + "mapTo": "dataConfig", + "name": "Data", + "sections": Array [ + Object { + "editor": [Function], + "id": "chart-styles", + "mapTo": "chartStyles", + "name": "Chart styles", + "schemas": Array [ + Object { + "component": [Function], + "defaultState": 2, "eleType": "slider", "mapTo": "lineWidth", "name": "Line width", @@ -2254,7 +2337,7 @@ exports[`Config panel component Renders config panel with visualization data 1`] }, Object { "component": [Function], - "defaultState": 80, + "defaultState": 40, "eleType": "slider", "mapTo": "fillOpacity", "name": "Fill Opacity", @@ -2271,6 +2354,60 @@ exports[`Config panel component Renders config panel with visualization data 1`] "name": "Color Theme", "schemas": Array [], }, + Object { + "editor": [Function], + "id": "legend", + "mapTo": "legend", + "name": "Legend", + "schemas": Array [ + Object { + "component": null, + "mapTo": "showLegend", + "name": "Show Legend", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, + }, + Object { + "component": null, + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, + }, + ], + }, ], }, Object { @@ -2280,41 +2417,25 @@ exports[`Config panel component Renders config panel with visualization data 1`] "mapTo": "layoutConfig", "name": "Layout", }, - Object { - "editor": [Function], - "id": "availability-panel", - "mapTo": "availabilityConfig", - "name": "Availability", - }, ], }, - "fillOpacity": 80, - "fullLabel": "Bar", - "groupWidth": 0.7, - "icon": [Function], - "iconType": "visBarVerticalStacked", - "id": "bar", - "label": "Bar", - "labelAngle": 0, - "legendPosition": "v", - "lineWidth": 1, - "mode": "group", - "name": "bar", - "orientation": "v", + "fullLabel": "Histogram", + "iconType": "visArea", + "id": "histogram", + "label": "Histogram", + "name": "histogram", "selection": Object { "dataLoss": "nothing", }, - "seriesAxis": "yaxis", - "showLegend": "show", - "type": "bar", + "type": "histogram", + "valueSeries": "yaxis", "visConfig": Object { "config": Object { "displaylogo": false, "responsive": true, }, - "isUniColor": false, "layout": Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -2332,2504 +2453,326 @@ exports[`Config panel component Renders config panel with visualization data 1`] }, ] } - singleSelection={true} - sortMatchesBy="none" - > -
- - -
-
-
- - - - - - - Bar - - - - - - - - - - - - -
- -
-
- -
- -
- - - - - - - - -
-
-
-
- - -
- - -
- -
- - -
- -
- , - "id": "data-panel", - "name": "Data", - } - } - tabs={ + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color Theme", + "schemas": Array [], + }, + ], + }, + Object { + "content": Array [], + "editor": [Function], + "id": "style-panel", + "mapTo": "layoutConfig", + "name": "Layout", + }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, + ], + }, + "fillOpacity": 80, + "fullLabel": "Bar", + "groupWidth": 0.7, + "icon": [Function], + "iconType": "visBarVerticalStacked", + "id": "bar", + "label": "Bar", + "labelAngle": 0, + "legendPosition": "v", + "lineWidth": 1, + "mode": "group", + "name": "bar", + "orientation": "v", + "selection": Object { + "dataLoss": "nothing", + }, + "seriesAxis": "yaxis", + "showLegend": "show", + "type": "bar", + "visConfig": Object { + "config": Object { + "displaylogo": false, + "responsive": true, + }, + "isUniColor": false, + "layout": Object { + "height": 1180, + "legend": Object { + "orientation": "v", + "traceorder": "normal", + }, + "margin": Object { + "b": 30, + "l": 60, + "pad": 0, + "r": 30, + "t": 50, + }, + "showlegend": true, + }, + }, + }, + ] + } + singleSelection={true} + sortMatchesBy="none" + > +
+ + +
+
+
+ , - "id": "data-panel", - "name": "Data", - }, - Object { - "content": , - "id": "style-panel", - "name": "Layout", - }, - Object { - "content": + + + + + + Bar + + + + + + + + + + + + , - "id": "availability-panel", - "name": "Availability", - }, - ] - } - > -
- -
- - - - - - - +
+
+ +
+ - - + + +
+
- -
- + + +
+ + +
+ +
+ + +
+ +
+ -
- -
- -
-
- - -
-
- -
-
- -
-
- -
- -
-
- - - -
-
- - -
-
- - - - -
-
-
-
- -
- Name your visualization. -
-
-
-
-
- -
-
- - - -
-
- - - -
- -
-
- The default logs view in the application will be filtered by this query. -
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
- -
-
-
-
-
-
-
- -
-
- -
-
-
-
- - Service map - -
-
-
- -
- - - -
-
-
-
-
-
- Focus on -
-
-
-
-
- -
- - - -
-
-
-
-
-
-
-
-
-

- No matches -

- -
-
-
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
- -
- -
-
-
-
-
-
-
- -
-
- -
-
-
-
-
-
- - Latency by trace group - - - (0) - -
-
-
- -
-
-
- -
-
-
-
-
-
-
-

- No matches -

- -
-
-
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
- -
-
-
-
-
-
-
-
-
-
-
-
- -
-
- - - -
-
- - - -
-
-
-
-
-
-