diff --git a/dashboards-observability/.cypress/integration/1_event_analytics.spec.js b/dashboards-observability/.cypress/integration/1_event_analytics.spec.js index d8dbde877..ca1f3d5ae 100644 --- a/dashboards-observability/.cypress/integration/1_event_analytics.spec.js +++ b/dashboards-observability/.cypress/integration/1_event_analytics.spec.js @@ -305,7 +305,7 @@ 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'); + .contains('Default Timestamp').should('exist'); cy.get('[data-attr-field="timestamp"] [data-test-subj="eventFields__default-timestamp-mark"').should('not.exist'); }); }); @@ -426,8 +426,8 @@ describe('Live tail stop automatically', () => { cy.get('[data-test-subj="eventExplorer__topLevelTabbing"]') .find('button.euiTab') .should('have.length', initialLength + 1); + }); }); -}); it('Click to switch to another tab', () => { cy.get('[data-test-subj="eventExplorer__addNewTab"]').click(); @@ -659,4 +659,195 @@ describe('Renders chart and verify Toast message if X-axis and Y-axis values are cy.get('[data-test-subj="eventExplorer__querySaveConfirm"]').click(); cy.get('[data-test-subj="euiToastHeader"]').contains('Invalid value options configuration selected.').should('exist'); }); -}); \ No newline at end of file +}); + +describe('Render line chart for value options ', () => { + beforeEach(() => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[5].query, TEST_QUERIES[5].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Line').type('{enter}') + }); + + it('Render line chart and add value Options ', () => { + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Line Chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Line chart with value adding Parameter'); + cy.get('.euiComboBox__inputWrap.euiComboBox__inputWrap-isClearable').eq(1).click(); + cy.get('.euiComboBoxOption__content').eq(0).click(); + cy.get('.euiComboBox__inputWrap.euiComboBox__inputWrap-isClearable').eq(2).click(); + cy.get('.euiComboBoxOption__content').eq(0).click(); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true }); + cy.get('.nsewdrag.drag.cursor-pointer').should('be.visible') + }); +}); + +describe('Render line chart for Legend ', () => { + beforeEach(() => { + cy.wait(2000); + landOnEventVisualizations(); + querySearch(TEST_QUERIES[5].query, TEST_QUERIES[5].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Line').click(); + }); + + it('Render line chart and verify legends for Show and Hidden', () => { + 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 }); + cy.wait(delay); + cy.get('.nsewdrag.drag.cursor-pointer').should('be.visible') + .and(chart => { + expect(chart.height()).to.be.greaterThan(200) + }) + }); + + it('Render line chart and verify legends for Position Right and Bottom', () => { + 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 }); + }) +}); + +describe('Render line chart for Chart Styles ', () => { + beforeEach(() => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[5].query, TEST_QUERIES[5].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Line').type('{enter}') + }); + + it('Render line chart and Verify Chart Style of Lines Mode with Smooth Interpolation ', () => { + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Line Chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Line chart with Line Mode and Smooth Interpolation'); + cy.get('[data-text="Lines"]').should('have.text', 'Lines'); + cy.get('[data-text="Lines"] [data-test-subj="lines"]').should('have.attr', 'checked'); + cy.get('[data-text="Smooth"]').should('have.text', 'Smooth'); + cy.get('[data-text="Smooth"] [data-test-subj="spline"]').should('have.attr', 'checked'); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true }); + }); + + it('Render line chart and Verify Chart Style of Lines Mode with Smooth Interpolation with higher Line width and Fill Opacity ', () => { + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Line Chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Line chart with Line Mode and Smooth Interpolation'); + cy.get('[data-text="Lines"]').should('have.text', 'Lines'); + cy.get('[data-text="Lines"] [data-test-subj="lines"]').should('have.attr', 'checked'); + cy.get('[data-text="Smooth"]').should('have.text', 'Smooth'); + cy.get('[data-text="Smooth"] [data-test-subj="spline"]').should('have.attr', 'checked'); + cy.get('input[type="range"]').eq(0) + .then($el => $el[0].stepUp(4)) + .trigger('change') + cy.get('.euiRangeSlider').eq(0).should('have.value', 6) + cy.get('input[type="range"]').eq(1) + .then($el => $el[0].stepUp(40)) + .trigger('change') + cy.get('.euiRangeSlider').eq(1).should('have.value', 80) + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true }); + }); + + it('Render line chart and Verify Chart Style of Lines Mode with Linear Interpolation ', () => { + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Line Chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Line chart with Line Mode and Linear Interpolation'); + cy.get('[data-text="Lines"]').should('have.text', 'Lines'); + cy.get('[data-text="Lines"] [data-test-subj="lines"]').should('have.attr', 'checked'); + cy.get('[data-text="Linear"]').should('have.text', 'Linear').click(); + cy.get('[data-text="Linear"]').should('have.text', 'Linear'); + cy.get('[data-text="Linear"] [data-test-subj="linear"]').should('not.have.attr', 'checked'); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true }); + }); + + it('Render line chart and Verify Chart Style of Lines Mode with Step before Interpolation ', () => { + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Line Chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Line chart with Line Mode and Step before Interpolation'); + cy.get('[data-text="Lines"]').should('have.text', 'Lines'); + cy.get('[data-text="Lines"] [data-test-subj="lines"]').should('have.attr', 'checked'); + cy.get('[data-text="Step before"]').should('have.text', 'Step before').click(); + cy.get('[data-text="Step before"] [data-test-subj="Step before"]').should('not.have.attr', 'checked'); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true }); + }); + + it('Render line chart and Verify Chart Style of Lines Mode with Step after Interpolation ', () => { + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Line Chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Line chart with Line Mode and Step after Interpolation'); + cy.get('[data-text="Lines"]').should('have.text', 'Lines'); + cy.get('[data-text="Lines"] [data-test-subj="lines"]').should('have.attr', 'checked'); + cy.get('[data-text="Step after"]').should('have.text', 'Step after').click(); + cy.get('[data-text="Step after"] [data-test-subj="Step after"]').should('not.have.attr', 'checked'); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true }); + }); + + it('Render line chart and Verify Chart Style of Bars Mode with Center bar alignment ', () => { + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Line Chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Line chart with chart style of Bars'); + cy.get('[data-text="Bars"]').should('have.text', 'Bars').click(); + cy.get('[data-text="Center"] [data-test-subj="center"]').should('have.attr', 'checked'); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true }); + }); + + it('Render line chart and Verify Chart Style of Bars Mode with Before bar alignment ', () => { + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Line Chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Line chart with chart style of Bars'); + cy.get('[data-text="Bars"]').should('have.text', 'Bars').click(); + cy.get('[data-text="Before"] [data-test-subj="before"]').should('not.have.attr', 'checked'); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true }); + }); + + it('Render line chart and Verify Chart Style of Bars Mode with After bar alignment ', () => { + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Line Chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Line chart with chart style of Bars'); + cy.get('[data-text="Bars"]').should('have.text', 'Bars').click(); + cy.get('[data-text="After"] [data-test-subj="after"]').should('not.have.attr', 'checked'); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true }); + }); + + it('Render line chart and Verify Chart Style of Points Mode', () => { + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Line Chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Line chart with chart style of Points'); + cy.get('[data-text="Points"]').should('have.text', 'Points').click(); + cy.get('[data-text="Points"] [data-test-subj="markers"]').should('not.have.attr', 'checked'); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true }); + }); + + it('Render line chart and Verify Chart Style of Points Mode with larger Point size', () => { + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Line Chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Line chart with chart style of Points'); + cy.get('[data-text="Points"]').should('have.text', 'Points').click(); + cy.get('[data-text="Points"] [data-test-subj="markers"]').should('not.have.attr', 'checked'); + cy.get('input[type="range"]') + .then($el => $el[0].stepUp(30)) + .trigger('change') + cy.get('.euiRangeSlider').should('have.value', 35) + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true }); + }); + + it('Render line chart and Verify Chart Style of Lines+Points Mode', () => { + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Line Chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Line chart with chart style of Lines and Points'); + cy.get('[data-text="Lines + Points"]').should('have.text', 'Lines + Points').click(); + cy.get('[data-text="Lines + Points"] [data-test-subj="lines+markers"]').should('not.have.attr', 'checked'); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true }); + }); + + it('Render line chart and Verify Chart Style of Lines+Points Mode with Line Width, Fill Opacity and Point Size', () => { + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Line Chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Line chart with chart style of Lines and Points'); + cy.get('[data-text="Lines + Points"]').should('have.text', 'Lines + Points').click(); + cy.get('[data-text="Lines + Points"] [data-test-subj="lines+markers"]').should('not.have.attr', 'checked'); + cy.get('input[type="range"]').eq(0) + .then($el => $el[0].stepUp(4)) + .trigger('change') + cy.get('.euiRangeSlider').eq(0).should('have.value', 6) + cy.get('input[type="range"]').eq(1) + .then($el => $el[0].stepUp(40)) + .trigger('change') + cy.get('.euiRangeSlider').eq(1).should('have.value', 80) + cy.get('input[type="range"]').eq(2) + .then($el => $el[0].stepUp(15)) + .trigger('change') + cy.get('.euiRangeSlider').eq(2).should('have.value', 20) + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true }); + }); +}); diff --git a/dashboards-observability/.cypress/utils/event_constants.js b/dashboards-observability/.cypress/utils/event_constants.js index 130d32590..380a0bea8 100644 --- a/dashboards-observability/.cypress/utils/event_constants.js +++ b/dashboards-observability/.cypress/utils/event_constants.js @@ -28,6 +28,10 @@ export const TEST_QUERIES = [ query: 'source = opensearch_dashboards_sample_data_logs | stats count(), avg(bytes) by host, tags', dateRangeDOM: YEAR_TO_DATE_DOM_ID }, + { + query: 'source=opensearch_dashboards_sample_data_logs | where response="503" or response="404" | stats count() as ip_count, sum(bytes) as sum_bytes by host, response | rename response as resp_code | sort - ip_count, + sum_bytes | eval per_ip_bytes=sum_bytes/ip_count, double_per_ip_bytes = 2 * per_ip_bytes', + dateRangeDOM: YEAR_TO_DATE_DOM_ID + }, ]; export const TESTING_PANEL = 'Mock Testing Panels'; diff --git a/dashboards-observability/common/constants/shared.ts b/dashboards-observability/common/constants/shared.ts index c27d2bab1..706b7e0a5 100644 --- a/dashboards-observability/common/constants/shared.ts +++ b/dashboards-observability/common/constants/shared.ts @@ -148,4 +148,26 @@ export const LIVE_OPTIONS = [ }, ]; -export const LIVE_END_TIME ='now'; \ No newline at end of file +export const LIVE_END_TIME ='now'; + +export interface DefaultChartStylesProps { + DefaultMode: string, + Interpolation: string, + LineWidth: number, + FillOpacity: number, + MarkerSize: number, + ShowLegend: string, + LegendPosition: string +}; + +export const DefaultChartStyles: DefaultChartStylesProps = { + DefaultMode: 'lines', + Interpolation: 'spline', + LineWidth: 2, + FillOpacity: 40, + MarkerSize: 5, + ShowLegend: 'show', + LegendPosition: 'v' +} + +export const FILLOPACITY_DIV_FACTOR = 200; diff --git a/dashboards-observability/common/types/explorer.ts b/dashboards-observability/common/types/explorer.ts index f373929c3..c523b1b09 100644 --- a/dashboards-observability/common/types/explorer.ts +++ b/dashboards-observability/common/types/explorer.ts @@ -187,9 +187,11 @@ export interface IConfigPanelOptions { export interface IConfigPanelOptionSection { name: string; component: null; - mapTo: 'mode'; + mapTo: 'mode' | string; props?: any; isSingleSelection?: boolean; + defaultState?: number; + eleType?: string; } export interface IVisualizationTypeDefination { 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 ba0e43feb..5512aa416 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 @@ -958,38 +958,158 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` }, 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": "mode", + "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": "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 { - "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 { @@ -1191,38 +1311,158 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` }, 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": "mode", + "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": "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 { - "modeId": "lines+markers", - "name": "Lines + Markers", + "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 { @@ -1478,38 +1718,158 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` }, 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": "mode", + "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": "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 { - "modeId": "lines+markers", - "name": "Lines + Markers", + "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 { @@ -1621,9 +1981,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", @@ -1700,9 +2070,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", 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 babb55222..72e9af72c 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 @@ -550,36 +550,156 @@ exports[`Config panel component Renders config panel with visualization data 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": "mode", + "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": "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 { - "modeId": "lines+markers", - "name": "Lines + Markers", + "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 { + "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, }, }, ], 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 new file mode 100644 index 000000000..b497d3466 --- /dev/null +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_button_group.tsx @@ -0,0 +1,41 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { uniqueId } from 'lodash'; +import { EuiTitle, EuiSpacer, EuiButtonGroup } from '@elastic/eui'; +interface ToggleButtonOptions { + id: string; + label: string; +} +interface ToggleGroupProps { + title: string; + legend: string; + groupOptions: ToggleButtonOptions[]; + idSelected: string; + handleButtonChange: (id: string, value?: any) => void; +} +export const ButtonGroupItem: React.FC = ({ + title, legend, groupOptions, idSelected, handleButtonChange +}) => ( + <> + +

{title}

+
+ +
+ +
+ +); diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_legend.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_legend.tsx new file mode 100644 index 000000000..be9bf9d47 --- /dev/null +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_legend.tsx @@ -0,0 +1,50 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useCallback, useMemo } from 'react'; +import { EuiAccordion, EuiSpacer } from '@elastic/eui'; +import { ButtonGroupItem } from './config_button_group'; +import { IConfigPanelOptionSection } from '../../../../../../../../common/types/explorer'; + +export const ConfigLegend = ({ schemas, vizState, handleConfigChange }: any) => { + const handleConfigurationChange = useCallback( + (stateFiledName) => { + return (changes) => { + handleConfigChange({ + ...vizState, + [stateFiledName]: changes, + }); + }; + }, + [handleConfigChange, vizState] + ); + + const dimensions = useMemo(() => { + return schemas.map((schema: IConfigPanelOptionSection, index: number) => { + const DimensionComponent = schema.component || ButtonGroupItem; + const 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 ( + <> + + + + ); + }); + }, [schemas, vizState, handleConfigurationChange]);; + + return ( + + {dimensions} + + ); +}; 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..eac9410ee --- /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,93 @@ +/* + * 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) => { + let params; + const DimensionComponent = schema.component || ButtonGroupItem; + 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, + }; + } else if (schema.eleType === 'slider') { + params = { + maxRange: schema?.props?.max, + title: schema.name, + currentRange: vizState[schema.mapTo] || schema?.defaultState, + handleSliderChange: handleConfigurationChange(schema.mapTo), + vizState, + ...schema.props, + }; + } + return ( + <> + + + + ) + }) + , [currentSchemas, vizState, handleConfigurationChange]); + + return ( + + {dimensions} + + ); +}; 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..ae1028b56 --- /dev/null +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_style_slider.tsx @@ -0,0 +1,34 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { uniqueId } from 'lodash'; +import { EuiTitle, EuiSpacer, EuiRange } from '@elastic/eui'; +interface Props { + title: string; + currentRange: string; + maxRange: number; + handleSliderChange: (e: React.ChangeEvent | React.MouseEvent) => void; +} + +export const SliderConfig: React.FC = ({ + title, currentRange, handleSliderChange, maxRange +}) => ( + <> + +

{title}

+
+ + handleSliderChange(e.target.value)} + showInput + aria-label="change lineWidth slider" + /> + +); diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts index f75c9126b..c4b4ad962 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts @@ -10,3 +10,5 @@ export { ConfigDataLinks } from './config_data_links'; export { ConfigThresholds } from './config_thresholds'; export { ConfigText } from './config_text'; export { ConfigGaugeValueOptions } from './config_gauge_options'; +export { ConfigLineChartStyles } from './config_line_chart_styles'; +export { ConfigLegend } from './config_legend'; diff --git a/dashboards-observability/public/components/event_analytics/utils/utils.tsx b/dashboards-observability/public/components/event_analytics/utils/utils.tsx index d142f472f..fcfa74620 100644 --- a/dashboards-observability/public/components/event_analytics/utils/utils.tsx +++ b/dashboards-observability/public/components/event_analytics/utils/utils.tsx @@ -119,8 +119,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} @@ -285,3 +285,13 @@ export const findAutoInterval = (start: string = '', end: string = '') => { return [minInterval, [{ text: 'Auto', value: 'auto_' + minInterval }, ...TIME_INTERVAL_OPTIONS]]; }; + +// to convert hex color code to rgba format +export const hexToRgba = (hex: string = '#3CA1C7', opacity: number = 1) => { + // default color PLOTLY_COLOR[0]: '#3CA1C7' + const defaultColor = [hex, '60', '161', '199']; + const rgbElements = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) || defaultColor; + const [, r, g, b] = rgbElements.map((color) => parseInt(color, 16)); + const rgbaFormat = `rgba(${r},${g},${b},${opacity})`; + return rgbaFormat; +}; 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..3c3c9111c 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 @@ -349,9 +349,19 @@ exports[`Line component Renders line component 1`] = ` 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": "count()", - "type": "line", + "type": "scatter", "x": Array [ "error", "info", @@ -377,6 +387,9 @@ exports[`Line component Renders line component 1`] = ` "#8C55A3", ], "height": 220, + "legend": Object { + "orientation": "v", + }, "margin": Object { "b": 15, "l": 60, @@ -398,9 +411,19 @@ exports[`Line component Renders line component 1`] = ` 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": "count()", - "type": "line", + "type": "scatter", "x": Array [ "error", "info", @@ -432,8 +455,7 @@ exports[`Line component Renders line component 1`] = ` "height": 220, "hovermode": "closest", "legend": Object { - "orientation": "h", - "traceorder": "normal", + "orientation": "v", }, "margin": Object { "b": 15, diff --git a/dashboards-observability/public/components/visualizations/charts/lines/line.tsx b/dashboards-observability/public/components/visualizations/charts/lines/line.tsx index 4ce620efd..27eb0e044 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 { hexToRgba } 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 }, @@ -25,10 +28,14 @@ export const Line = ({ visualizations, layout, config }: any) => { const yaxis = dataConfig?.valueOptions && dataConfig.valueOptions.xaxis ? dataConfig.valueOptions.yaxis : []; 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 ? false : true; + 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)) { @@ -38,20 +45,53 @@ export const Line = ({ visualizations, layout, config }: any) => { } const [calculatedLayout, lineValues] = useMemo(() => { - let calculatedLineValues = valueSeries.map((field: any) => { + const isBarMode = mode === 'bar'; + + let calculatedLineValues = valueSeries.map((field: any, index: number) => { + const fillColor = hexToRgba(PLOTLY_COLOR[index % PLOTLY_COLOR.length], fillOpacity); + const barMarker = { + color: fillColor, + line: { + color: PLOTLY_COLOR[index], + width: lineWidth + } + }; + const fillProperty = { + fill: 'tozeroy', + fillcolor: fillColor, + }; return { x: data[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name], y: data[field.name], - type: 'line', + type: isBarMode ? 'bar' : 'scatter', name: field.name, mode, + ...!['bar', 'markers'].includes(mode) && fillProperty, + line: { + shape: lineShape, + width: lineWidth, + color: PLOTLY_COLOR[index], + }, + marker: { + size: markerSize, + ...isBarMode && barMarker, + }, }; }); + var layoutForBarMode = { + barmode: 'group', + }; const mergedLayout = { ...layout, ...layoutConfig.layout, title: dataConfig?.panelOptions?.title || layoutConfig.layout?.title || '', + legend: { + ...layout.legend, + orientation: legendPosition, + }, + showlegend: showLegend, + ...isBarMode && layoutForBarMode, }; 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..1f1f86779 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', @@ -61,27 +66,105 @@ export const createLineTypeDefinition = (params: any = {}) => ({ ], }, { - id: 'chart_options', - name: 'Chart options', - editor: ConfigValueOptions, - mapTo: 'chartOptions', + id: 'legend', + name: 'Legend', + editor: ConfigLegend, + mapTo: 'legend', schemas: [ { - name: 'Mode', - 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, - mapTo: 'mode', props: { - dropdownList: [ - { name: 'Markers', modeId: 'markers' }, - { name: 'Lines', modeId: 'lines' }, - { name: 'Lines + Markers', modeId: 'lines+markers' }, + options: [ + { name: 'Right', id: 'v' }, + { name: 'Bottom', id: 'h' }, ], - defaultSelections: [{ name: 'Lines', modeId: 'lines' }], + defaultSelections: [{ name: 'Right', id: LegendPosition }], }, }, ], }, + { + id: 'chart_styles', + name: 'Chart styles', + editor: ConfigLineChartStyles, + mapTo: 'chartStyles', + schemas: [ + { + name: 'Mode', + component: ButtonGroupItem, + mapTo: 'style', + eleType: 'buttons', + props: { + options: [ + { name: 'Lines', id: 'lines' }, + { name: 'Bars', id: 'bar' }, + { name: 'Points', id: 'markers' }, + { name: 'Lines + Points', id: 'lines+markers' } + ], + 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, + } + }, + ], + }, { id: 'thresholds', name: 'Thresholds',