Skip to content

Commit d07248c

Browse files
Merge remote-tracking branch 'origin/main' into beta-releases
2 parents b151ff6 + 93f6efc commit d07248c

File tree

35 files changed

+1502
-105
lines changed

35 files changed

+1502
-105
lines changed

package-lock.json

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/compass-app-stores/src/stores/instance-store.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,24 @@ export function createInstanceStore({
266266
});
267267
onAppRegistryEvent('refresh-databases', onRefreshDatabases);
268268

269+
const onCollectionRenamed = voidify(
270+
async ({ from, to }: { from: string; to: string }) => {
271+
// we must fetch the old collection's metadata before refreshing because refreshing the
272+
// collection metadata will remove the old collection from the model.
273+
const metadata = await fetchCollectionMetadata(from);
274+
appRegistry.emit('refresh-collection-tabs', {
275+
metadata,
276+
newNamespace: to,
277+
});
278+
const { database } = toNS(from);
279+
await refreshNamespace({
280+
ns: to,
281+
database,
282+
});
283+
}
284+
);
285+
appRegistry.on('collection-renamed', onCollectionRenamed);
286+
269287
// Event emitted when the Collections grid needs to be refreshed
270288
// with new collections or collection stats for existing ones.
271289
const onRefreshCollections = voidify(async ({ ns }: { ns: string }) => {

packages/compass-collection/src/modules/tabs.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ enum CollectionTabsActions {
3737
DatabaseDropped = 'compass-collection/DatabaseDropped',
3838
DataServiceConnected = 'compass-collection/DataServiceConnected',
3939
DataServiceDisconnected = 'compass-collection/DataServiceDisconnected',
40+
CollectionRenamed = 'compass-collection/CollectionRenamed',
4041
}
4142

4243
type CollectionTabsThunkAction<
@@ -182,6 +183,17 @@ const reducer: Reducer<CollectionTabsState> = (
182183
tabs: newTabs,
183184
};
184185
}
186+
if (action.type === CollectionTabsActions.CollectionRenamed) {
187+
const { tabs } = action;
188+
189+
const activeTabIndex = getActiveTabIndex(state);
190+
const activeTabId = tabs[activeTabIndex]?.id ?? null;
191+
return {
192+
...state,
193+
tabs,
194+
activeTabId,
195+
};
196+
}
185197
return state;
186198
};
187199

@@ -391,4 +403,30 @@ export const dataServiceDisconnected = () => {
391403
return { type: CollectionTabsActions.DataServiceDisconnected };
392404
};
393405

406+
export const collectionRenamed = ({
407+
from,
408+
newNamespace,
409+
}: {
410+
from: CollectionMetadata;
411+
newNamespace: string;
412+
}): CollectionTabsThunkAction<void> => {
413+
return (dispatch, getState) => {
414+
const tabs = getState().tabs.map((tab) =>
415+
tab.namespace === from.namespace
416+
? dispatch(
417+
createNewTab({
418+
...from,
419+
namespace: newNamespace,
420+
})
421+
)
422+
: tab
423+
);
424+
425+
dispatch({
426+
type: CollectionTabsActions.CollectionRenamed,
427+
tabs,
428+
});
429+
};
430+
};
431+
394432
export default reducer;

