From 49e82fabbfd2f14b992c5ccea6026f3ced6348e1 Mon Sep 17 00:00:00 2001 From: Ivan Medina Date: Mon, 1 Dec 2025 12:27:36 +0100 Subject: [PATCH 01/11] COMPASS-8348: improve quert to language discoverability --- .../aggregations/aggregations.spec.tsx | 1 - .../pipeline-toolbar/index.spec.tsx | 18 +---- .../src/components/pipeline-toolbar/index.tsx | 3 - .../pipeline-header/index.spec.tsx | 1 - .../pipeline-header/index.tsx | 3 - .../pipeline-header/pipeline-actions.spec.tsx | 77 ------------------- .../pipeline-header/pipeline-actions.tsx | 27 +------ .../pipeline-settings/index.spec.tsx | 8 +- .../pipeline-settings/index.tsx | 69 ++++++++++++++--- .../src/components/pipeline/pipeline.tsx | 6 +- .../compass-aggregations/src/plugin.spec.tsx | 4 +- packages/compass-aggregations/src/plugin.tsx | 2 - .../src/components/crud-toolbar.spec.tsx | 2 + .../src/components/crud-toolbar.tsx | 54 +++++++++---- .../src/components/document-list.tsx | 3 + .../compass-crud/src/stores/crud-store.ts | 17 ++++ .../src/components/query-bar.spec.tsx | 48 ------------ .../src/components/query-bar.tsx | 21 ----- 18 files changed, 131 insertions(+), 233 deletions(-) diff --git a/packages/compass-aggregations/src/components/aggregations/aggregations.spec.tsx b/packages/compass-aggregations/src/components/aggregations/aggregations.spec.tsx index e770ecd0885..ce792c32aa8 100644 --- a/packages/compass-aggregations/src/components/aggregations/aggregations.spec.tsx +++ b/packages/compass-aggregations/src/components/aggregations/aggregations.spec.tsx @@ -9,7 +9,6 @@ describe('Aggregations [Component]', function () { beforeEach(async function () { await renderWithStore( , + , { pipeline: [{ $match: { _id: 1 } }] }, undefined, { @@ -106,10 +101,6 @@ describe('PipelineToolbar', function () { within(settings).getByTestId('pipeline-toolbar-create-new-button'), 'shows create-new button' ).to.exist; - expect( - within(settings).getByTestId('pipeline-toolbar-export-button'), - 'shows export to language button' - ).to.exist; expect( within(settings).getByTestId('pipeline-toolbar-preview-toggle'), @@ -197,12 +188,7 @@ describe('PipelineToolbar', function () { assignExperiment={mockAssignExperiment} getAssignment={mockGetAssignment} > - + , { pipeline: [] }, // Initial state undefined, // Connection info diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/index.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/index.tsx index edd26f02345..b65563cba51 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/index.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/index.tsx @@ -53,7 +53,6 @@ export type PipelineToolbarProps = { isAggregationGeneratedFromQuery?: boolean; isBuilderView: boolean; showRunButton: boolean; - showExportButton: boolean; showExplainButton: boolean; onHideAIInputClick?: () => void; }; @@ -61,7 +60,6 @@ export type PipelineToolbarProps = { export const PipelineToolbar: React.FunctionComponent = ({ isBuilderView, showRunButton, - showExportButton, showExplainButton, }) => { const darkMode = useDarkMode(); @@ -91,7 +89,6 @@ export const PipelineToolbar: React.FunctionComponent = ({ isOptionsVisible={isOptionsVisible} onToggleOptions={() => setIsOptionsVisible(!isOptionsVisible)} showRunButton={showRunButton} - showExportButton={showExportButton} showExplainButton={showExplainButton} /> {isOptionsVisible && ( diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/index.spec.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/index.spec.tsx index 50aeeba6463..4e6be26a6ec 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/index.spec.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/index.spec.tsx @@ -17,7 +17,6 @@ describe('PipelineHeader', function () { isOpenPipelineVisible isOptionsVisible showRunButton - showExportButton showExplainButton onToggleOptions={onToggleOptionsSpy} />, diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/index.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/index.tsx index edff962481a..ac64dbeda83 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/index.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/index.tsx @@ -57,7 +57,6 @@ const savedAggregationsPopoverStyles = css({ type PipelineHeaderProps = { isOptionsVisible: boolean; showRunButton: boolean; - showExportButton: boolean; showExplainButton: boolean; onToggleOptions: () => void; isOpenPipelineVisible: boolean; @@ -104,7 +103,6 @@ const SavedPipelinesButton: React.FunctionComponent = () => { export const PipelineHeader: React.FunctionComponent = ({ showRunButton, - showExportButton, showExplainButton, onToggleOptions, isOptionsVisible, @@ -126,7 +124,6 @@ export const PipelineHeader: React.FunctionComponent = ({ onToggleOptions={onToggleOptions} isOptionsVisible={isOptionsVisible} showRunButton={showRunButton} - showExportButton={showExportButton} showExplainButton={showExplainButton} /> diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.spec.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.spec.tsx index af50c90ea49..a1b0fac4db6 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.spec.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.spec.tsx @@ -26,13 +26,11 @@ describe('PipelineActions', function () { describe('options visible', function () { let onRunAggregationSpy: SinonSpy; let onToggleOptionsSpy: SinonSpy; - let onExportAggregationResultsSpy: SinonSpy; let onExplainAggregationSpy: SinonSpy; beforeEach(function () { onRunAggregationSpy = spy(); onToggleOptionsSpy = spy(); - onExportAggregationResultsSpy = spy(); onExplainAggregationSpy = spy(); render( @@ -40,11 +38,9 @@ describe('PipelineActions', function () { isOptionsVisible={true} showAIEntry={false} showRunButton={true} - showExportButton={true} showExplainButton={true} onRunAggregation={onRunAggregationSpy} onToggleOptions={onToggleOptionsSpy} - onExportAggregationResults={onExportAggregationResultsSpy} isExplainButtonDisabled={false} onExplainAggregation={onExplainAggregationSpy} onUpdateView={() => {}} @@ -64,17 +60,6 @@ describe('PipelineActions', function () { expect(onRunAggregationSpy.calledOnce).to.be.true; }); - it('calls onExportAggregationResults on click', function () { - const button = screen.getByTestId( - 'pipeline-toolbar-export-aggregation-button' - ); - expect(button).to.exist; - - userEvent.click(button); - - expect(onExportAggregationResultsSpy.calledOnce).to.be.true; - }); - it('calls onExplainAggregation on click', function () { const button = screen.getByTestId( 'pipeline-toolbar-explain-aggregation-button' @@ -107,11 +92,9 @@ describe('PipelineActions', function () { isOptionsVisible={false} showAIEntry={false} showRunButton={true} - showExportButton={true} showExplainButton={true} onRunAggregation={onRunAggregationSpy} onToggleOptions={onToggleOptionsSpy} - onExportAggregationResults={() => {}} onUpdateView={() => {}} onExplainAggregation={() => {}} onCollectionScanInsightActionButtonClick={() => {}} @@ -151,11 +134,9 @@ describe('PipelineActions', function () { isOptionsVisible={false} showAIEntry={false} showRunButton={true} - showExportButton={true} showExplainButton={true} onRunAggregation={onRunAggregationSpy} onToggleOptions={onToggleOptionsSpy} - onExportAggregationResults={() => {}} onUpdateView={() => {}} onExplainAggregation={() => {}} onCollectionScanInsightActionButtonClick={() => {}} @@ -174,26 +155,21 @@ describe('PipelineActions', function () { describe('disables actions when pipeline is invalid', function () { let onRunAggregationSpy: SinonSpy; - let onExportAggregationResultsSpy: SinonSpy; let onExplainAggregationSpy: SinonSpy; beforeEach(function () { onRunAggregationSpy = spy(); - onExportAggregationResultsSpy = spy(); onExplainAggregationSpy = spy(); render( {}} - onExportAggregationResults={onExportAggregationResultsSpy} onExplainAggregation={onExplainAggregationSpy} onUpdateView={() => {}} onCollectionScanInsightActionButtonClick={() => {}} @@ -213,18 +189,6 @@ describe('PipelineActions', function () { expect(onRunAggregationSpy.calledOnce).to.be.false; }); - it('export action disabled', function () { - const button = screen.getByTestId( - 'pipeline-toolbar-export-aggregation-button' - ); - expect(button.getAttribute('aria-disabled')).to.equal('true'); - - userEvent.click(button, undefined, { - skipPointerEventsCheck: true, - }); - expect(onExportAggregationResultsSpy.calledOnce).to.be.false; - }); - it('explain action disabled', function () { const button = screen.getByTestId( 'pipeline-toolbar-explain-aggregation-button' @@ -243,7 +207,6 @@ describe('PipelineActions', function () { const result = await renderWithStore( {}} >, @@ -264,12 +227,6 @@ describe('PipelineActions', function () { .getAttribute('aria-disabled') ).to.equal('true'); - expect( - screen - .getByTestId('pipeline-toolbar-export-aggregation-button') - .getAttribute('aria-disabled') - ).to.equal('true'); - expect( screen .getByTestId('pipeline-toolbar-run-button') @@ -294,12 +251,6 @@ describe('PipelineActions', function () { .getAttribute('aria-disabled') ).to.equal('true'); - expect( - screen - .getByTestId('pipeline-toolbar-export-aggregation-button') - .getAttribute('aria-disabled') - ).to.equal('true'); - expect( screen .getByTestId('pipeline-toolbar-run-button') @@ -307,33 +258,5 @@ describe('PipelineActions', function () { ).to.equal('true'); }); }); - - it('should disable export button when pipeline is $out / $merge', async function () { - await renderPipelineActions({ - pipeline: [{ $out: 'foo' }], - }); - - expect( - screen - .getByTestId('pipeline-toolbar-export-aggregation-button') - .getAttribute('aria-disabled') - ).to.equal('true'); - }); - - it('should disable export button when last enabled stage is $out / $merge', async function () { - const { store } = await renderPipelineActions({ - pipeline: [{ $out: 'foo' }, { $match: { _id: 1 } }], - }); - - store.dispatch(changeStageDisabled(1, true)); - - await waitFor(() => { - expect( - screen - .getByTestId('pipeline-toolbar-export-aggregation-button') - .getAttribute('aria-disabled') - ).to.equal('true'); - }); - }); }); }); diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.tsx index 556b20daa6b..077148be3dc 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.tsx @@ -10,10 +10,7 @@ import { } from '@mongodb-js/compass-components'; import { AIExperienceEntry } from '@mongodb-js/compass-generative-ai'; import type { RootState } from '../../../modules'; -import { - exportAggregationResults, - runAggregation, -} from '../../../modules/aggregation'; +import { runAggregation } from '../../../modules/aggregation'; import { updateView } from '../../../modules/update-view'; import { explainAggregation } from '../../../modules/explain'; import { @@ -40,10 +37,6 @@ type PipelineActionsProps = { isRunButtonDisabled?: boolean; onRunAggregation: () => void; - showExportButton?: boolean; - isExportButtonDisabled?: boolean; - onExportAggregationResults: () => void; - showUpdateViewButton?: boolean; isUpdateViewButtonDisabled?: boolean; onUpdateView: () => void; @@ -68,8 +61,6 @@ export const PipelineActions: React.FunctionComponent = ({ isOptionsVisible, showRunButton, isRunButtonDisabled, - showExportButton, - isExportButtonDisabled, showUpdateViewButton, isUpdateViewButtonDisabled, isExplainButtonDisabled, @@ -79,7 +70,6 @@ export const PipelineActions: React.FunctionComponent = ({ onUpdateView, onRunAggregation, onToggleOptions, - onExportAggregationResults, onExplainAggregation, showCollectionScanInsight, onCollectionScanInsightActionButtonClick, @@ -152,18 +142,6 @@ export const PipelineActions: React.FunctionComponent = ({ Explain )} - {!showUpdateViewButton && showExportButton && ( - - )} {!showUpdateViewButton && showRunButton && ( )} + {isExportDataEnabled && ( + + data-testid="pipeline-toolbar-export-data-button" + actions={exportDataActions} + onAction={onExportData} + buttonText="Export Data" + buttonProps={{ + className: exportDataButtonStyles, + size: 'xsmall', + leftGlyph: , + }} + narrowBreakpoint="900px" + /> + )} {editViewName && ( @@ -100,10 +141,20 @@ export default connect( return { editViewName: state.editViewName ?? undefined, isExportToLanguageEnabled: !hasSyntaxErrors, + isExportDataEnabled: !hasSyntaxErrors, }; }, { onExportToLanguage: exportToLanguage, + onExportData: (action: ExportDataOption) => { + return (dispatch: PipelineBuilderThunkDispatch) => { + if (action === 'export-full-collection') { + dispatch(exportAggregationResults()); + } else { + dispatch(exportAggregationResults()); + } + }; + }, onCreateNewPipeline: confirmNewPipeline, } )(PipelineSettings); diff --git a/packages/compass-aggregations/src/components/pipeline/pipeline.tsx b/packages/compass-aggregations/src/components/pipeline/pipeline.tsx index 748408e056a..df3902420d7 100644 --- a/packages/compass-aggregations/src/components/pipeline/pipeline.tsx +++ b/packages/compass-aggregations/src/components/pipeline/pipeline.tsx @@ -49,10 +49,7 @@ export type PipelineProps = Pick< | 'savingPipelineCancel' | 'clonePipeline' > & - Pick< - PipelineToolbarProps, - 'showRunButton' | 'showExportButton' | 'showExplainButton' - > & + Pick & Pick< SettingsProps, | 'toggleSettingsIsExpanded' @@ -108,7 +105,6 @@ class Pipeline extends PureComponent< return ( ); diff --git a/packages/compass-aggregations/src/plugin.spec.tsx b/packages/compass-aggregations/src/plugin.spec.tsx index 23cfe762682..0ec341cea37 100644 --- a/packages/compass-aggregations/src/plugin.spec.tsx +++ b/packages/compass-aggregations/src/plugin.spec.tsx @@ -5,13 +5,11 @@ import { renderWithStore } from '../test/configure-store'; import { AggregationsPlugin } from './plugin'; describe('Aggregations [Plugin]', function () { - it('should render plugin with toolbar and export button', async function () { + it('should render plugin with toolbar', async function () { const metadata = {} as any; await renderWithStore( ); expect(screen.getByTestId('pipeline-toolbar')).to.exist; - expect(screen.getByTestId('pipeline-toolbar-export-aggregation-button')).to - .exist; }); }); diff --git a/packages/compass-aggregations/src/plugin.tsx b/packages/compass-aggregations/src/plugin.tsx index 1b185aeebc0..ce24ba6d5b4 100644 --- a/packages/compass-aggregations/src/plugin.tsx +++ b/packages/compass-aggregations/src/plugin.tsx @@ -7,7 +7,6 @@ import type { ConfigureStoreOptions } from './stores/store'; export const AggregationsPlugin: React.FunctionComponent< ConfigureStoreOptions > = () => { - const showExportButton = usePreference('enableImportExport'); const showRunButton = usePreference('enableAggregationBuilderRunPipeline'); const showExplainButton = usePreference('enableExplainPlan'); const enableSearchActivationProgramP1 = usePreference( @@ -17,7 +16,6 @@ export const AggregationsPlugin: React.FunctionComponent< return ( void; onCollapseAllClicked: () => void; openExportFileDialog: (exportFullCollection?: boolean) => void; + onOpenExportToLanguage: () => void; outdated: boolean; page: number; readonly: boolean; @@ -184,6 +194,7 @@ const CrudToolbar: React.FunctionComponent = ({ onExpandAllClicked, onCollapseAllClicked, openExportFileDialog, + onOpenExportToLanguage, outdated, page, readonly, @@ -359,22 +370,6 @@ const CrudToolbar: React.FunctionComponent = ({ instanceDescription={instanceDescription} /> )} - {isImportExportEnabled && ( - - data-testid="crud-export-collection" - actions={exportDataActions} - onAction={(action: ExportDataOption) => - openExportFileDialog(action === 'export-full-collection') - } - buttonText="Export Data" - buttonProps={{ - className: exportCollectionButtonStyles, - size: 'xsmall', - leftGlyph: , - }} - narrowBreakpoint={DOCUMENT_NARROW_ICON_BREAKPOINT} - /> - )} {!readonly && ( = ({ onClick={onDeleteButtonClicked} > )} + {isImportExportEnabled && ( + + data-testid="crud-export-collection" + actions={exportDataActions} + onAction={(action: ExportDataOption) => + openExportFileDialog(action === 'export-full-collection') + } + buttonText="Export Data" + buttonProps={{ + className: exportCollectionButtonStyles, + size: 'xsmall', + leftGlyph: , + }} + narrowBreakpoint={DOCUMENT_NARROW_ICON_BREAKPOINT} + /> + )} + {insights && }
diff --git a/packages/compass-crud/src/components/document-list.tsx b/packages/compass-crud/src/components/document-list.tsx index 6d847994e64..28fc4d55423 100644 --- a/packages/compass-crud/src/components/document-list.tsx +++ b/packages/compass-crud/src/components/document-list.tsx @@ -570,6 +570,9 @@ const DocumentList: React.FunctionComponent = (props) => { onExpandAllClicked={onExpandAllClicked} onCollapseAllClicked={onCollapseAllClicked} openExportFileDialog={openExportFileDialog} + onOpenExportToLanguage={store.openQueryExportToLanguageDialog.bind( + store + )} outdated={outdated} readonly={!isEditable} viewSwitchHandler={handleViewChanged} diff --git a/packages/compass-crud/src/stores/crud-store.ts b/packages/compass-crud/src/stores/crud-store.ts index 4cbb909b0be..c07c2c313ec 100644 --- a/packages/compass-crud/src/stores/crud-store.ts +++ b/packages/compass-crud/src/stores/crud-store.ts @@ -98,6 +98,7 @@ export type CrudActions = { runBulkUpdate(): Promise; closeBulkDeleteDialog(): void; runBulkDelete(): Promise; + openQueryExportToLanguageDialog(): void; openDeleteQueryExportToLanguageDialog(): void; saveUpdateQuery(name: string): Promise; }; @@ -1983,6 +1984,22 @@ class CrudStoreImpl } } + openQueryExportToLanguageDialog(): void { + const query = this.queryBar.getLastAppliedQuery('crud'); + this.localAppRegistry.emit( + 'open-query-export-to-language', + { + filter: toJSString(query.filter) || '{}', + project: query.project ? toJSString(query.project) : undefined, + sort: query.sort ? toJSString(query.sort) : undefined, + collation: query.collation ? toJSString(query.collation) : undefined, + skip: query.skip ? String(query.skip) : undefined, + limit: query.limit ? String(query.limit) : undefined, + }, + 'Query' + ); + } + openDeleteQueryExportToLanguageDialog(): void { const { filter = {} } = this.queryBar.getLastAppliedQuery('crud'); this.localAppRegistry.emit( diff --git a/packages/compass-query-bar/src/components/query-bar.spec.tsx b/packages/compass-query-bar/src/components/query-bar.spec.tsx index 340b236a1cd..d27cf6b9147 100644 --- a/packages/compass-query-bar/src/components/query-bar.spec.tsx +++ b/packages/compass-query-bar/src/components/query-bar.spec.tsx @@ -36,7 +36,6 @@ const noop = () => { /* no op */ }; -const exportToLanguageButtonId = 'query-bar-open-export-to-language-button'; const queryHistoryButtonId = 'query-history-button'; const queryHistoryComponentTestId = 'query-history'; @@ -87,7 +86,6 @@ describe('QueryBar Component', function () { onApply={noop} onReset={noop} resultId="123" - showExportToLanguageButton {...props} /> @@ -120,7 +118,6 @@ describe('QueryBar Component', function () { renderQueryBar({ onApply: onApplySpy, onReset: onResetSpy, - showExportToLanguageButton: true, }); }); @@ -207,26 +204,11 @@ describe('QueryBar Component', function () { }); it('query controls are enabled', function () { - expect( - screen - .getByTestId('query-bar-open-export-to-language-button') - .getAttribute('aria-disabled') - ).to.equal('false'); expect( screen .getByTestId('query-bar-apply-filter-button') .getAttribute('aria-disabled') ).to.equal('false'); - expect( - screen - .getByTestId('query-bar-open-export-to-language-button') - .getAttribute('aria-disabled') - ).to.equal('false'); - expect( - screen - .getByTestId('query-bar-open-export-to-language-button') - .getAttribute('aria-disabled') - ).to.equal('false'); }); it('editors are not disabled', function () { @@ -253,26 +235,11 @@ describe('QueryBar Component', function () { }); rerender(); - expect( - screen - .getByTestId('query-bar-open-export-to-language-button') - .getAttribute('aria-disabled') - ).to.equal('true'); expect( screen .getByTestId('query-bar-apply-filter-button') .getAttribute('aria-disabled') ).to.equal('true'); - expect( - screen - .getByTestId('query-bar-open-export-to-language-button') - .getAttribute('aria-disabled') - ).to.equal('true'); - expect( - screen - .getByTestId('query-bar-open-export-to-language-button') - .getAttribute('aria-disabled') - ).to.equal('true'); }); it('editors are disabled', function () { @@ -368,21 +335,6 @@ describe('QueryBar Component', function () { }); }); - describe('when showExportToLanguageButton is false', function () { - beforeEach(function () { - renderQueryBar({ - showExportToLanguageButton: false, - }); - }); - - it('does not render the exportToLanguage button', function () { - const exportToLanguageButton = screen.queryByTestId( - exportToLanguageButtonId - ); - expect(exportToLanguageButton).to.not.exist; - }); - }); - describe('with three query options', function () { beforeEach(function () { renderQueryBar({ diff --git a/packages/compass-query-bar/src/components/query-bar.tsx b/packages/compass-query-bar/src/components/query-bar.tsx index 585d1af6bca..b6c5e201447 100644 --- a/packages/compass-query-bar/src/components/query-bar.tsx +++ b/packages/compass-query-bar/src/components/query-bar.tsx @@ -1,7 +1,6 @@ import React, { useCallback, useMemo } from 'react'; import { Button, - Icon, OptionsToggle, css, cx, @@ -118,11 +117,6 @@ type QueryBarProps = { applyId: number; filterHasContent: boolean; showExplainButton?: boolean; - /** - * Used by Cloud only to hide the export to language functionality - * as it isn't supported. - */ - showExportToLanguageButton?: boolean; valid: boolean; expanded: boolean; placeholders?: Record; @@ -137,7 +131,6 @@ export const QueryBar: React.FunctionComponent = ({ buttonLabel = 'Apply', onApply, onReset, - onOpenExportToLanguage, // Used to specify which query options to show and where they are positioned. queryOptionsLayout = [ 'project', @@ -150,7 +143,6 @@ export const QueryBar: React.FunctionComponent = ({ applyId, filterHasContent, showExplainButton = false, - showExportToLanguageButton = true, valid: isQueryValid, expanded: isQueryOptionsExpanded, placeholders, @@ -281,19 +273,6 @@ export const QueryBar: React.FunctionComponent = ({ > {buttonLabel} - {showExportToLanguageButton && ( - - )} {queryOptionsLayout && queryOptionsLayout.length > 0 && (
Date: Mon, 1 Dec 2025 12:50:03 +0100 Subject: [PATCH 02/11] fix: remove duplicated option in aggregations tab --- .../pipeline-toolbar/pipeline-settings/index.tsx | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx index 9c926236021..a783a340164 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx @@ -52,10 +52,9 @@ const exportCodeButtonTextStyles = css({ }, }); -type ExportDataOption = 'export-query' | 'export-full-collection'; +type ExportDataOption = 'export-query'; const exportDataActions: MenuAction[] = [ - { action: 'export-query', label: 'Export query results' }, - { action: 'export-full-collection', label: 'Export the full collection' }, + { action: 'export-query', label: 'Export pipeline results' }, ]; type PipelineSettingsProps = { @@ -146,13 +145,9 @@ export default connect( }, { onExportToLanguage: exportToLanguage, - onExportData: (action: ExportDataOption) => { + onExportData: () => { return (dispatch: PipelineBuilderThunkDispatch) => { - if (action === 'export-full-collection') { - dispatch(exportAggregationResults()); - } else { - dispatch(exportAggregationResults()); - } + dispatch(exportAggregationResults()); }; }, onCreateNewPipeline: confirmNewPipeline, From 12d0680284ac6c2bfde3031aec89d463ca8a1f18 Mon Sep 17 00:00:00 2001 From: Ivan Medina Date: Mon, 1 Dec 2025 15:11:47 +0100 Subject: [PATCH 03/11] fix: selector name for e2e test --- packages/compass-e2e-tests/helpers/selectors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compass-e2e-tests/helpers/selectors.ts b/packages/compass-e2e-tests/helpers/selectors.ts index 878243c1a25..04139269ce0 100644 --- a/packages/compass-e2e-tests/helpers/selectors.ts +++ b/packages/compass-e2e-tests/helpers/selectors.ts @@ -1232,7 +1232,7 @@ export const queryBarResetFilterButton = (tabName: string): string => { }; export const queryBarExportToLanguageButton = (tabName: string): string => { const tabSelector = collectionContent(tabName); - return `${tabSelector} [data-testid="query-bar-open-export-to-language-button"]`; + return `${tabSelector} [data-testid="crud-export-to-language-button"]`; }; export const GenAIEntryButton = '[data-testid="open-ai-query-entry-button"]'; export const GenAITextInput = '[data-testid="ai-user-text-input"]'; From 7749190117fa1227edc2b3297cf8ffebf8c0890c Mon Sep 17 00:00:00 2001 From: Ivan Medina Date: Mon, 1 Dec 2025 15:14:57 +0100 Subject: [PATCH 04/11] fix: selector name for e2e test --- packages/compass-e2e-tests/helpers/selectors.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/compass-e2e-tests/helpers/selectors.ts b/packages/compass-e2e-tests/helpers/selectors.ts index 04139269ce0..f8cdded0908 100644 --- a/packages/compass-e2e-tests/helpers/selectors.ts +++ b/packages/compass-e2e-tests/helpers/selectors.ts @@ -843,7 +843,7 @@ export const AggregationSettingsApplyButton = '[data-testid="aggregation-settings-apply"]'; export const AddStageButton = '[data-testid="add-stage"]'; export const ExportAggregationToLanguage = - '[data-testid="pipeline-toolbar-export-button"]'; + '[data-testid="pipeline-toolbar-export-code-button"]'; export const CreateNewPipelineButton = '[data-testid="pipeline-toolbar-create-new-button"]'; export const NewPipelineActions = '#new-pipeline-actions'; @@ -863,7 +863,7 @@ export const AggregationErrorDetailsBtn = export const RunPipelineButton = `[data-testid="pipeline-toolbar-run-button"]`; export const EditPipelineButton = `[data-testid="pipeline-toolbar-edit-button"]`; export const GoToCollectionButton = `[data-testid="pipeline-results-go-to-collection"]`; -export const ExportAggregationResultsButton = `[data-testid="pipeline-toolbar-export-aggregation-button"]`; +export const ExportAggregationResultsButton = `[data-testid="pipeline-toolbar-export-data-button"]`; export const AggregationOpenSavedPipelinesButton = `[data-testid="pipeline-toolbar-open-pipelines-button"]`; export const AggregationSavedPipelinesPopover = `[data-testid="saved-pipelines"]`; From 35586be7017d805680a032e9a075bcb6b49ed363 Mon Sep 17 00:00:00 2001 From: Ivan Medina Date: Mon, 1 Dec 2025 16:20:12 +0100 Subject: [PATCH 05/11] fix: condition to render export data --- .../components/pipeline-toolbar/pipeline-settings/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx index a783a340164..32eebb8651e 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx @@ -77,6 +77,7 @@ export const PipelineSettings: React.FunctionComponent< onCreateNewPipeline, }) => { const enableSavedAggregationsQueries = usePreference('enableMyQueries'); + const isImportExportEnabled = usePreference('enableImportExport'); const isPipelineNameDisplayed = !editViewName && !!enableSavedAggregationsQueries; @@ -98,7 +99,7 @@ export const PipelineSettings: React.FunctionComponent< Create new )} - {isExportDataEnabled && ( + {isImportExportEnabled && isExportDataEnabled && ( data-testid="pipeline-toolbar-export-data-button" actions={exportDataActions} From b342715ad35e83af0d2a6c0db0157d8dcc3c362a Mon Sep 17 00:00:00 2001 From: Ivan Medina Date: Mon, 1 Dec 2025 17:27:22 +0100 Subject: [PATCH 06/11] fix: e2e test for export data --- .../pipeline-toolbar/pipeline-settings/index.tsx | 3 +-- .../tests/collection-aggregations-tab.test.ts | 7 +++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx index 32eebb8651e..a783a340164 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx @@ -77,7 +77,6 @@ export const PipelineSettings: React.FunctionComponent< onCreateNewPipeline, }) => { const enableSavedAggregationsQueries = usePreference('enableMyQueries'); - const isImportExportEnabled = usePreference('enableImportExport'); const isPipelineNameDisplayed = !editViewName && !!enableSavedAggregationsQueries; @@ -99,7 +98,7 @@ export const PipelineSettings: React.FunctionComponent< Create new )} - {isImportExportEnabled && isExportDataEnabled && ( + {isExportDataEnabled && ( data-testid="pipeline-toolbar-export-data-button" actions={exportDataActions} diff --git a/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts b/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts index fa9c346f181..96cc5ebe1b0 100644 --- a/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts +++ b/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts @@ -979,6 +979,13 @@ describe('Collection aggregations tab', function () { '{ i: 5 }' ); + // Wait for the pipeline to be validated before trying to export + await browser.waitUntil(async function () { + const textElement = browser.$(Selectors.stagePreviewToolbarTooltip(0)); + const text = await textElement.getText(); + return text === '(Sample of 1 document)'; + }); + // Open the modal. await browser.clickVisible(Selectors.ExportAggregationResultsButton); const exportModal = browser.$(Selectors.ExportModal); From ef1af2ae4ad9e317c71d54debb0b81a2079d31f9 Mon Sep 17 00:00:00 2001 From: Ivan Medina Date: Mon, 1 Dec 2025 18:17:37 +0100 Subject: [PATCH 07/11] fix: e2e test selector --- packages/compass-e2e-tests/helpers/selectors.ts | 2 +- .../tests/collection-aggregations-tab.test.ts | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/compass-e2e-tests/helpers/selectors.ts b/packages/compass-e2e-tests/helpers/selectors.ts index f8cdded0908..148424d3a42 100644 --- a/packages/compass-e2e-tests/helpers/selectors.ts +++ b/packages/compass-e2e-tests/helpers/selectors.ts @@ -863,7 +863,7 @@ export const AggregationErrorDetailsBtn = export const RunPipelineButton = `[data-testid="pipeline-toolbar-run-button"]`; export const EditPipelineButton = `[data-testid="pipeline-toolbar-edit-button"]`; export const GoToCollectionButton = `[data-testid="pipeline-results-go-to-collection"]`; -export const ExportAggregationResultsButton = `[data-testid="pipeline-toolbar-export-data-button"]`; +export const ExportAggregationResultsButton = `[data-testid="pipeline-toolbar-export-data-button-show-actions"]`; export const AggregationOpenSavedPipelinesButton = `[data-testid="pipeline-toolbar-open-pipelines-button"]`; export const AggregationSavedPipelinesPopover = `[data-testid="saved-pipelines"]`; diff --git a/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts b/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts index 96cc5ebe1b0..250fb175779 100644 --- a/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts +++ b/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts @@ -980,11 +980,16 @@ describe('Collection aggregations tab', function () { ); // Wait for the pipeline to be validated before trying to export - await browser.waitUntil(async function () { - const textElement = browser.$(Selectors.stagePreviewToolbarTooltip(0)); - const text = await textElement.getText(); - return text === '(Sample of 1 document)'; - }); + await browser.waitUntil( + async function () { + const textElement = browser.$(Selectors.stagePreviewToolbarTooltip(0)); + const text = await textElement.getText(); + return text === '(Sample of 1 document)'; + }, + { + timeoutMsg: 'Expected stage preview to show "(Sample of 1 document)"', + } + ); // Open the modal. await browser.clickVisible(Selectors.ExportAggregationResultsButton); From 2a3867d5f6b5f9406e8a9a0bf4d150ab2a38ebc3 Mon Sep 17 00:00:00 2001 From: Ivan Medina Date: Mon, 1 Dec 2025 19:50:49 +0100 Subject: [PATCH 08/11] fix: e2e test for export data modal --- .../components/pipeline-toolbar/pipeline-settings/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx index a783a340164..1d80bbbfc53 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx @@ -145,7 +145,8 @@ export default connect( }, { onExportToLanguage: exportToLanguage, - onExportData: () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onExportData: (_action: ExportDataOption) => { return (dispatch: PipelineBuilderThunkDispatch) => { dispatch(exportAggregationResults()); }; From 60f0051db44214097b5ed8a3bc3f83307c528e97 Mon Sep 17 00:00:00 2001 From: Ivan Medina Date: Mon, 1 Dec 2025 20:56:59 +0100 Subject: [PATCH 09/11] fix: e2e test for export data button --- .../tests/collection-aggregations-tab.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts b/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts index 250fb175779..a76182237e9 100644 --- a/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts +++ b/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts @@ -993,6 +993,10 @@ describe('Collection aggregations tab', function () { // Open the modal. await browser.clickVisible(Selectors.ExportAggregationResultsButton); + // Click the "Export pipeline results" menu item + await browser.clickVisible( + '[data-testid="pipeline-toolbar-export-data-button-export-query-action"]' + ); const exportModal = browser.$(Selectors.ExportModal); await exportModal.waitForDisplayed(); From 4bff1b9b6ffca6df6d2ab0c4fbbf01597ceca769 Mon Sep 17 00:00:00 2001 From: Ivan Medina Date: Tue, 2 Dec 2025 10:24:16 +0100 Subject: [PATCH 10/11] fix: export data button tests --- .../pipeline-header/pipeline-actions.spec.tsx | 1 - .../pipeline-header/pipeline-actions.tsx | 3 - .../pipeline-settings/index.spec.tsx | 202 +++++++++++++++--- .../pipeline-settings/index.tsx | 18 +- 4 files changed, 182 insertions(+), 42 deletions(-) diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.spec.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.spec.tsx index a1b0fac4db6..fdc1a9d23a2 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.spec.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.spec.tsx @@ -12,7 +12,6 @@ import { spy } from 'sinon'; import type { SinonSpy } from 'sinon'; import ConnectedPipelineActions, { PipelineActions } from './pipeline-actions'; import { renderWithStore } from '../../../../test/configure-store'; -import { changeStageDisabled } from '../../../modules/pipeline-builder/stage-editor'; import { type PreferencesAccess, createSandboxFromDefaultPreferences, diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.tsx index 077148be3dc..7923f91966d 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.tsx @@ -17,7 +17,6 @@ import { getIsPipelineInvalidFromBuilderState, getPipelineStageOperatorsFromBuilderState, } from '../../../modules/pipeline-builder/builder-helpers'; -import { isOutputStage } from '../../../utils/stage'; import { openCreateIndexModal } from '../../../modules/insights'; import { useIsAIFeatureEnabled, @@ -169,8 +168,6 @@ export const PipelineActions: React.FunctionComponent = ({ const mapState = (state: RootState) => { const resultPipeline = getPipelineStageOperatorsFromBuilderState(state); - const lastStage = resultPipeline[resultPipeline.length - 1]; - const isMergeOrOutPipeline = isOutputStage(lastStage); const hasSyntaxErrors = getIsPipelineInvalidFromBuilderState(state, false); const isBuilderView = state.workspace === 'builder'; const isAIFetching = state.pipelineBuilder.aiPipeline.status === 'fetching'; diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.spec.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.spec.tsx index a5924464215..ee68b35192c 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.spec.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.spec.tsx @@ -11,48 +11,182 @@ import type { SinonSpy } from 'sinon'; import { renderWithStore } from '../../../../test/configure-store'; import { PipelineSettings } from '.'; +import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; +import { PreferencesProvider } from 'compass-preferences-model/provider'; describe('PipelineSettings', function () { - let container: HTMLElement; - let onExportToLanguageSpy: SinonSpy; - let onExportDataSpy: SinonSpy; - let onCreateNewPipelineSpy: SinonSpy; - beforeEach(async function () { - onExportToLanguageSpy = spy(); - onExportDataSpy = spy(); - onCreateNewPipelineSpy = spy(); - await renderWithStore( - - ); - container = screen.getByTestId('pipeline-settings'); + describe('basic functionality', function () { + let container: HTMLElement; + let onExportToLanguageSpy: SinonSpy; + let onExportDataSpy: SinonSpy; + let onCreateNewPipelineSpy: SinonSpy; + beforeEach(async function () { + onExportToLanguageSpy = spy(); + onExportDataSpy = spy(); + onCreateNewPipelineSpy = spy(); + await renderWithStore( + + ); + container = screen.getByTestId('pipeline-settings'); + }); + + afterEach(cleanup); + + it('calls onCreateNewPipeline callback when create new button is clicked', function () { + const button = within(container).getByTestId( + 'pipeline-toolbar-create-new-button' + ); + expect(button).to.exist; + expect(onCreateNewPipelineSpy.calledOnce).to.be.false; + userEvent.click(button); + expect(onCreateNewPipelineSpy.calledOnce).to.be.true; + }); + + it('calls onExportToLanguage callback when export code button is clicked', function () { + const button = within(container).getByTestId( + 'pipeline-toolbar-export-code-button' + ); + expect(button).to.exist; + + userEvent.click(button); + + expect(onExportToLanguageSpy.calledOnce).to.be.true; + }); }); - afterEach(cleanup); + describe('export data button visibility', function () { + afterEach(cleanup); + + it('hides export data button when enableImportExport preference is disabled', async function () { + const preferences = await createSandboxFromDefaultPreferences(); + await preferences.savePreferences({ + enableImportExport: false, + }); + + await renderWithStore( + + {}} + onExportData={() => {}} + onCreateNewPipeline={() => {}} + /> + + ); + + expect( + screen.queryByTestId('pipeline-toolbar-export-data-button-show-actions') + ).to.not.exist; + }); + + it('shows export data button when enableImportExport preference is enabled', async function () { + const preferences = await createSandboxFromDefaultPreferences(); + await preferences.savePreferences({ + enableImportExport: true, + }); - it('calls onCreateNewPipeline callback when create new button is clicked', function () { - const button = within(container).getByTestId( - 'pipeline-toolbar-create-new-button' - ); - expect(button).to.exist; - expect(onCreateNewPipelineSpy.calledOnce).to.be.false; - userEvent.click(button); - expect(onCreateNewPipelineSpy.calledOnce).to.be.true; + await renderWithStore( + + {}} + onExportData={() => {}} + onCreateNewPipeline={() => {}} + /> + + ); + + expect( + screen.getByTestId('pipeline-toolbar-export-data-button-show-actions') + ).to.exist; + }); }); - it('calls onExportToLanguage callback when export code button is clicked', function () { - const button = within(container).getByTestId( - 'pipeline-toolbar-export-code-button' - ); - expect(button).to.exist; + describe('export data button disabled state', function () { + afterEach(cleanup); + + it('should disable export data button when isExportDataEnabled is false', async function () { + const preferences = await createSandboxFromDefaultPreferences(); + await preferences.savePreferences({ + enableImportExport: true, + }); + + await renderWithStore( + + {}} + onExportData={() => {}} + onCreateNewPipeline={() => {}} + /> + + ); + + // Button should not be rendered when isExportDataEnabled is false + expect( + screen.queryByTestId('pipeline-toolbar-export-data-button-show-actions') + ).to.not.exist; + }); + + it('should disable export code button when isExportToLanguageEnabled is false', async function () { + const preferences = await createSandboxFromDefaultPreferences(); + await preferences.savePreferences({ + enableImportExport: true, + }); + + await renderWithStore( + + {}} + onExportData={() => {}} + onCreateNewPipeline={() => {}} + /> + + ); + + const button = screen.getByTestId('pipeline-toolbar-export-code-button'); + expect(button.getAttribute('aria-disabled')).to.equal('true'); + }); + + it('should disable both export buttons when both are disabled', async function () { + const preferences = await createSandboxFromDefaultPreferences(); + await preferences.savePreferences({ + enableImportExport: true, + }); + + await renderWithStore( + + {}} + onExportData={() => {}} + onCreateNewPipeline={() => {}} + /> + + ); - userEvent.click(button); + // Export data button should not be rendered + expect( + screen.queryByTestId('pipeline-toolbar-export-data-button-show-actions') + ).to.not.exist; - expect(onExportToLanguageSpy.calledOnce).to.be.true; + // Export code button should be disabled + const codeButton = screen.getByTestId( + 'pipeline-toolbar-export-code-button' + ); + expect(codeButton.getAttribute('aria-disabled')).to.equal('true'); + }); }); }); diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx index 1d80bbbfc53..207119a0c16 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx @@ -14,7 +14,11 @@ import { SaveMenu } from './pipeline-menus'; import PipelineName from './pipeline-name'; import PipelineExtraSettings from './pipeline-extra-settings'; import type { RootState, PipelineBuilderThunkDispatch } from '../../../modules'; -import { getIsPipelineInvalidFromBuilderState } from '../../../modules/pipeline-builder/builder-helpers'; +import { + getIsPipelineInvalidFromBuilderState, + getPipelineStageOperatorsFromBuilderState, +} from '../../../modules/pipeline-builder/builder-helpers'; +import { isOutputStage } from '../../../utils/stage'; import { confirmNewPipeline } from '../../../modules/is-new-pipeline-confirm'; import ModifySourceBanner from '../../modify-source-banner'; import type { MenuAction } from '@mongodb-js/compass-components'; @@ -77,6 +81,7 @@ export const PipelineSettings: React.FunctionComponent< onCreateNewPipeline, }) => { const enableSavedAggregationsQueries = usePreference('enableMyQueries'); + const enableImportExport = usePreference('enableImportExport'); const isPipelineNameDisplayed = !editViewName && !!enableSavedAggregationsQueries; @@ -98,7 +103,7 @@ export const PipelineSettings: React.FunctionComponent< Create new )} - {isExportDataEnabled && ( + {enableImportExport && isExportDataEnabled && ( data-testid="pipeline-toolbar-export-data-button" actions={exportDataActions} @@ -136,11 +141,16 @@ export const PipelineSettings: React.FunctionComponent< export default connect( (state: RootState) => { + const resultPipeline = getPipelineStageOperatorsFromBuilderState(state); + const lastStage = resultPipeline[resultPipeline.length - 1]; + const isMergeOrOutPipeline = isOutputStage(lastStage); const hasSyntaxErrors = getIsPipelineInvalidFromBuilderState(state, false); + const isAIFetching = state.pipelineBuilder.aiPipeline.status === 'fetching'; return { editViewName: state.editViewName ?? undefined, - isExportToLanguageEnabled: !hasSyntaxErrors, - isExportDataEnabled: !hasSyntaxErrors, + isExportToLanguageEnabled: !hasSyntaxErrors && !isAIFetching, + isExportDataEnabled: + !isMergeOrOutPipeline && !hasSyntaxErrors && !isAIFetching, }; }, { From 93480b70df409d8f57cb3f950e975ba4314cc73d Mon Sep 17 00:00:00 2001 From: Ivan Medina Date: Tue, 9 Dec 2025 10:37:28 -0300 Subject: [PATCH 11/11] fix: make export data a button instead of dropdown --- .../pipeline-settings/index.spec.tsx | 33 +++++++++++-------- .../pipeline-settings/index.tsx | 32 +++++++----------- .../compass-e2e-tests/helpers/selectors.ts | 2 +- .../tests/collection-aggregations-tab.test.ts | 6 +--- 4 files changed, 33 insertions(+), 40 deletions(-) diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.spec.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.spec.tsx index ee68b35192c..c24518515f9 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.spec.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.spec.tsx @@ -58,6 +58,17 @@ describe('PipelineSettings', function () { expect(onExportToLanguageSpy.calledOnce).to.be.true; }); + + it('calls onExportData callback when export data button is clicked', function () { + const button = within(container).getByTestId( + 'pipeline-toolbar-export-data-button' + ); + expect(button).to.exist; + + userEvent.click(button); + + expect(onExportDataSpy.calledOnce).to.be.true; + }); }); describe('export data button visibility', function () { @@ -81,9 +92,8 @@ describe('PipelineSettings', function () { ); - expect( - screen.queryByTestId('pipeline-toolbar-export-data-button-show-actions') - ).to.not.exist; + expect(screen.queryByTestId('pipeline-toolbar-export-data-button')).to.not + .exist; }); it('shows export data button when enableImportExport preference is enabled', async function () { @@ -104,16 +114,15 @@ describe('PipelineSettings', function () { ); - expect( - screen.getByTestId('pipeline-toolbar-export-data-button-show-actions') - ).to.exist; + expect(screen.getByTestId('pipeline-toolbar-export-data-button')).to + .exist; }); }); describe('export data button disabled state', function () { afterEach(cleanup); - it('should disable export data button when isExportDataEnabled is false', async function () { + it('should hide export data button when isExportDataEnabled is false', async function () { const preferences = await createSandboxFromDefaultPreferences(); await preferences.savePreferences({ enableImportExport: true, @@ -132,9 +141,8 @@ describe('PipelineSettings', function () { ); // Button should not be rendered when isExportDataEnabled is false - expect( - screen.queryByTestId('pipeline-toolbar-export-data-button-show-actions') - ).to.not.exist; + expect(screen.queryByTestId('pipeline-toolbar-export-data-button')).to.not + .exist; }); it('should disable export code button when isExportToLanguageEnabled is false', async function () { @@ -178,9 +186,8 @@ describe('PipelineSettings', function () { ); // Export data button should not be rendered - expect( - screen.queryByTestId('pipeline-toolbar-export-data-button-show-actions') - ).to.not.exist; + expect(screen.queryByTestId('pipeline-toolbar-export-data-button')).to.not + .exist; // Export code button should be disabled const codeButton = screen.getByTestId( diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx index 207119a0c16..86c5fddc34a 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx @@ -5,7 +5,6 @@ import { Icon, css, spacing, - DropdownMenuButton, WorkspaceContainer, } from '@mongodb-js/compass-components'; import { exportToLanguage } from '../../../modules/export-to-language'; @@ -21,7 +20,6 @@ import { import { isOutputStage } from '../../../utils/stage'; import { confirmNewPipeline } from '../../../modules/is-new-pipeline-confirm'; import ModifySourceBanner from '../../modify-source-banner'; -import type { MenuAction } from '@mongodb-js/compass-components'; import { usePreference } from 'compass-preferences-model/provider'; @@ -56,17 +54,12 @@ const exportCodeButtonTextStyles = css({ }, }); -type ExportDataOption = 'export-query'; -const exportDataActions: MenuAction[] = [ - { action: 'export-query', label: 'Export pipeline results' }, -]; - type PipelineSettingsProps = { editViewName?: string; isExportToLanguageEnabled?: boolean; isExportDataEnabled?: boolean; onExportToLanguage: () => void; - onExportData: (action: ExportDataOption) => void; + onExportData: () => void; onCreateNewPipeline: () => void; }; @@ -104,18 +97,16 @@ export const PipelineSettings: React.FunctionComponent< )} {enableImportExport && isExportDataEnabled && ( - + )}