diff --git a/.cypress/integration/notebooks_test/notebooks.spec.js b/.cypress/integration/notebooks_test/notebooks.spec.js index 6e21785c1..d4a316eb3 100644 --- a/.cypress/integration/notebooks_test/notebooks.spec.js +++ b/.cypress/integration/notebooks_test/notebooks.spec.js @@ -75,7 +75,7 @@ describe('Testing notebooks table', () => { cy.get('h3[data-test-subj="notebookTableTitle"]').contains('Notebooks (0)').should('exist'); cy.get('div[data-test-subj="notebookEmptyTableText"]').contains('No notebooks'); cy.get('a[data-test-subj="notebookEmptyTableCreateBtn"]').contains('Create notebook'); - cy.get('button[data-test-subj="notebookEmptyTableAddSamplesBtn"]').contains('Add samples'); + cy.get('button[data-test-subj="notebookEmptyTableAddSamplesBtn"]').contains('Add sample notebooks'); }); it('Displays error toast for invalid notebook name', () => { @@ -135,7 +135,7 @@ describe('Testing paragraphs', () => { cy.get('a[data-test-subj="createNotebookPrimaryBtn"]').click(); cy.get('input[data-test-subj="custom-input-modal-input"]').focus().type(TEST_NOTEBOOK); cy.get('button[data-test-subj="custom-input-modal-confirm-button"]').click(); - cy.get('div[data-test-subj="notebookTitle"]').contains(TEST_NOTEBOOK).should('exist'); + cy.get('h3[data-test-subj="notebookTableTitle"]').contains(TEST_NOTEBOOK).should('exist'); }); beforeEach(() => { @@ -166,7 +166,7 @@ describe('Testing paragraphs', () => { it('Has working breadcrumbs', () => { cy.get('a[data-test-subj="breadcrumb last"]').contains(TEST_NOTEBOOK).click(); - cy.get('div[data-test-subj="notebookTitle"]').contains(TEST_NOTEBOOK).should('exist'); + cy.get('h3[data-test-subj="notebookTableTitle"]').contains(TEST_NOTEBOOK).should('exist'); cy.get('a[data-test-subj="breadcrumb"]').contains('Notebooks').click(); cy.get('h3[data-test-subj="notebookTableTitle"]').should('exist'); cy.get('a[data-test-subj="breadcrumb first"]').contains('Observability').click(); @@ -175,12 +175,8 @@ describe('Testing paragraphs', () => { it('Paragraph actions layout', () => { cy.get('button[data-test-subj="notebook-paragraph-actions-button"]').should('exist').click(); - cy.get('.euiContextMenuPanelTitle').contains('Actions'); - cy.get('.euiContextMenuItem__text').eq(0).contains('Add paragraph to top'); - cy.get('.euiContextMenuItem__text').eq(1).contains('Add paragraph to bottom'); - cy.get('.euiContextMenuItem__text').eq(2).contains('Run all paragraphs'); - cy.get('.euiContextMenuItem__text').eq(3).contains('Clear all outputs'); - cy.get('.euiContextMenuItem__text').eq(4).contains('Delete all paragraphs'); + cy.get('.euiContextMenuItem__text').eq(0).contains('To top'); + cy.get('.euiContextMenuItem__text').eq(1).contains('To bottom'); }); it('Shows output message', () => { @@ -243,15 +239,16 @@ describe('Testing paragraphs', () => { }); it('Adds a SQL query paragraph', () => { - cy.get('button[data-test-subj="AddParagraphButton"]').click(); - cy.get('button[data-test-subj="AddCodeBlockBtn"]').click(); + cy.get('button[data-test-subj="notebook-paragraph-actions-button"]').click(); + cy.get('span.euiContextMenuItem__text').contains('To top').click(); + cy.get('button.euiContextMenuItem').contains('Code block').click(); - cy.get('textarea[data-test-subj="editorArea-3"]').clear(); - cy.get('textarea[data-test-subj="editorArea-3"]').focus(); - cy.get('textarea[data-test-subj="editorArea-3"]').type(SQL_QUERY_TEXT); - cy.get('button[data-test-subj="runRefreshBtn-3"]').click(); + cy.get('textarea[data-test-subj="editorArea-0"]').clear(); + cy.get('textarea[data-test-subj="editorArea-0"]').focus(); + cy.get('textarea[data-test-subj="editorArea-0"]').type(SQL_QUERY_TEXT); + cy.get('button[data-test-subj="runRefreshBtn-0"]').click(); - cy.get('textarea[data-test-subj="editorArea-3"]').should('not.exist'); + cy.get('textarea[data-test-subj="editorArea-0"]').should('not.exist'); cy.get('div[data-test-subj="queryOutputText"]') .contains('select * from opensearch_dashboards_sample_data_flights limit 20') .should('exist'); @@ -301,10 +298,11 @@ describe('Testing paragraphs', () => { }); it('Adds an observability visualization paragraph', () => { - cy.get('button[data-test-subj="AddParagraphButton"]').click(); - cy.get('button[data-test-subj="AddVisualizationBlockBtn"]').click(); + cy.get('button[data-test-subj="notebook-paragraph-actions-button"]').click(); + cy.get('span.euiContextMenuItem__text').contains('To top').click(); + cy.get('button.euiContextMenuItem').contains('Visualization').click(); - cy.get('button[data-test-subj="runRefreshBtn-6"]').click(); + cy.get('button[data-test-subj="runRefreshBtn-0"]').click(); cy.get('div[data-test-subj="paragraphInputErrorText"]') .contains('Visualization is required.') .should('exist'); @@ -314,7 +312,7 @@ describe('Testing paragraphs', () => { .type('[Logs] Count total requests by tags'); cy.get('.euiComboBoxOption__content').contains('[Logs] Count total requests by tags').click(); - cy.get('button[data-test-subj="runRefreshBtn-6"]').click(); + cy.get('button[data-test-subj="runRefreshBtn-0"]').click(); cy.get('h5').contains('[Logs] Count total requests by tags').should('exist'); }); @@ -336,7 +334,7 @@ describe('Testing paragraphs', () => { }); it('Clears outputs', () => { - cy.get('div[data-test-subj="notebookTitle"]').contains(TEST_NOTEBOOK).should('exist'); + cy.get('h3[data-test-subj="notebookTitle"]').contains(TEST_NOTEBOOK).should('exist'); cy.get('[data-test-subj="notebook-paragraph-actions-button"]').should('exist'); cy.get('[data-test-subj="notebook-paragraph-actions-button"]').click(); cy.get('.euiContextMenuItem__text').contains('Clear all outputs').click(); @@ -346,7 +344,7 @@ describe('Testing paragraphs', () => { }); it('Runs all paragraphs', () => { - cy.get('div[data-test-subj="notebookTitle"]').contains(TEST_NOTEBOOK).should('exist'); + cy.get('h3[data-test-subj="notebookTitle"]').contains(TEST_NOTEBOOK).should('exist'); cy.get('[data-test-subj="notebook-paragraph-actions-button"]').click(); cy.get('.euiContextMenuItem__text').contains('Run all paragraphs').click(); @@ -354,27 +352,28 @@ describe('Testing paragraphs', () => { }); it('Adds paragraph to top', () => { - cy.get('div[data-test-subj="notebookTitle"]').contains(TEST_NOTEBOOK).should('exist'); + cy.get('h3[data-test-subj="notebookTitle"]').contains(TEST_NOTEBOOK).should('exist'); - cy.get('[data-test-subj="notebook-paragraph-actions-button"]').click(); - cy.get('.euiContextMenuItem__text').contains('Add paragraph to top').click(); - cy.get('.euiContextMenuItem__text').contains('Code block').click(); + cy.get('button[data-test-subj="notebook-paragraph-actions-button"]').click(); + cy.get('span.euiContextMenuItem__text').contains('To top').click(); + cy.get('button.euiContextMenuItem').contains('Code block').click(); cy.get('.euiText').contains('[1] Code block').should('exist'); }); it('Adds paragraph to bottom', () => { - cy.get('div[data-test-subj="notebookTitle"]').contains(TEST_NOTEBOOK).should('exist'); - cy.get('[data-test-subj="notebook-paragraph-actions-button"]').click(); - cy.get('.euiContextMenuItem__text').contains('Add paragraph to bottom').click(); - cy.get('.euiContextMenuItem__text').contains('Code block').click(); + cy.get('h3[data-test-subj="notebookTitle"]').contains(TEST_NOTEBOOK).should('exist'); + + cy.get('button[data-test-subj="notebook-paragraph-actions-button"]').click(); + cy.get('span.euiContextMenuItem__text').contains('To To bottom').click(); + cy.get('button.euiContextMenuItem').contains('Code block').click(); cy.get('.euiText').contains('[4] Visualization').should('exist'); cy.get('.euiText').contains('[5] Code block').should('exist'); }); it('Moves paragraphs', () => { - cy.get('div[data-test-subj="notebookTitle"]').contains(TEST_NOTEBOOK).should('exist'); + cy.get('h3[data-test-subj="notebookTitle"]').contains(TEST_NOTEBOOK).should('exist'); cy.get('.euiButtonIcon[aria-label="Open paragraph menu"').eq(0).click(); cy.get('.euiContextMenuItem-isDisabled').should('have.length.gte', 2); cy.get('.euiContextMenuItem__text').contains('Move to bottom').click(); @@ -383,7 +382,7 @@ describe('Testing paragraphs', () => { }); it('Duplicates and renames the notebook', () => { - cy.get('div[data-test-subj="notebookTitle"]').contains(TEST_NOTEBOOK).should('exist'); + cy.get('h3[data-test-subj="notebookTitle"]').contains(TEST_NOTEBOOK).should('exist'); cy.get('[data-test-subj="notebook-notebook-actions-button"]').click(); cy.get('.euiContextMenuItem__text').contains('Duplicate notebook').click(); cy.get('.euiButton__text').contains('Duplicate').click(); @@ -401,7 +400,7 @@ describe('Testing paragraphs', () => { }); it('Deletes paragraphs', () => { - cy.get('div[data-test-subj="notebookTitle"]').contains(TEST_NOTEBOOK).should('exist'); + cy.get('h3[data-test-subj="notebookTitle"]').contains(TEST_NOTEBOOK).should('exist'); cy.get('[data-test-subj="notebook-paragraph-actions-button"]').click(); cy.get('.euiContextMenuItem__text').contains('Delete all paragraphs').click(); cy.get('button[data-test-subj="confirmModalConfirmButton"]').click(); diff --git a/public/components/datasources/components/__tests__/data_connection.test.tsx b/public/components/datasources/components/__tests__/data_connection.test.tsx index 51bfe867c..ea53e119a 100644 --- a/public/components/datasources/components/__tests__/data_connection.test.tsx +++ b/public/components/datasources/components/__tests__/data_connection.test.tsx @@ -28,6 +28,9 @@ jest.mock('../../../../../public/framework/core_refs', () => ({ coreRefs: { chrome: { setBreadcrumbs: jest.fn(), + navGroup: { + getNavGroupEnabled: jest.fn().mockReturnValue(false), + }, }, http: { get: jest.fn(), diff --git a/public/components/event_analytics/explorer/query_assist/__tests__/input.test.tsx b/public/components/event_analytics/explorer/query_assist/__tests__/input.test.tsx index 8cb78acb5..013fe5ff9 100644 --- a/public/components/event_analytics/explorer/query_assist/__tests__/input.test.tsx +++ b/public/components/event_analytics/explorer/query_assist/__tests__/input.test.tsx @@ -82,7 +82,7 @@ describe(' spec', () => { ); }); - it('should display toast for generate errors', async () => { + it.skip('should display toast for generate errors', async () => { httpMock.post.mockRejectedValueOnce({ body: { statusCode: 429 } }); const { component } = renderQueryAssistInput(); diff --git a/public/components/integrations/components/__tests__/__snapshots__/added_integration.test.tsx.snap b/public/components/integrations/components/__tests__/__snapshots__/added_integration.test.tsx.snap index 6b74bdb82..db2286ad8 100644 --- a/public/components/integrations/components/__tests__/__snapshots__/added_integration.test.tsx.snap +++ b/public/components/integrations/components/__tests__/__snapshots__/added_integration.test.tsx.snap @@ -60,13 +60,6 @@ exports[`Added Integration View Test Renders added integration view using dummy
- -
- - -
-
- +
-
-
-

+ +
+

+

+

-
-
-
- -
- - + +
-
- +
-
- - - - - - - -
-
- -
- Unknown + + + + + + +
+
+ +
+ Unknown +
+
- +
- -
-
-
+ + +
+
-
+
-
+ @@ -292,84 +282,67 @@ exports[`Added Integration View Test Renders added integration view using dummy
- +
+ + + + +
+ +
+
+

+ Template +

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

- Template -

-
-
- -
- - -
- -
-

- Date Added -

-
-
- -
- - -
- -
- +

+ Date Added +

- + + +
+
- - - - + +
+ +
@@ -436,8 +409,10 @@ exports[`Added Integration View Test Renders added integration view using dummy search={ Object { "box": Object { + "compressed": true, "incremental": true, }, + "compressed": true, "filters": Array [ Object { "field": "assetType", @@ -481,9 +456,11 @@ exports[`Added Integration View Test Renders added integration view using dummy
@@ -591,7 +569,7 @@ exports[`Added Integration View Test Renders added integration view using dummy className="euiFormControlLayoutIcons" >
- +
diff --git a/public/components/integrations/components/__tests__/__snapshots__/added_integration_table.test.tsx.snap b/public/components/integrations/components/__tests__/__snapshots__/added_integration_table.test.tsx.snap index e4286b273..6d9ea2848 100644 --- a/public/components/integrations/components/__tests__/__snapshots__/added_integration_table.test.tsx.snap +++ b/public/components/integrations/components/__tests__/__snapshots__/added_integration_table.test.tsx.snap @@ -170,10 +170,12 @@ exports[`Added Integration Table View Test Renders added integration table view search={ Object { "box": Object { + "compressed": true, "incremental": true, }, "filters": Array [ Object { + "compressed": true, "field": "templateName", "multiSelect": false, "name": "Type", @@ -195,12 +197,14 @@ exports[`Added Integration Table View Test Renders added integration table view
@@ -285,7 +290,7 @@ exports[`Added Integration Table View Test Renders added integration table view className="euiFormControlLayoutIcons" >
@@ -1791,7 +1805,7 @@ exports[`Added Integration Table View Test Renders added integration table view className="euiFormControlLayoutIcons" >
diff --git a/public/components/integrations/components/__tests__/__snapshots__/integration_fields.test.tsx.snap b/public/components/integrations/components/__tests__/__snapshots__/integration_fields.test.tsx.snap index 9617512f0..5c210c6a0 100644 --- a/public/components/integrations/components/__tests__/__snapshots__/integration_fields.test.tsx.snap +++ b/public/components/integrations/components/__tests__/__snapshots__/integration_fields.test.tsx.snap @@ -16,10 +16,10 @@ exports[`Available Integration Table View Test Renders nginx integration table v - -
-
-

+

Set Up Integration -

+
- +
- +
@@ -95,9 +99,11 @@ exports[`Integration Setup Page Renders integration setup page as expected 1`] =
- +
- +
@@ -239,9 +247,11 @@ exports[`Integration Setup Page Renders integration setup page as expected 1`] =
- +
-

+

Set Up Integration -

+ - - + +

Integration Details

- + - +

Integration Connection

- + - +

Query Fields @@ -135,7 +147,9 @@ exports[`Integration Setup Inputs Renders the S3 connector form as expected 1`]

- + - +

Integration Resources @@ -203,7 +219,9 @@ exports[`Integration Setup Inputs Renders the S3 connector form as expected 1`]

- + -

+

Set Up Integration -

+

- - + +

Integration Details

- + - +

Integration Connection

- + - +

Query Fields @@ -388,7 +418,9 @@ exports[`Integration Setup Inputs Renders the S3 connector form without workflow

- + - +

Integration Resources @@ -456,7 +490,9 @@ exports[`Integration Setup Inputs Renders the S3 connector form without workflow

- + -

+

Set Up Integration -

+

- - + +

Integration Details

- + - +

Integration Connection

- + { switch (status) { @@ -127,71 +131,108 @@ export function AddedIntegration(props: AddedIntegrationProps) { function AddedOverview(overviewProps: { data: { data?: IntegrationInstanceResult } }) { const { data } = overviewProps.data; - return ( - - {dataSourceEnabled && ( - - )} - - - - - - - -

{data?.name}

-
-
- - - -
+ const badgeContent = ; + const deleteButton = ( + { + activateDeleteModal(data?.name); + }} + data-test-subj="deleteInstanceButton" + /> + ); + + const headerContent = ( + + + + - { - activateDeleteModal(data?.name); - }} - data-test-subj="deleteInstanceButton" - /> + +

{data?.name}

+
+ {badgeContent}
-
- - - - -

Template

-
- - {data?.templateName} -
- - -

Date Added

-
- - {data?.creationDate?.split('T')[0]} -
-
-
-
+ + {deleteButton} + + + ); + + const additionalInfo = ( + + + +

Template

+
+ {data?.templateName} +
+ + +

Date Added

+
+ {data?.creationDate?.split('T')[0]} +
+
+ ); + + return newNavigation ? ( + <> + } + components={[ + ...(dataSourceEnabled + ? [ + , + ] + : []), + deleteButton, + ]} + /> + {additionalInfo} + + ) : ( + <> + + {dataSourceEnabled && ( + + )} + + {headerContent} + + + {additionalInfo} + ); } @@ -272,6 +313,7 @@ export function AddedIntegration(props: AddedIntegrationProps) { const search = { box: { incremental: true, + compressed: true, }, filters: [ { @@ -286,6 +328,7 @@ export function AddedIntegration(props: AddedIntegrationProps) { })), }, ], + compressed: true, }; const tableColumns = [ @@ -333,11 +376,10 @@ export function AddedIntegration(props: AddedIntegrationProps) { return ( - {AddedOverview({ data: stateData })} - + {AddedAssets({ data: stateData })} - + {isModalVisible && modalLayout} diff --git a/public/components/integrations/components/added_integration_table.tsx b/public/components/integrations/components/added_integration_table.tsx index 47abcbde6..054f8f538 100644 --- a/public/components/integrations/components/added_integration_table.tsx +++ b/public/components/integrations/components/added_integration_table.tsx @@ -149,6 +149,7 @@ export function AddedIntegrationsTable(props: AddedIntegrationsTableProps) { const search = { box: { incremental: true, + compressed: true, }, filters: [ { @@ -177,7 +178,10 @@ export function AddedIntegrationsTable(props: AddedIntegrationsTableProps) { }, ] : []), - ], + ].map((filter) => ({ + ...filter, + compressed: true, + })), }; const entries = props.data.hits.map((integration) => { diff --git a/public/components/integrations/components/integration.tsx b/public/components/integrations/components/integration.tsx index 9ae5f3fc5..f8a299880 100644 --- a/public/components/integrations/components/integration.tsx +++ b/public/components/integrations/components/integration.tsx @@ -63,12 +63,12 @@ export function Integration(props: AvailableIntegrationProps) { href: '#/', }, { - text: integrationTemplateId, + text: integration.displayName || integration.name || '...', href: `#/available/${integrationTemplateId}`, }, ]); handleDataRequest(); - }, [integrationTemplateId]); + }, [integrationTemplateId, integration.name]); async function handleDataRequest() { // TODO fill in ID request here @@ -227,7 +227,6 @@ export function Integration(props: AvailableIntegrationProps) { return ( - { @@ -251,11 +250,10 @@ export function Integration(props: AvailableIntegrationProps) { }} loading={loading} /> - {IntegrationDetails({ integration })} - + {IntegrationScreenshots({ integration, http })} - + {renderTabs()} @@ -263,7 +261,7 @@ export function Integration(props: AvailableIntegrationProps) { {selectedTabId === 'assets' ? IntegrationAssets({ integration, integrationAssets }) : IntegrationFields({ integration, integrationMapping })} - + {modal} diff --git a/public/components/integrations/components/integration_assets_panel.tsx b/public/components/integrations/components/integration_assets_panel.tsx index 39e9a89be..eafd6683d 100644 --- a/public/components/integrations/components/integration_assets_panel.tsx +++ b/public/components/integrations/components/integration_assets_panel.tsx @@ -25,6 +25,7 @@ export function IntegrationAssets(props: { const search = { box: { incremental: true, + compressed: true, }, filters: [ { @@ -39,6 +40,7 @@ export function IntegrationAssets(props: { })), }, ], + compressed: true, }; const tableColumns = [ @@ -86,7 +88,7 @@ export function IntegrationAssets(props: {

Assets

- +

Details

- + diff --git a/public/components/integrations/components/integration_fields_panel.tsx b/public/components/integrations/components/integration_fields_panel.tsx index f1a4522f6..daca71093 100644 --- a/public/components/integrations/components/integration_fields_panel.tsx +++ b/public/components/integrations/components/integration_fields_panel.tsx @@ -21,6 +21,7 @@ export function IntegrationFields(props: any) { const search = { box: { incremental: true, + compressed: true, }, }; @@ -96,7 +97,7 @@ export function IntegrationFields(props: any) {

Fields

- + void }) => { - const [isPopoverOpen, setPopover] = useState(false); - - const closePopover = () => { - setPopover(false); - }; - - const onButtonClick = () => { - setPopover((isOpen) => !isOpen); - }; +const newNavigation = coreRefs.chrome?.navGroup.getNavGroupEnabled(); - const items = [ - { - closePopover(); // If the popover isn't closed, it overlays over the flyout - onShowUpload(); - }} - > - Upload Integrations - , - View Catalog, - ]; - const button = ( - - Catalog - - ); +export const IntegrationHeaderActions = ({ onShowUpload }: { onShowUpload: () => void }) => { return ( - - - + + + + View Catalog + + + + + Upload Integration + + + ); }; @@ -103,24 +83,39 @@ export const IntegrationHeader = () => { return (
- - - -

Integrations

+ {newNavigation ? ( + + View integrations with preconfigured assets immediately within your OpenSearch setup.{' '} + + Learn more + + + } + components={[ setShowUploadFlyout(true)} />]} + /> + ) : ( + <> + + + +

Integrations

+
+
+ + setShowUploadFlyout(true)} /> + +
+ + View integrations with preconfigured assets immediately within your OpenSearch setup.{' '} + + Learn more + -
- - setShowUploadFlyout(true)} /> - -
- - - View integrations with preconfigured assets immediately within your OpenSearch setup.{' '} - - Learn more - - - + + )} + {!newNavigation && } {renderTabs()} diff --git a/public/components/integrations/components/integration_overview_panel.tsx b/public/components/integrations/components/integration_overview_panel.tsx index 47698e7e7..29f7155b6 100644 --- a/public/components/integrations/components/integration_overview_panel.tsx +++ b/public/components/integrations/components/integration_overview_panel.tsx @@ -4,65 +4,76 @@ */ import { - EuiButton, EuiFlexGroup, EuiFlexItem, EuiPageContentHeaderSection, EuiPageHeader, EuiPageHeaderSection, - EuiSpacer, - EuiText, + EuiTitle, + EuiSmallButton, } from '@elastic/eui'; import React from 'react'; +import { HeaderControlledComponentsWrapper } from '../../../../public/plugin_headerControl'; +import { coreRefs } from '../../../framework/core_refs'; -const pageStyles: CSS.Properties = { +const newNavigation = coreRefs.chrome?.navGroup.getNavGroupEnabled(); + +const pageStyles = { width: '100%', - justifyContent: 'spaceBetween', }; export function IntegrationOverview(props: any) { const config = props.integration; - return ( - - + + const buttons = ( + <> + + { + props.setUpSample(); + }} + disabled={props.loading} + data-test-subj="try-it-button" + data-click-metric-element="integrations.create_from_try_it" + > + Try It + + + + { + props.showFlyout(config.name); + }} + fill + disabled={props.loading} + data-test-subj="add-integration-button" + data-click-metric-element="integrations.set_up" + > + Set up integration + + + + ); + + return newNavigation ? ( + + {buttons} + , + ]} + /> + ) : ( + - -

{config.displayName || config.name}

-
-
- - { - props.setUpSample(); - }} - disabled={props.loading} - data-test-subj="try-it-button" - data-click-metric-element="integrations.create_from_try_it" - > - Try It - - - - { - props.showFlyout(config.name); - }} - fill - disabled={props.loading} - data-test-subj="add-integration-button" - data-click-metric-element="integrations.set_up" - > - Set Up - + +

{config.displayName || config.name}

+
+ {buttons}
diff --git a/public/components/integrations/components/integration_screenshots_panel.tsx b/public/components/integrations/components/integration_screenshots_panel.tsx index 93866383f..b346df4ea 100644 --- a/public/components/integrations/components/integration_screenshots_panel.tsx +++ b/public/components/integrations/components/integration_screenshots_panel.tsx @@ -20,7 +20,7 @@ export function IntegrationScreenshots(props: any) {

Screenshots

- + {screenshots?.map((screenshot: { path: string; annotation?: string }) => { return ( diff --git a/public/components/integrations/components/setup_integration_inputs.tsx b/public/components/integrations/components/setup_integration_inputs.tsx index b78472bac..2fd240200 100644 --- a/public/components/integrations/components/setup_integration_inputs.tsx +++ b/public/components/integrations/components/setup_integration_inputs.tsx @@ -423,29 +423,29 @@ export function SetupIntegrationFormInputs(props: IntegrationConfigProps) { return ( -

Set Up Integration

+

Set Up Integration

- + {setupCallout.show ? (

{setupCallout.text}

) : null} - +

Integration Details

- + - +

Integration Connection

- + {config.connectionType === 's3' ? ( <> - +

Query Fields

@@ -471,7 +471,7 @@ export function SetupIntegrationFormInputs(props: IntegrationConfigProps) {

- + {integration.workflows ? ( <> - +

Integration Resources

@@ -492,7 +492,7 @@ export function SetupIntegrationFormInputs(props: IntegrationConfigProps) {

- + spec renders the component 1`] = `
-
-

- Notebooks -

-
+ Notebooks +

spec renders the component 1`] = `
-
-
- - +
- - - + + + +
+
+
+
+
+
+
+ +
@@ -250,7 +285,7 @@ exports[` spec renders the component 1`] = ` > spec renders the component 1`] = ` xmlns="http://www.w3.org/2000/svg" > spec renders the component 1`] = ` > spec renders the component 1`] = ` xmlns="http://www.w3.org/2000/svg" > spec renders the empty component 1`] = `
-
-

- Notebooks -

-
+ Notebooks +
spec renders the empty component 1`] = `
-
@@ -1121,6 +1141,20 @@ exports[` spec renders the empty component 1`] = ` + @@ -1143,7 +1177,7 @@ exports[` spec renders the empty component 1`] = ` - Add samples + Add sample notebooks diff --git a/public/components/notebooks/components/__tests__/__snapshots__/notebook.test.tsx.snap b/public/components/notebooks/components/__tests__/__snapshots__/notebook.test.tsx.snap index 253279eaa..90ebce72d 100644 --- a/public/components/notebooks/components/__tests__/__snapshots__/notebook.test.tsx.snap +++ b/public/components/notebooks/components/__tests__/__snapshots__/notebook.test.tsx.snap @@ -2,90 +2,65 @@ exports[` spec Renders the empty component 1`] = `
-
+
-
-
-
+

+ sample-notebook-1 +

-
+
-
-
-
-
-
-
- -
-
-
-
-
-
-
+
- spec Renders the empty component 1`] = ` d="M5.277 10.088c.02.014.04.03.057.047.582.55 1.134.812 1.666.812.586 0 1.84-.293 3.713-.88L9 6.212V2H7v4.212l-1.723 3.876Zm-.438.987L3.539 14h8.922l-1.32-2.969C9.096 11.677 7.733 12 7 12c-.74 0-1.463-.315-2.161-.925ZM6 2H5V1h6v1h-1v4l3.375 7.594A1 1 0 0 1 12.461 15H3.54a1 1 0 0 1-.914-1.406L6 6V2Z" /> - +
+
+
+
+
+
- Notebook actions - - - + +
+
+
+
-
-

- sample-notebook-1 -

+
+

+ Created on 12/14/2023 06:49 PM +

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

- Created -
- - 12/14/2023 06:49 PM -

+
+
+ +
+
+
+
-
+

+ No paragraphs +

-

- No paragraphs -

-
- Add a paragraph to compose your document or story. Notebooks now support two types of input: -
+ Add a paragraph to compose your document or story. Notebooks now support two types of input:
+
+
+
-
- + + +
+
+ + Code block +
- - Code block - -
-

- Write contents directly using markdown, SQL or PPL. -

-
+

+ Write contents directly using markdown, SQL or PPL. +

-
+
+
- + + +
+
+ + Visualization +
- - Visualization - -
-

- Import OpenSearch Dashboards or Observability visualizations to the notes. -

-
+

+ Import OpenSearch Dashboards or Observability visualizations to the notes. +

-
-
+
@@ -363,102 +457,65 @@ exports[` spec Renders the empty component 1`] = ` exports[` spec Renders the visualization component 1`] = `
-
+
-
-
-
-
-
- -
- -
-
-
-
-

-

-
-
-
-
+ +
+
+
- Upgrade this notebook to take full advantage of the latest features -
+
+
+ > +

+ Created on Invalid date +

+
+
+
-
-

- Created -
- - Invalid date -

-
+ Upgrade this notebook to take full advantage of the latest features +
+
+
+
+
+
+
+
+
+
+
+
-
+

+ No paragraphs +

-

- No paragraphs -

-
- Add a paragraph to compose your document or story. Notebooks now support two types of input: -
+ Add a paragraph to compose your document or story. Notebooks now support two types of input:
-
-
+
+
diff --git a/public/components/notebooks/components/__tests__/note_table.test.tsx b/public/components/notebooks/components/__tests__/note_table.test.tsx index 7fbb42b46..94dd36068 100644 --- a/public/components/notebooks/components/__tests__/note_table.test.tsx +++ b/public/components/notebooks/components/__tests__/note_table.test.tsx @@ -4,7 +4,7 @@ */ import '@testing-library/jest-dom'; -import { act, cleanup, fireEvent, render, waitFor } from '@testing-library/react'; +import { cleanup, fireEvent, render, waitFor } from '@testing-library/react'; import { configure } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import React from 'react'; @@ -62,8 +62,7 @@ describe(' spec', () => { const utils = renderNoteTable({ notebooks }); expect(utils.container.firstChild).toMatchSnapshot(); - fireEvent.click(utils.getByText('Actions')); - fireEvent.click(utils.getByText('Add samples')); + fireEvent.click(utils.getByText('Add sample notebooks')); fireEvent.click(utils.getAllByLabelText('Select this row')[0]); fireEvent.click(utils.getByText('Actions')); fireEvent.click(utils.getByText('Delete')); @@ -155,14 +154,13 @@ describe(' spec', () => { }); it('adds sample notebooks', async () => { - const { getByText, getAllByText, getByTestId } = renderNoteTable({ notebooks: [] }); + const { getAllByText, getByTestId } = renderNoteTable({ notebooks: [] }); - // Open Actions dropdown and click Add samples - fireEvent.click(getByText('Actions')); - fireEvent.click(getAllByText('Add samples')[0]); + // Add samples + fireEvent.click(getAllByText('Add sample notebooks')[0]); // Ensure the modal is open (you may need to adjust based on your modal implementation) - expect(getAllByText('Add sample notebooks')).toHaveLength(1); + expect(getAllByText('Add sample notebooks')).toHaveLength(3); // Mock user confirmation and submit fireEvent.click(getByTestId('confirmModalConfirmButton')); @@ -171,24 +169,6 @@ describe(' spec', () => { expect(props.addSampleNotebooks).toHaveBeenCalledTimes(1); }); - it('closes the action panel', async () => { - const { getByText, queryByTestId } = renderNoteTable({ notebooks: [] }); - expect(queryByTestId('renameNotebookBtn')).not.toBeInTheDocument(); - - // Open Actions dropdown - fireEvent.click(getByText('Actions')); - - // Ensure the action panel is open - expect(queryByTestId('deleteNotebookBtn')).toBeInTheDocument(); - - await act(async () => { - fireEvent.click(getByText('Actions')); - }); - - // Ensure the action panel is closed - expect(queryByTestId('deleteNotebookBtn')).not.toBeInTheDocument(); - }); - it('closes the delete modal', () => { const notebooks = [ { diff --git a/public/components/notebooks/components/__tests__/notebook.test.tsx b/public/components/notebooks/components/__tests__/notebook.test.tsx index 0a4935bb3..f00c67702 100644 --- a/public/components/notebooks/components/__tests__/notebook.test.tsx +++ b/public/components/notebooks/components/__tests__/notebook.test.tsx @@ -313,10 +313,6 @@ describe(' spec', () => { expect(utils.getByText('sample-notebook-1')).toBeInTheDocument(); }); - act(() => { - fireEvent.click(utils.getByText('Paragraph actions')); - }); - act(() => { fireEvent.click(utils.getByText('Clear all outputs')); }); @@ -337,10 +333,6 @@ describe(' spec', () => { expect(utils.queryByText('hello')).toBeNull(); }); - act(() => { - fireEvent.click(utils.getByText('Paragraph actions')); - }); - act(() => { fireEvent.click(utils.getByText('Delete all paragraphs')); }); @@ -399,11 +391,7 @@ describe(' spec', () => { }); act(() => { - fireEvent.click(utils.getByText('Notebook actions')); - }); - - act(() => { - fireEvent.click(utils.getByText('Rename notebook')); + fireEvent.click(utils.getByTestId('notebook-edit-icon')); }); await waitFor(() => { @@ -459,11 +447,7 @@ describe(' spec', () => { }); act(() => { - fireEvent.click(utils.getByText('Notebook actions')); - }); - - act(() => { - fireEvent.click(utils.getByText('Duplicate notebook')); + fireEvent.click(utils.getByTestId('notebook-duplicate-icon')); }); await waitFor(() => { @@ -514,11 +498,7 @@ describe(' spec', () => { }); act(() => { - fireEvent.click(utils.getByText('Notebook actions')); - }); - - act(() => { - fireEvent.click(utils.getByText('Delete notebook')); + fireEvent.click(utils.getByTestId('notebook-delete-icon')); }); await waitFor(() => { @@ -670,7 +650,7 @@ describe(' spec', () => { }); act(() => { - fireEvent.click(utils.getByText('Delete this notebook')); + fireEvent.click(utils.getByTestId('notebook-delete-icon')); }); await waitFor(() => { diff --git a/public/components/notebooks/components/note_table.tsx b/public/components/notebooks/components/note_table.tsx index 8dc7cf0a6..bb2b64b89 100644 --- a/public/components/notebooks/components/note_table.tsx +++ b/public/components/notebooks/components/note_table.tsx @@ -44,6 +44,10 @@ import { } from './helpers/modal_containers'; import { NotebookType } from './main'; import { setNavBreadCrumbs } from '../../../../common/utils/set_nav_bread_crumbs'; +import { HeaderControlledComponentsWrapper } from '../../../../public/plugin_headerControl'; +import { coreRefs } from '../../../framework/core_refs'; + +const newNavigation = coreRefs.chrome?.navGroup.getNavGroupEnabled(); interface NoteTableProps { loading: boolean; @@ -185,16 +189,6 @@ export function NoteTable({ > Delete , - { - setIsActionsPopoverOpen(false); - addSampleNotebooksModal(); - }} - data-test-subj="add-samples-btn" - > - Add samples - , ]; const tableColumns = [ @@ -232,34 +226,112 @@ export function NoteTable({ <> - - - -

Notebooks

-
-
-
- - - - -

- Notebooks ({notebooks.length}) -

+ {!newNavigation && ( + + + +

Notebooks

- - - Use Notebooks to interactively and collaboratively develop rich reports backed by - live data. Common use cases for notebooks includes creating postmortem reports, - designing run books, building live infrastructure reports, or even documentation.{' '} - - Learn more - - -
- - + + + )} + + {newNavigation ? ( + 0 ? notebooks.length : '0'} + description={ + <> + Use Notebooks to interactively and collaboratively develop rich reports backed + by live data. Common use cases for notebooks include creating postmortem + reports, designing run books, building live infrastructure reports, or even + documentation.{' '} + + Learn more + + + } + components={[ + + + addSampleNotebooksModal()} + > + Add sample notebooks + + + + + Create notebook + + + , + ]} + /> + ) : ( + + + +

+ Notebooks ({notebooks.length}) +

+
+ + + Use Notebooks to interactively and collaboratively develop rich reports backed + by live data. Common use cases for notebooks include creating postmortem + reports, designing run books, building live infrastructure reports, or even + documentation.{' '} + + Learn more + + +
+ + + + addSampleNotebooksModal()} + > + Add sample notebooks + + + + + Create notebook + + + + +
+ )} + {notebooks.length > 0 ? ( + <> + + setSearchQuery(e.target.value)} + /> + + - - - Create notebook - - -
-
- - {notebooks.length > 0 ? ( - <> - setSearchQuery(e.target.value)} - /> Create notebook @@ -345,7 +403,7 @@ export function NoteTable({ fullWidth={false} onClick={() => addSampleNotebooksModal()} > - Add samples + Add sample notebooks diff --git a/public/components/notebooks/components/notebook.tsx b/public/components/notebooks/components/notebook.tsx index d7d82fabb..3505e4f2b 100644 --- a/public/components/notebooks/components/notebook.tsx +++ b/public/components/notebooks/components/notebook.tsx @@ -21,6 +21,8 @@ import { EuiPopover, EuiSpacer, EuiText, + EuiButtonIcon, + EuiTitle, } from '@elastic/eui'; import CSS from 'csstype'; import moment from 'moment'; @@ -50,17 +52,13 @@ import { generateInContextReport, } from './helpers/reporting_context_menu_helper'; import { Paragraphs } from './paragraph_components/paragraphs'; -const panelStyles: CSS.Properties = { - float: 'left', - width: '100%', - maxWidth: '1130px', - marginTop: '20px', -}; +import { HeaderControlledComponentsWrapper } from '../../../../public/plugin_headerControl'; +import { coreRefs } from '../../../framework/core_refs'; -const pageStyles: CSS.Properties = { - float: 'left', - width: '100%', - maxWidth: '1500px', +const newNavigation = coreRefs.chrome?.navGroup.getNavGroupEnabled(); + +const panelStyles: CSS.Properties = { + marginTop: '10px', }; /* @@ -784,13 +782,6 @@ export class Notebook extends Component { } render() { - const createdText = ( -
-

- Created
{moment(this.state.dateCreated).format(UI_DATE_FORMAT)} -

-
- ); const viewOptions: EuiButtonGroupOptionProps[] = [ { id: 'view_both', @@ -829,48 +820,69 @@ export class Notebook extends Component { ], }, ]; + + const renderParaActionButtons = () => { + const { parsedPara, selectedViewId } = this.state; + + return ( + + + { + this.setState({ isParaActionsPopoverOpen: false }); + this.showDeleteAllParaModal(); + }} + isDisabled={parsedPara.length === 0} + > + Delete all paragraphs + + + + + { + this.setState({ isParaActionsPopoverOpen: false }); + this.showClearOutputsModal(); + }} + isDisabled={parsedPara.length === 0} + > + Clear all outputs + + + + + { + this.setState({ isParaActionsPopoverOpen: false }); + this.runForAllParagraphs((para: ParaType, _index: number) => { + return para.paraRef.current?.runParagraph(); + }); + if (selectedViewId === 'input_only') { + this.updateView('view_both'); + } + }} + isDisabled={parsedPara.length === 0} + > + Run all paragraphs + + + + ); + }; + const paraActionsPanels: EuiContextMenuPanelDescriptor[] = [ { id: 0, - title: 'Actions', + title: 'Add paragraph', items: [ { - name: 'Add paragraph to top', + name: 'To top', panel: 1, }, { - name: 'Add paragraph to bottom', + name: 'To bottom', panel: 2, }, - { - name: 'Run all paragraphs', - disabled: this.state.parsedPara.length === 0, - onClick: () => { - this.setState({ isParaActionsPopoverOpen: false }); - this.runForAllParagraphs((para: ParaType, _index: number) => { - return para.paraRef.current?.runParagraph(); - }); - if (this.state.selectedViewId === 'input_only') { - this.updateView('view_both'); - } - }, - }, - { - name: 'Clear all outputs', - disabled: this.state.parsedPara.length === 0, - onClick: () => { - this.setState({ isParaActionsPopoverOpen: false }); - this.showClearOutputsModal(); - }, - }, - { - name: 'Delete all paragraphs', - disabled: this.state.parsedPara.length === 0, - onClick: () => { - this.setState({ isParaActionsPopoverOpen: false }); - this.showDeleteAllParaModal(); - }, - }, ], }, { @@ -914,35 +926,6 @@ export class Notebook extends Component { ], }, ]; - const noteActionsPanels: EuiContextMenuPanelDescriptor[] = [ - { - id: 0, - title: 'Notebook actions', - items: [ - { - name: 'Rename notebook', - onClick: () => { - this.setState({ isNoteActionsPopoverOpen: false }); - this.showRenameModal(); - }, - }, - { - name: 'Duplicate notebook', - onClick: () => { - this.setState({ isNoteActionsPopoverOpen: false }); - this.showCloneModal(); - }, - }, - { - name: 'Delete notebook', - onClick: () => { - this.setState({ isNoteActionsPopoverOpen: false }); - this.showDeleteNotebookModal(); - }, - }, - ], - }, - ]; const reportingActionPanels: EuiContextMenuPanelDescriptor[] = [ { @@ -1000,7 +983,7 @@ export class Notebook extends Component { }) } > - Reporting actions + Reporting } isOpen={this.state.isReportingActionsPopoverOpen} @@ -1015,16 +998,125 @@ export class Notebook extends Component { ) : null; + const noteActionIcons = ( + + {this.state.savedObjectNotebook ? ( + <> + + + + + + + + + + + ) : ( + <> + + + + + )} + + ); + + const reportingTopButton = !this.state.savedObjectNotebook ? ( + + this.showUpgradeModal()} + > + Upgrade Notebook + + + ) : null; + + const notebookHeader = newNavigation ? ( + {showReportingContextMenu}, + {reportingTopButton}, + ]} + /> + ) : ( +
+ + +

{this.state.path}

+
+ + + {noteActionIcons} + {showReportingContextMenu} + {reportingTopButton} + + +
+ + +

{`Created on ${moment(this.state.dateCreated).format(UI_DATE_FORMAT)}`}

+
+
+ +
+ ); + return ( -
+ <> - - - {this.state.parsedPara.length > 0 && ( + {notebookHeader} + {!this.state.savedObjectNotebook && ( + + + Upgrade this notebook to take full advantage of the latest features + + this.showUpgradeModal()} + > + Upgrade Notebook + + + + )} + {!this.state.savedObjectNotebook && } + + {this.state.parsedPara.length > 0 ? ( { @@ -1033,106 +1125,39 @@ export class Notebook extends Component { legend="notebook view buttons" /> - )} - - - {showReportingContextMenu} - {this.state.savedObjectNotebook && ( - - - this.setState({ - isParaActionsPopoverOpen: !this.state.isParaActionsPopoverOpen, - }) - } - > - Paragraph actions - - } - isOpen={this.state.isParaActionsPopoverOpen} - closePopover={() => this.setState({ isParaActionsPopoverOpen: false })} - > - - - - )} - {this.state.savedObjectNotebook ? ( - - - this.setState({ - isNoteActionsPopoverOpen: !this.state.isNoteActionsPopoverOpen, - }) - } - > - Notebook actions - - } - isOpen={this.state.isNoteActionsPopoverOpen} - closePopover={() => this.setState({ isNoteActionsPopoverOpen: false })} - > - - - ) : ( - <> - - this.showUpgradeModal()} - > - Upgrade Notebook - - - - this.showDeleteNotebookModal()} - > - Delete this notebook - - - + )} - - - -

{this.state.path}

-
- - {!this.state.savedObjectNotebook && ( - - - - Upgrade this notebook to take full advantage of the latest features - - this.showUpgradeModal()} - > - Upgrade Notebook - - - - - - )} - - - {createdText} + + {this.state.savedObjectNotebook && renderParaActionButtons()} + {this.state.savedObjectNotebook && ( + + + this.setState({ + isParaActionsPopoverOpen: !this.state.isParaActionsPopoverOpen, + }) + } + > + Add paragraph + + } + isOpen={this.state.isParaActionsPopoverOpen} + closePopover={() => this.setState({ isParaActionsPopoverOpen: false })} + > + + + + )} + {this.state.parsedPara.length > 0 ? ( @@ -1260,7 +1285,7 @@ export class Notebook extends Component {
{this.state.isModalVisible && this.state.modalLayout} -
+ ); } } diff --git a/public/components/notebooks/components/paragraph_components/__tests__/__snapshots__/para_input.test.tsx.snap b/public/components/notebooks/components/paragraph_components/__tests__/__snapshots__/para_input.test.tsx.snap index baf6b4813..c76d09983 100644 --- a/public/components/notebooks/components/paragraph_components/__tests__/__snapshots__/para_input.test.tsx.snap +++ b/public/components/notebooks/components/paragraph_components/__tests__/__snapshots__/para_input.test.tsx.snap @@ -66,140 +66,149 @@ exports[` spec renders the visualization component 1`] = ` class="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsFlexEnd euiFlexGroup--directionRow euiFlexGroup--responsive" >
- -
-
-
-
-
- + + + Browse + + + +
+
spec renders the visualization component 1`] = ` class="euiFlexItem" >
spec renders the visualization component 1`] = ` class="euiDatePickerRange euiDatePickerRange--inGroup" >
-
`; diff --git a/public/components/notebooks/components/paragraph_components/para_input.tsx b/public/components/notebooks/components/paragraph_components/para_input.tsx index f53cf06a3..f2646d04a 100644 --- a/public/components/notebooks/components/paragraph_components/para_input.tsx +++ b/public/components/notebooks/components/paragraph_components/para_input.tsx @@ -133,40 +133,45 @@ export const ParaInput = (props: { return ( <> - - - { - if (newOption.length > 0) props.setVisType(newOption[0].className); - props.setSelectedVisOption(newOption); - props.setIsOutputStale(true); - }} - /> - + + + + + { + if (newOption.length > 0) props.setVisType(newOption[0].className); + props.setSelectedVisOption(newOption); + props.setIsOutputStale(true); + }} + /> + + + + { + setSelectableOptions([ + ...props.visOptions[0].options, + ...props.visOptions[1].options, + ]); + setSelectableError(false); + setIsModalOpen(true); + }} + > + Browse + + + + - { - setSelectableOptions([ - ...props.visOptions[0].options, - ...props.visOptions[1].options, - ]); - setSelectableError(false); - setIsModalOpen(true); - }} - > - Browse - - - - - - {isModalOpen && ( setIsModalOpen(false)} style={{ width: 500 }}> diff --git a/public/components/notebooks/index.scss b/public/components/notebooks/index.scss index c14e07d80..760d17d58 100644 --- a/public/components/notebooks/index.scss +++ b/public/components/notebooks/index.scss @@ -11,10 +11,6 @@ height: 14em; } -#notebookArea { - max-width: 88vw; -} - .panel-header-count { color: #687078; font-weight: normal; diff --git a/public/components/trace_analytics/components/dashboard/mode_picker.tsx b/public/components/trace_analytics/components/dashboard/mode_picker.tsx index e593310f6..85c65d602 100644 --- a/public/components/trace_analytics/components/dashboard/mode_picker.tsx +++ b/public/components/trace_analytics/components/dashboard/mode_picker.tsx @@ -13,14 +13,14 @@ const labels = new Map([ ]); export function DataSourcePicker(props: { - modes: { + modes: Array<{ id: string; title: string; - }[]; + }>; selectedMode: TraceAnalyticsMode; setMode: (mode: TraceAnalyticsMode) => void; }) { - const { modes, selectedMode, setMode } = props; + const { modes = [], selectedMode, setMode } = props; const [isPopoverOpen, setPopoverIsOpen] = useState(false); const trigger = { @@ -70,10 +70,10 @@ export function DataSourcePicker(props: { key: x.id, value: x.id, checked: x.id === selectedMode ? 'on' : undefined, - "data-test-subj": x.id + '-mode', + 'data-test-subj': x.id + '-mode', }))} onChange={(choices) => { - const choice = choices.find(({ checked }) => checked) as unknown as { + const choice = (choices.find(({ checked }) => checked) as unknown) as { value: string; label: string; key: TraceAnalyticsMode; diff --git a/public/plugin.tsx b/public/plugin.tsx index d8b23f20d..6f68e175f 100644 --- a/public/plugin.tsx +++ b/public/plugin.tsx @@ -33,8 +33,8 @@ import { observabilityApplicationsPluginOrder, observabilityApplicationsTitle, observabilityGettingStartedID, - observabilityGettingStartedPluginOrder, observabilityGettingStartedTitle, + observabilityGettingStartedPluginOrder, observabilityIntegrationsID, observabilityIntegrationsPluginOrder, observabilityIntegrationsTitle, diff --git a/public/plugin_headerControl.tsx b/public/plugin_headerControl.tsx index 369dc843c..b7281ae93 100644 --- a/public/plugin_headerControl.tsx +++ b/public/plugin_headerControl.tsx @@ -10,7 +10,7 @@ import { coreRefs } from './framework/core_refs'; interface HeaderControlledComponentsWrapperProps { components?: React.ReactElement[]; badgeContent?: React.ReactElement | string | number; - description?: string; + description?: React.ReactNode; } export const HeaderControlledComponentsWrapper = ({ @@ -21,7 +21,7 @@ export const HeaderControlledComponentsWrapper = ({ const HeaderControl = coreRefs.navigation?.ui.HeaderControl; const showActionsInHeader = coreRefs.chrome?.navGroup.getNavGroupEnabled(); - const formattedBadgeContent = badgeContent ? `(${badgeContent})` : ''; + const isBadgeReactElement = React.isValidElement(badgeContent); if (showActionsInHeader && HeaderControl) { return ( @@ -32,7 +32,11 @@ export const HeaderControlledComponentsWrapper = ({ controls={[ { key: 'header-badge-control-left', - renderComponent: {formattedBadgeContent}, + renderComponent: isBadgeReactElement ? ( + {badgeContent} + ) : ( + {`(${badgeContent})`} + ), // Render based on type }, ]} />