packages/compass-collection/src/stores/tabs.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ describe('Collection Tabs Store', function () {
324324
'select-namespace',
325325
'collection-dropped',
326326
'database-dropped',
327+
'refresh-collection-tabs',
327328
'data-service-connected',
328329
'data-service-disconnected',
329330
'menu-share-schema-json',

packages/compass-collection/src/stores/tabs.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ import tabs, {
1313
getActiveTab,
1414
dataServiceDisconnected,
1515
dataServiceConnected,
16+
collectionRenamed,
1617
} from '../modules/tabs';
1718
import { globalAppRegistry } from 'hadron-app-registry';
19+
import type { CollectionMetadata } from 'mongodb-collection-model';
1820

1921
type ThunkExtraArg = {
2022
globalAppRegistry: AppRegistry;
@@ -81,6 +83,24 @@ export function configureStore({
8183
store.dispatch(databaseDropped(namespace));
8284
});
8385

86+
globalAppRegistry.on(
87+
'refresh-collection-tabs',
88+
({
89+
metadata,
90+
newNamespace,
91+
}: {
92+
metadata: CollectionMetadata;
93+
newNamespace: string;
94+
}) => {
95+
store.dispatch(
96+
collectionRenamed({
97+
from: metadata,
98+
newNamespace,
99+
})
100+
);
101+
}
102+
);
103+
84104
/**
85105
* Set the data service in the store when connected.
86106
*/

packages/compass-components/src/hooks/use-toast.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const defaultToastProperties: Partial<ToastProperties> = {
2121
dismissible: true,
2222
};
2323

24-
interface ToastActions {
24+
export interface ToastActions {
2525
openToast: (id: string, toastProperties: ToastProperties) => void;
2626
closeToast: (id: string) => void;
2727
}

packages/compass-crud/src/components/bulk-update-dialog.spec.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ describe('BulkUpdateDialog Component', function () {
9090
expect(screen.getByRole('button', { name: 'Update 1 document' })).to.exist;
9191
});
9292

93+
it('renders the empty state if the count is 0', function () {
94+
renderBulkUpdateDialog({ count: 0 });
95+
expect(screen.getByTestId('bulk-update-preview-empty-state')).to.exist;
96+
});
97+
9398
it('resets if the modal is re-opened', async function () {
9499
// initial open
95100
const { rerender } = renderBulkUpdateDialog({ isOpen: true });

packages/compass-crud/src/components/bulk-update-dialog.tsx

Lines changed: 84 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import Document from './document';
3232
import type { BSONObject } from '../stores/crud-store';
3333

3434
import { ReadonlyFilter } from './readonly-filter';
35+
import { DocumentIcon } from '@mongodb-js/compass-components';
3536

3637
const columnsStyles = css({
3738
marginTop: spacing[4],
@@ -225,6 +226,88 @@ const InlineSaveQueryModal: React.FunctionComponent<
225226
);
226227
};
227228

229+
const previewZeroStateIconStyles = css({
230+
margin: 'auto',
231+
display: 'flex',
232+
flexDirection: 'column',
233+
gap: spacing[2],
234+
alignItems: 'center',
235+
});
236+
237+
const previewNoResultsLabel = css({
238+
color: palette.green.dark2,
239+
});
240+
241+
const previewZeroStateDescriptionStyles = css({
242+
textAlign: 'center',
243+
margin: 0,
244+
});
245+
246+
export type BulkUpdatePreviewProps = {
247+
count?: number;
248+
preview: UpdatePreview;
249+
};
250+
251+
const BulkUpdatePreview: React.FunctionComponent<BulkUpdatePreviewProps> = ({
252+
count,
253+
preview,
254+
}) => {
255+
const previewDocuments = useMemo(() => {
256+
return preview.changes.map(
257+
(change) => new HadronDocument(change.after as Record<string, unknown>)
258+
);
259+
}, [preview]);
260+
261+
// show a preview for the edge case where the count is undefined, not the
262+
// empty state
263+
if (count === 0) {
264+
return (
265+
<div
266+
className={updatePreviewStyles}
267+
data-testid="bulk-update-preview-empty-state"
268+
>
269+
<Label htmlFor="bulk-update-preview">
270+
Preview{' '}
271+
<Description className={previewDescriptionStyles}>
272+
(sample of {preview.changes.length} document
273+
{preview.changes.length === 1 ? '' : 's'})
274+
</Description>
275+
</Label>
276+
<div className={previewZeroStateIconStyles}>
277+
<DocumentIcon />
278+
<b className={previewNoResultsLabel}>No results</b>
279+
<p className={previewZeroStateDescriptionStyles}>
280+
Try modifying your query to get results.
281+
</p>
282+
</div>
283+
</div>
284+
);
285+
}
286+
287+
return (
288+
<div className={previewStyles}>
289+
<Label htmlFor="bulk-update-preview">
290+
Preview{' '}
291+
<Description className={previewDescriptionStyles}>
292+
(sample of {preview.changes.length} document
293+
{preview.changes.length === 1 ? '' : 's'})
294+
</Description>
295+
</Label>
296+
<div className={updatePreviewStyles}>
297+
{previewDocuments.map((doc: HadronDocument, index: number) => {
298+
return (
299+
<UpdatePreviewDocument
300+
key={`change=${index}`}
301+
data-testid="bulk-update-preview-document"
302+
doc={doc}
303+
/>
304+
);
305+
})}
306+
</div>
307+
</div>
308+
);
309+
};
310+
228311
export type BulkUpdateDialogProps = {
229312
isOpen: boolean;
230313
ns: string;
@@ -261,12 +344,6 @@ export default function BulkUpdateDialog({
261344
const [text, setText] = useState(updateText);
262345
const wasOpen = usePrevious(isOpen);
263346

264-
const previewDocuments = useMemo(() => {
265-
return preview.changes.map(
266-
(change) => new HadronDocument(change.after as Record<string, unknown>)
267-
);
268-
}, [preview]);
269-
270347
const onChangeText = (value: string) => {
271348
setText(value);
272349
updateBulkUpdatePreview(value);
@@ -376,26 +453,7 @@ export default function BulkUpdateDialog({
376453
</div>
377454
</div>
378455
{enablePreview && (
379-
<div className={previewStyles}>
380-
<Label htmlFor="bulk-update-preview">
381-
Preview{' '}
382-
<Description className={previewDescriptionStyles}>
383-
(sample of {preview.changes.length} document
384-
{preview.changes.length === 1 ? '' : 's'})
385-
</Description>
386-
</Label>
387-
<div className={updatePreviewStyles}>
388-
{previewDocuments.map((doc: HadronDocument, index: number) => {
389-
return (
390-
<UpdatePreviewDocument
391-
key={`change=${index}`}
392-
data-testid="bulk-update-preview-document"
393-
doc={doc}
394-
/>
395-
);
396-
})}
397-
</div>
398-
</div>
456+
<BulkUpdatePreview count={count} preview={preview} />
399457
)}
400458
</div>
401459
</ModalBody>

packages/compass-databases-navigation/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
},
5252
"dependencies": {
5353
"@mongodb-js/compass-components": "^1.19.0",
54+
"compass-preferences-model": "^2.15.6",
5455
"react-virtualized-auto-sizer": "^1.0.6",
5556
"react-window": "^1.8.6"
5657
},

packages/compass-databases-navigation/src/collection-item.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import type {
2121
NamespaceItemProps,
2222
} from './tree-item';
2323
import type { Actions } from './constants';
24+
import { usePreference } from 'compass-preferences-model';
2425

2526
const CollectionIcon: React.FunctionComponent<{
2627
type: string;
@@ -64,6 +65,10 @@ export const CollectionItem: React.FunctionComponent<
6465
style,
6566
onNamespaceAction,
6667
}) => {
68+
const isRenameCollectionEnabled = usePreference(
69+
'enableRenameCollectionModal',
70+
React
71+
);
6772
const [hoverProps, isHovered] = useHoverState();
6873

6974
const onDefaultAction = useCallback(
@@ -121,16 +126,26 @@ export const CollectionItem: React.FunctionComponent<
121126
icon: 'Edit',
122127
}
123128
);
124-
} else {
129+
130+
return actions;
131+
}
132+
133+
if (type !== 'timeseries' && isRenameCollectionEnabled) {
125134
actions.push({
126-
action: 'drop-collection',
127-
label: 'Drop collection',
128-
icon: 'Trash',
135+
action: 'rename-collection',
136+
label: 'Rename collection',
137+
icon: 'Edit',
129138
});
130139
}
131140

141+
actions.push({
142+
action: 'drop-collection',
143+
label: 'Drop collection',
144+
icon: 'Trash',
145+
});
146+
132147
return actions;
133-
}, [type, isReadOnly]);
148+
}, [type, isReadOnly, isRenameCollectionEnabled]);
134149

135150
return (
136151
<ItemContainer

0 commit comments

Comments
 (0)