Skip to content

Commit cfff98c

Browse files
Merge remote-tracking branch 'origin/main' into beta-releases
2 parents 300e878 + f93ded3 commit cfff98c

File tree

14 files changed

+178
-50
lines changed

14 files changed

+178
-50
lines changed

THIRD-PARTY-NOTICES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
The following third-party software is used by and included in **Mongodb Compass**.
2-
This document was automatically generated on Wed Nov 29 2023.
2+
This document was automatically generated on Sun Dec 03 2023.
33

44
## List of dependencies
55

packages/compass-aggregations/src/components/modify-source-banner/modify-source-banner.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,25 @@ import PropTypes from 'prop-types';
33
import { Badge, BadgeVariant, css } from '@mongodb-js/compass-components';
44

55
const modifySourceBannerStyles = css({
6-
textAlign: 'center',
7-
margin: '5px auto',
8-
marginTop: '20px',
9-
zIndex: 500,
6+
display: 'inline-block',
7+
minWidth: 0,
8+
overflow: 'hidden',
9+
textOverflow: 'ellipsis',
1010
});
1111

1212
/**
1313
* The blue banner displayed when modifying a source pipeline.
1414
*/
15-
const ModifySourceBanner = (props: { editViewName: React.ReactNode }) => {
15+
const ModifySourceBanner = (props: { editViewName: string }) => {
16+
const bannerText = `Modifying pipeline backing "${props.editViewName}"`;
1617
return (
1718
<Badge
1819
className={modifySourceBannerStyles}
1920
variant={BadgeVariant.Blue}
2021
data-testid="modify-source-banner"
22+
title={bannerText}
2123
>
22-
Modifying pipeline backing &quot;{props.editViewName}&quot;
24+
{bannerText}
2325
</Badge>
2426
);
2527
};

packages/compass-aggregations/src/components/pipeline-builder-workspace/pipeline-builder-ui-workspace/index.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
addStage,
1111
moveStage,
1212
} from '../../../modules/pipeline-builder/stage-editor';
13-
import ModifySourceBanner from '../../modify-source-banner';
1413
import AggregationSidePanel from '../../aggregation-side-panel';
1514
import { addWizard } from '../../../modules/pipeline-builder/stage-editor';
1615
import PipelineBuilderInputDocuments from '../../pipeline-builder-input-documents';
@@ -37,7 +36,6 @@ const pipelineWorkspaceStyles = css({
3736

3837
export type PipelineBuilderUIWorkspaceProps = {
3938
stagesIdAndType: StageIdAndType[];
40-
editViewName?: string | null;
4139
isSidePanelOpen: boolean;
4240
onStageMoveEnd: (from: number, to: number) => void;
4341
onStageAddAfterEnd: (after?: number) => void;
@@ -52,7 +50,6 @@ export const PipelineBuilderUIWorkspace: React.FunctionComponent<
5250
PipelineBuilderUIWorkspaceProps
5351
> = ({
5452
stagesIdAndType,
55-
editViewName,
5653
isSidePanelOpen,
5754
onStageMoveEnd,
5855
onStageAddAfterEnd,
@@ -69,7 +66,6 @@ export const PipelineBuilderUIWorkspace: React.FunctionComponent<
6966
className={pipelineWorkspaceContainerStyles}
7067
>
7168
<div className={pipelineWorkspaceStyles}>
72-
{editViewName && <ModifySourceBanner editViewName={editViewName} />}
7369
<PipelineBuilderInputDocuments />
7470
<SortableList
7571
stagesIdAndType={stagesIdAndType}
@@ -106,7 +102,6 @@ export const PipelineBuilderUIWorkspace: React.FunctionComponent<
106102
const mapState = (state: RootState) => {
107103
return {
108104
stagesIdAndType: state.pipelineBuilder.stageEditor.stagesIdAndType,
109-
editViewName: state.editViewName,
110105
isSidePanelOpen: state.sidePanel.isPanelOpen,
111106
};
112107
};

packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,30 @@ import { getIsPipelineInvalidFromBuilderState } from '../../../modules/pipeline-
1010
import { confirmNewPipeline } from '../../../modules/is-new-pipeline-confirm';
1111
import { usePreference } from 'compass-preferences-model';
1212
import { hiddenOnNarrowPipelineToolbarStyles } from '../pipeline-toolbar-container';
13+
import ModifySourceBanner from '../../modify-source-banner';
1314

1415
const containerStyles = css({
15-
display: 'grid',
16+
display: 'flex',
1617
gap: spacing[2],
17-
gridTemplateAreas: '"settings extraSettings"',
18-
gridTemplateColumns: '1fr auto',
1918
alignItems: 'center',
19+
justifyContent: 'space-between',
2020
whiteSpace: 'nowrap',
2121
});
2222

2323
const settingsStyles = css({
2424
display: 'flex',
2525
gap: spacing[2],
2626
alignItems: 'center',
27+
flex: 'none',
2728
});
2829

2930
const extraSettingsStyles = css({
30-
gridArea: 'extraSettings',
3131
display: 'flex',
32+
flex: 'none',
3233
});
3334

3435
type PipelineSettingsProps = {
35-
isEditingViewPipeline?: boolean;
36+
editViewName?: string;
3637
isExportToLanguageEnabled?: boolean;
3738
onExportToLanguage: () => void;
3839
onCreateNewPipeline: () => void;
@@ -41,7 +42,7 @@ type PipelineSettingsProps = {
4142
export const PipelineSettings: React.FunctionComponent<
4243
PipelineSettingsProps
4344
> = ({
44-
isEditingViewPipeline = false,
45+
editViewName,
4546
isExportToLanguageEnabled,
4647
onExportToLanguage,
4748
onCreateNewPipeline,
@@ -51,8 +52,8 @@ export const PipelineSettings: React.FunctionComponent<
5152
React
5253
);
5354
const isSavePipelineDisplayed =
54-
!isEditingViewPipeline && enableSavedAggregationsQueries;
55-
const isCreatePipelineDisplayed = !isEditingViewPipeline;
55+
!editViewName && enableSavedAggregationsQueries;
56+
const isCreatePipelineDisplayed = !editViewName;
5657

5758
return (
5859
<div className={containerStyles} data-testid="pipeline-settings">
@@ -87,6 +88,9 @@ export const PipelineSettings: React.FunctionComponent<
8788
</span>
8889
</Button>
8990
</div>
91+
{editViewName && (
92+
<ModifySourceBanner editViewName={editViewName}></ModifySourceBanner>
93+
)}
9094
<div className={extraSettingsStyles}>
9195
<PipelineExtraSettings />
9296
</div>
@@ -98,7 +102,7 @@ export default connect(
98102
(state: RootState) => {
99103
const hasSyntaxErrors = getIsPipelineInvalidFromBuilderState(state, false);
100104
return {
101-
isEditingViewPipeline: !!state.editViewName,
105+
editViewName: state.editViewName ?? undefined,
102106
isExportToLanguageEnabled: !hasSyntaxErrors,
103107
};
104108
},

packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/pipeline-extra-settings.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ import type { PipelineMode } from '../../../modules/pipeline-builder/pipeline-mo
1919
import { getIsPipelineInvalidFromBuilderState } from '../../../modules/pipeline-builder/builder-helpers';
2020
import { toggleSidePanel } from '../../../modules/side-panel';
2121
import { usePreference } from 'compass-preferences-model';
22-
import { hiddenOnNarrowPipelineToolbarStyles } from '../pipeline-toolbar-container';
22+
import {
23+
hiddenOnNarrowPipelineToolbarStyles,
24+
smallPipelineToolbar,
25+
} from '../pipeline-toolbar-container';
2326

2427
const containerStyles = css({
2528
display: 'flex',
@@ -39,6 +42,17 @@ const toggleLabelStyles = css({
3942
textTransform: 'uppercase',
4043
});
4144

45+
const segmentControlStyles = css({
46+
[smallPipelineToolbar()]: {
47+
// NB: leafygreen renders labels near icons, that's the most "stable" way of
48+
// targeting it so we can hide it on smaller toolbar sizes. This can still
49+
// break if they drastically change how segmented control is rendered
50+
'& [data-segmented-control-icon] + span': {
51+
display: 'none',
52+
},
53+
},
54+
});
55+
4256
const toggleStageWizardStyles = css({ margin: 'auto' });
4357

4458
type PipelineExtraSettingsProps = {
@@ -99,15 +113,17 @@ export const PipelineExtraSettings: React.FunctionComponent<
99113
disabled={isPipelineModeDisabled}
100114
data-testid="pipeline-builder-toggle-builder-ui"
101115
value="builder-ui"
102-
glyph={<Icon glyph="CurlyBraces"></Icon>}
116+
glyph={<Icon data-segmented-control-icon glyph="CurlyBraces"></Icon>}
117+
className={segmentControlStyles}
103118
>
104119
Stages
105120
</SegmentedControlOption>
106121
<SegmentedControlOption
107122
disabled={isPipelineModeDisabled}
108123
data-testid="pipeline-builder-toggle-as-text"
109124
value="as-text"
110-
glyph={<Icon glyph="Code"></Icon>}
125+
glyph={<Icon data-segmented-control-icon glyph="Code"></Icon>}
126+
className={segmentControlStyles}
111127
>
112128
Text
113129
</SegmentedControlOption>

packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-toolbar-container.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@ const containerDisplayStyles = css({
1919
`,
2020
});
2121

22+
export const smallPipelineToolbar = () => {
23+
return `@container ${WorkspaceContainer.toolbarContainerQueryName} (width < 900px)`;
24+
};
25+
2226
export const hiddenOnNarrowPipelineToolbarStyles = css({
23-
[`@container ${WorkspaceContainer.toolbarContainerQueryName} (width < 900px)`]:
24-
{
25-
display: 'none',
26-
},
27+
[smallPipelineToolbar()]: {
28+
display: 'none',
29+
},
2730
});
2831

2932
export const PipelineToolbarContainer = ({

packages/compass-crud/src/components/bulk-delete-modal.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const documentContainerStyles = css({
3737
});
3838

3939
const modalBodySpacingStyles = css({
40-
marginTop: spacing[3],
40+
marginTop: spacing[3] - spacing[1], // see queryBarStyles below
4141
paddingLeft: spacing[5],
4242
display: 'flex',
4343
flexDirection: 'column',
@@ -49,6 +49,7 @@ const queryBarStyles = css({
4949
flexDirection: 'row',
5050
alignItems: 'center',
5151
gap: spacing[3],
52+
marginTop: spacing[1], // don't cut off the focus/hover ring on the Export button
5253
});
5354

5455
const exportToLanguageButtonStyles = css({

packages/compass-crud/src/stores/crud-store.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,10 @@ class CrudStoreImpl
11021102
}
11031103

11041104
async openBulkUpdateDialog() {
1105+
track('Bulk Update Opened', {
1106+
isUpdatePreviewSupported: this.state.isUpdatePreviewSupported,
1107+
});
1108+
11051109
await this.updateBulkUpdatePreview('{ $set: { } }');
11061110
this.setState({
11071111
bulkUpdate: {
@@ -1224,6 +1228,10 @@ class CrudStoreImpl
12241228
}
12251229

12261230
async runBulkUpdate() {
1231+
track('Bulk Update Executed', {
1232+
isUpdatePreviewSupported: this.state.isUpdatePreviewSupported,
1233+
});
1234+
12271235
this.closeBulkUpdateDialog();
12281236

12291237
// keep the filter count around for the duration of the toast
@@ -1826,6 +1834,8 @@ class CrudStoreImpl
18261834
}
18271835

18281836
openBulkDeleteDialog() {
1837+
track('Bulk Delete Opened');
1838+
18291839
const PREVIEW_DOCS = 5;
18301840

18311841
this.setState({
@@ -1880,6 +1890,8 @@ class CrudStoreImpl
18801890
}
18811891

18821892
async runBulkDelete() {
1893+
track('Bulk Delete Executed');
1894+
18831895
const { affected } = this.state.bulkDelete;
18841896
this.closeBulkDeleteDialog();
18851897

@@ -1919,6 +1931,10 @@ class CrudStoreImpl
19191931
}
19201932

19211933
async saveUpdateQuery(name: string): Promise<void> {
1934+
track('Bulk Update Favorited', {
1935+
isUpdatePreviewSupported: this.state.isUpdatePreviewSupported,
1936+
});
1937+
19221938
const { filter } = this.state.query;
19231939
let update;
19241940
try {

packages/compass-e2e-tests/tests/collection-bulk-delete.test.ts

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ describe('Bulk Delete', () => {
3636
});
3737

3838
it('deletes documents matching a filter', async function () {
39-
//const telemetryEntry = await browser.listenForTelemetryEvents(telemetry);
39+
const telemetryEntry = await browser.listenForTelemetryEvents(telemetry);
4040

4141
// Set a query that we'll use.
4242
await browser.runFindOperation('Documents', '{ i: 5 }');
@@ -45,6 +45,10 @@ describe('Bulk Delete', () => {
4545
await browser.clickVisible(Selectors.OpenBulkDeleteButton);
4646
await browser.$(Selectors.BulkDeleteModal).waitForDisplayed();
4747

48+
// Check the telemetry
49+
const openedEvent = await telemetryEntry('Bulk Delete Opened');
50+
expect(openedEvent).to.deep.equal({});
51+
4852
// Make sure the query is shown in the modal.
4953
expect(
5054
await browser.$(Selectors.BulkDeleteModalReadonlyFilter).getText()
@@ -71,6 +75,10 @@ describe('Bulk Delete', () => {
7175
await browser.clickVisible(Selectors.ConfirmationModalConfirmButton());
7276
await browser.runFindOperation('Documents', '{ i: 5 }');
7377

78+
// Check the telemetry
79+
const executedEvent = await telemetryEntry('Bulk Delete Executed');
80+
expect(executedEvent).to.deep.equal({});
81+
7482
// The success toast is displayed
7583
await browser.$(Selectors.BulkDeleteSuccessToast).waitForDisplayed();
7684

@@ -93,8 +101,6 @@ describe('Bulk Delete', () => {
93101
});
94102

95103
it('does not delete documents when cancelled', async function () {
96-
//const telemetryEntry = await browser.listenForTelemetryEvents(telemetry);
97-
98104
// Set a query that we'll use.
99105
await browser.runFindOperation('Documents', '{ i: 5 }');
100106

@@ -129,4 +135,52 @@ describe('Bulk Delete', () => {
129135
await browser.$(Selectors.DocumentListActionBarMessage).getText()
130136
).to.equal('1 – 1 of 1');
131137
});
138+
139+
it('can export a delete query', async function () {
140+
if (process.env.COMPASS_E2E_DISABLE_CLIPBOARD_USAGE === 'true') {
141+
this.skip();
142+
}
143+
144+
const telemetryEntry = await browser.listenForTelemetryEvents(telemetry);
145+
146+
// Set a query that we'll use.
147+
await browser.runFindOperation('Documents', '{ i: 5 }');
148+
149+
// Open the modal.
150+
await browser.clickVisible(Selectors.OpenBulkDeleteButton);
151+
await browser.$(Selectors.BulkDeleteModal).waitForDisplayed();
152+
153+
// Click the export button
154+
await browser.clickVisible(Selectors.BulkDeleteModalExportButton);
155+
156+
const openedEvent = await telemetryEntry('Delete Export Opened');
157+
expect(openedEvent).to.deep.equal({});
158+
159+
const text = await browser.exportToLanguage('Python', {
160+
includeImportStatements: true,
161+
includeDriverSyntax: true,
162+
useBuilders: false,
163+
});
164+
expect(text).to.equal(`from pymongo import MongoClient
165+
166+
# Requires the PyMongo package.
167+
# https://api.mongodb.com/python/current
168+
169+
client = MongoClient('mongodb://localhost:27091/test')
170+
filter={
171+
'i': 5
172+
}
173+
174+
result = client['test']['numbers'].delete_many(
175+
filter=filter
176+
)`);
177+
178+
const exportedEvent = await telemetryEntry('Delete Exported');
179+
expect(exportedEvent).to.deep.equal({
180+
language: 'python',
181+
with_builders: false,
182+
with_drivers_syntax: true,
183+
with_import_statements: true,
184+
});
185+
});
132186
});

0 commit comments

Comments
 (0)