diff --git a/.cypress/integration/app_analytics_test/app_analytics.spec.js b/.cypress/integration/app_analytics_test/app_analytics.spec.js index f1147d5a12..ce0ff7820f 100644 --- a/.cypress/integration/app_analytics_test/app_analytics.spec.js +++ b/.cypress/integration/app_analytics_test/app_analytics.spec.js @@ -206,6 +206,7 @@ describe('Setting availability', () => { cy.get('.euiTableRow').should('have.length.lessThan', 1); cy.get('[data-test-subj="applicationTitle"]').should('contain', nameThree); cy.get('.euiBreadcrumb[href="#/"]').click(); + cy.reload(); cy.get(`[data-test-subj="${nameThree}ApplicationLink"]`); cy.get('[data-test-subj="setAvailabilityHomePageLink"]').first().click(); cy.get('[data-test-subj="applicationTitle"]').should('contain', nameThree); @@ -254,6 +255,7 @@ describe('Viewing application', () => { it('Shows latency variance in dashboards table', () => { changeTimeTo24('years'); + cy.get('[data-test-subj="app-analytics-traceTab"]').click(); cy.get('[data-test-subj="trace-groups-service-operation-accordian"]').click(); cy.get('[data-test-subj="dashboardTable"]').first().within(($table) => { cy.get('.plot-container').should('have.length.at.least', 1); @@ -261,6 +263,7 @@ describe('Viewing application', () => { }); it('Adds filter when Trace group name is clicked', () => { + cy.get('[data-test-subj="app-analytics-traceTab"]').click(); cy.get('[data-test-subj="trace-groups-service-operation-accordian"]').click(); cy.get('[data-test-subj="dashboard-table-trace-group-name-button"]').contains('client_create_order').click(); cy.get('[data-test-subj="client_create_orderFilterBadge"]').should('exist'); @@ -271,7 +274,7 @@ describe('Viewing application', () => { it('Opens service detail flyout when Service Name is clicked', () => { cy.get('[data-test-subj="app-analytics-serviceTab"]').click(); - cy.get('.euiLink').contains('authentication').click(); + cy.get('*[data-test-subj^="service-flyout-action-btntrace"]').eq(0).click(); cy.get('[data-test-subj="serviceDetailFlyoutTitle"]').should('be.visible'); cy.get('[data-test-subj="Number of connected servicesDescriptionList"]').should('contain', '3'); cy.get('[data-text="Errors"]').eq(1).click(); // Selecting errors tab within flyout @@ -432,11 +435,12 @@ describe('Viewing application', () => { cy.get('[data-test-subj="app-analytics-configTab"]').click(); cy.get('select').select(visOneName); cy.intercept('PUT', `**/api/observability/application`).as('selectUpdate'); - cy.wait('@selectUpdate') - cy.wait(2000); // despite the previous wait grabbing the call that updates the panel select, it doesn't appear without this + cy.wait('@selectUpdate'); + moveToHomePage(); cy.intercept('GET', `**/api/observability/operational_panels/panels/**`).as('loadingPanels') cy.wait('@loadingPanels'); + cy.reload(); 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('[data-test-subj="app-analytics-configTab"]').click(); @@ -479,8 +483,7 @@ describe('Separate from other plugins', () => { cy.visit( `${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/` ); - cy.get('[data-test-subj="operationalPanelsActionsButton"]', { timeout: timeoutDelay }).click(); - cy.get('[data-test-subj="addSampleContextMenuItem"]', { timeout: timeoutDelay }).click(); + cy.get('.euiButtonContent').contains('Add samples').click(); cy.get('[data-test-subj="confirmModalConfirmButton"]', { timeout: timeoutDelay }).click(); cy.get('.euiLink').contains('[Logs] Web traffic Panel').first().click(); cy.get('[data-test-subj="addVisualizationButton"]').click(); @@ -517,10 +520,12 @@ describe('Editing application', () => { cy.get('[data-test-subj="logSourceAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="searchAutocompleteTextArea"]').should('be.disabled'); cy.get('[data-test-subj="servicesEntitiesAccordion"]').trigger('mouseover').click(); + cy.get('[data-test-subj="servicesEntitiesCountBadge"]').should('contain', '1'); cy.get('[data-test-subj="servicesEntitiesComboBox"]').click(); 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="traceGroupsCountBadge"]').should('contain', '2'); 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'); @@ -547,15 +552,9 @@ describe('Application Analytics home page', () => { }); it('Renames application', () => { - 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().find('.euiCheckbox').click(); - cy.wait(2000); // checkbox being clicked has a small delay before enabling action button - cy.get('[data-test-subj="appAnalyticsActionsButton"]').click(); - cy.get('[data-test-subj="renameApplicationContextMenuItem"]').click(); - cy.get('[data-test-subj="customModalFieldText"]').clear().click().type(newName); - cy.get('[data-test-subj="runModalButton"]').click(); + cy.get('[data-test-subj="renameApplication"]').eq(0).click(); + cy.get('input[type="text"]').clear().click().type(newName); + cy.get('.euiButton__text').contains('Rename').click(); cy.get('.euiToast').contains(`Application successfully renamed to "${newName}"`); cy.get('.euiTableRow').first().within(($row) => { cy.get('.euiLink').contains(newName).should('exist'); @@ -563,23 +562,20 @@ describe('Application Analytics home page', () => { }); it('Deletes application', () => { - 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.get('.euiTableRow').eq(2).within(($row) => { - cy.get('.euiCheckbox').click(); - }); - cy.get('[data-test-subj="appAnalyticsActionsButton"]').click(); - cy.get('[data-test-subj="deleteApplicationContextMenuItem"]').click(); + cy.get('.euiFieldSearch').clear().type(nameTwo); + cy.get('[data-test-subj="deleteApplication"]').click(); cy.get('[data-test-subj="popoverModal__deleteTextInput"]').type('delete'); cy.get('[data-test-subj="popoverModal__deleteButton"').click(); cy.get('.euiToast').contains(`Applications successfully deleted!`); - cy.get(`[data-test-subj="${newName}ApplicationLink"]`).should('not.exist'); + cy.get('.euiFieldSearch').clear().type(nameThree); + cy.get('[data-test-subj="deleteApplication"]').click(); + cy.get('[data-test-subj="popoverModal__deleteTextInput"]').type('delete'); + cy.get('[data-test-subj="popoverModal__deleteButton"').click(); + cy.get('.euiFieldSearch').clear().type(newName); + cy.get('[data-test-subj="deleteApplication"]').click(); + cy.get('[data-test-subj="popoverModal__deleteTextInput"]').type('delete'); + cy.get('[data-test-subj="popoverModal__deleteButton"').click(); + cy.get('.euiFieldSearch').clear() + cy.get('[data-test-subj="applicationHomePageTitle"]').contains('(0)'); }); }); diff --git a/public/components/application_analytics/components/app_table.tsx b/public/components/application_analytics/components/app_table.tsx index d9d702334d..3141a61c7c 100644 --- a/public/components/application_analytics/components/app_table.tsx +++ b/public/components/application_analytics/components/app_table.tsx @@ -86,7 +86,7 @@ export function AppTable(props: AppTableProps) { ); clear(); fetchApplications(); - }, [applications.length]); + }, []); const clear = () => { setFilters([]); @@ -138,6 +138,7 @@ export function AppTable(props: AppTableProps) { description: 'Rename this application', icon: 'pencil', type: 'icon', + 'data-test-subj': 'renameApplication', onClick: (app: ApplicationType) => renameApp(app), }, { @@ -146,6 +147,7 @@ export function AppTable(props: AppTableProps) { icon: 'trash', type: 'icon', color: 'danger', + 'data-test-subj': 'deleteApplication', onClick: (app: ApplicationType) => deleteApp(app), }, ]; @@ -231,7 +233,7 @@ export function AppTable(props: AppTableProps) { {!newNavigation && ( - +

Applications {` (${applications.length})`}

)} diff --git a/public/components/application_analytics/components/application.tsx b/public/components/application_analytics/components/application.tsx index 140c156930..ff2536f3b9 100644 --- a/public/components/application_analytics/components/application.tsx +++ b/public/components/application_analytics/components/application.tsx @@ -294,7 +294,7 @@ export function Application(props: AppDetailProps) { { const deleteSavedVisualizationsForPanel = async (appPanelId: string) => { const savedVizIdsToDelete = await fetchPanelsVizIdList(http, appPanelId); if (!isEmpty(savedVizIdsToDelete)) { - savedObjects - .deleteSavedObjectsList({ objectIdList: savedVizIdsToDelete }) + await SavedObjectsActions.deleteBulk({ objectIdList: savedVizIdsToDelete }) .then((_res) => { deletePanelForApp(appPanelId); }) @@ -350,14 +350,10 @@ export const Home = (props: HomeProps) => { }; // Delete existing applications - const deleteApp = (appList: string[], panelList: string[], toastMessage?: string) => { + const deleteApp = async (appList: string[], panelList: string[], toastMessage?: string) => { return http - .delete(`${APP_ANALYTICS_API_PREFIX}/${appList.join(',')}`) - .then((res) => { - setApplicationList((prevApplicationList) => { - return prevApplicationList.filter((app) => !appList.includes(app.id)); - }); - + .delete(`${APP_ANALYTICS_API_PREFIX}/${[...appList].join(',')}`) + .then(async (res) => { for (let i = 0; i < appList.length; i++) { removeTabData(dispatch, appList[i], ''); } @@ -366,6 +362,9 @@ export const Home = (props: HomeProps) => { deleteSavedVisualizationsForPanel(panelList[i]); } + setApplicationList((prevApplicationList) => { + return prevApplicationList.filter((app) => !appList.includes(app.id)); + }); const message = toastMessage || `Application${appList.length > 1 ? 's' : ''} successfully deleted!`; setToast(message); diff --git a/public/components/custom_panels/panel_modules/visualization_container/__tests__/__snapshots__/visualization_container.test.tsx.snap b/public/components/custom_panels/panel_modules/visualization_container/__tests__/__snapshots__/visualization_container.test.tsx.snap index 38d69afad3..b855f47d64 100644 --- a/public/components/custom_panels/panel_modules/visualization_container/__tests__/__snapshots__/visualization_container.test.tsx.snap +++ b/public/components/custom_panels/panel_modules/visualization_container/__tests__/__snapshots__/visualization_container.test.tsx.snap @@ -94,6 +94,48 @@ exports[`Visualization Container Component renders add visualization container 1 + +
+ + + + + + + +
+
diff --git a/public/components/custom_panels/panel_modules/visualization_container/visualization_container.tsx b/public/components/custom_panels/panel_modules/visualization_container/visualization_container.tsx index 365fe2b6fc..9730eb8706 100644 --- a/public/components/custom_panels/panel_modules/visualization_container/visualization_container.tsx +++ b/public/components/custom_panels/panel_modules/visualization_container/visualization_container.tsx @@ -5,8 +5,10 @@ import { EuiSmallButton, + EuiSmallButtonIcon, EuiCodeBlock, EuiContextMenuItem, + EuiContextMenuPanel, EuiFlexGroup, EuiFlexItem, EuiIcon, @@ -17,6 +19,7 @@ import { EuiModalHeader, EuiModalHeaderTitle, EuiPanel, + EuiPopover, EuiSpacer, EuiText, EuiToolTip, @@ -100,6 +103,7 @@ export const VisualizationContainer = ({ onEditClick, cloneVisualization, showFlyout, + removeVisualization, catalogVisualization, inlineEditor, actionMenuType, @@ -112,6 +116,7 @@ export const VisualizationContainer = ({ const [visualizationData, setVisualizationData] = useState([]); const [isLoading, setIsLoading] = useState(true); const [isError, setIsError] = useState({} as VizContainerError); + const onActionsMenuClick = () => setIsPopoverOpen((currPopoverOpen) => !currPopoverOpen); const closeActionsMenu = () => setIsPopoverOpen(false); const { http, pplService } = coreRefs; const { setToast } = useToast(); @@ -233,10 +238,7 @@ export const VisualizationContainer = ({ , ]; - if ( - visualizationMetaData?.metricType === PROMQL_METRIC_SUBTYPE && - actionMenuType === 'metricsGrid' - ) { + if (actionMenuType === 'metricsGrid') { popoverPanel = [showPPLQueryPanel]; } else if (usedInNotebooks) { popoverPanel = [popoverPanel[0]]; @@ -368,6 +370,37 @@ export const VisualizationContainer = ({ + + {editMode ? ( + { + removeVisualization(visualizationId); + }} + /> + ) : ( + + } + isOpen={isPopoverOpen} + closePopover={closeActionsMenu} + anchorPosition="downLeft" + panelPaddingSize="none" + > + + + )} + {inlineEditor}