Skip to content

Commit 2e47c57

Browse files
authored
feat(bulk-update): render document count and restyle query preview (#5140)
* handle doc count and style filter preview * fix font family * focus on right element, align filter * fix tests * fix use id * remove disabled true
1 parent f285a73 commit 2e47c57

File tree

5 files changed

+86
-53
lines changed

5 files changed

+86
-53
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ describe('BulkDeleteModal Component', function () {
6969

7070
it('shows the provided query', function () {
7171
renderBulkDeleteModal({ filterQuery: '{ a: 1 }' });
72-
expect(screen.queryByDisplayValue('{ a: 1 }')).to.be.visible;
72+
expect(screen.getByTestId('readonly-filter').textContent).to.equal(
73+
'{ a: 1 }'
74+
);
7375
});
7476

7577
it('closes the modal when cancelled', function () {

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
css,
1111
cx,
1212
spacing,
13+
useId,
1314
} from '@mongodb-js/compass-components';
1415
import { ReadonlyFilter } from './readonly-filter';
1516
import ReadonlyDocument from './readonly-document';
@@ -87,8 +88,14 @@ const BulkDeleteModal: React.FunctionComponent<BulkDeleteModalProps> = ({
8788
</div>
8889
);
8990

91+
const exportButtonId = useId();
9092
return (
91-
<Modal setOpen={onCancel} open={open} data-testid="bulk-delete-modal">
93+
<Modal
94+
initialFocus={`#${exportButtonId}`}
95+
setOpen={onCancel}
96+
open={open}
97+
data-testid="bulk-delete-modal"
98+
>
9299
<ModalHeader
93100
title={`Delete ${documentCount ?? ''} document${
94101
documentCount === 1 ? '' : 's'
@@ -106,6 +113,7 @@ const BulkDeleteModal: React.FunctionComponent<BulkDeleteModalProps> = ({
106113
leftGlyph={<Icon glyph="Code" />}
107114
onClick={onExportToLanguage}
108115
data-testid="export-button"
116+
id={exportButtonId}
109117
>
110118
Export
111119
</Button>

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

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,14 @@ describe('BulkUpdateDialog Component', function () {
4545
it('renders if open', function () {
4646
renderBulkUpdateDialog({ count: 42 });
4747

48+
expect(screen.getByTestId('modal-title').textContent).to.equal(
49+
'Update 42 documents'
50+
);
51+
4852
// filter
49-
expect(
50-
screen.getByTestId('readonly-filter').getAttribute('value')
51-
).to.equal('{\n a: 1\n}');
53+
expect(screen.getByTestId('readonly-filter').textContent).to.equal(
54+
'{\n a: 1\n}'
55+
);
5256

5357
// update
5458
expect(screen.getByTestId('bulk-update-update').textContent).to.match(
@@ -66,13 +70,22 @@ describe('BulkUpdateDialog Component', function () {
6670
.exist;
6771
});
6872

69-
it('renders with count=undefined', function () {
73+
it('hides document count if count is N/A', function () {
7074
renderBulkUpdateDialog({ count: undefined });
75+
76+
expect(screen.getByTestId('modal-title').textContent).to.equal(
77+
'Update documents'
78+
);
79+
7180
expect(screen.getByRole('button', { name: 'Update documents' })).to.exist;
7281
});
7382

74-
it('renders with count=1', function () {
83+
it('use singular if count is 1', function () {
7584
renderBulkUpdateDialog({ count: 1 });
85+
expect(screen.getByTestId('modal-title').textContent).to.equal(
86+
'Update 1 document'
87+
);
88+
7689
expect(screen.getByRole('button', { name: 'Update 1 document' })).to.exist;
7790
});
7891

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

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
Icon,
2424
InteractivePopover,
2525
TextInput,
26+
useId,
2627
} from '@mongodb-js/compass-components';
2728
import type { Annotation } from '@mongodb-js/compass-editor';
2829
import { CodemirrorMultilineEditor } from '@mongodb-js/compass-editor';
@@ -293,17 +294,28 @@ export default function BulkUpdateDialog({
293294
}
294295
}, [isOpen, wasOpen, updateText]);
295296

297+
const modalTitleAndButtonText = useMemo(() => {
298+
if (typeof count !== 'number') {
299+
return 'Update documents';
300+
}
301+
302+
if (count === 1) {
303+
return `Update 1 document`;
304+
}
305+
306+
return `Update ${count} documents`;
307+
}, [count]);
308+
309+
const bulkUpdateUpdateId = useId();
296310
return (
297311
<Modal
298312
open={isOpen}
299313
setOpen={closeBulkUpdateDialog}
300314
size="large"
301315
data-testid="bulk-update-dialog"
316+
initialFocus={`#${bulkUpdateUpdateId} .cm-content`}
302317
>
303-
<ModalHeader
304-
title={`Update ${count ?? ''} document${count === 1 ? '' : 's'}`}
305-
subtitle={ns}
306-
/>
318+
<ModalHeader title={modalTitleAndButtonText} subtitle={ns} />
307319
<ModalBody>
308320
<div className={columnsStyles}>
309321
<div className={queryStyles}>
@@ -315,9 +327,12 @@ export default function BulkUpdateDialog({
315327
</div>
316328

317329
<div className={cx(queryFieldStyles, updateFieldStyles)}>
318-
<Label htmlFor="bulk-update-update">Update</Label>
330+
<Label htmlFor={bulkUpdateUpdateId}>Update</Label>
319331
<Description className={descriptionStyles}>
320-
<Link href="https://www.mongodb.com/docs/manual/reference/method/db.collection.updateMany/#std-label-update-many-update">
332+
<Link
333+
tabIndex={0}
334+
href="https://www.mongodb.com/docs/manual/reference/method/db.collection.updateMany/#std-label-update-many-update"
335+
>
321336
Learn more about Update syntax
322337
</Link>
323338
</Description>
@@ -331,7 +346,7 @@ export default function BulkUpdateDialog({
331346
<CodemirrorMultilineEditor
332347
text={text}
333348
onChangeText={onChangeText}
334-
id="bulk-update-update"
349+
id={bulkUpdateUpdateId}
335350
data-testid="bulk-update-update"
336351
onBlur={() => ({})}
337352
annotations={annotations}
@@ -398,7 +413,7 @@ export default function BulkUpdateDialog({
398413
onClick={runBulkUpdate}
399414
data-testid="update-button"
400415
>
401-
Update {count ?? ''} document{count === 1 ? '' : 's'}
416+
{modalTitleAndButtonText}
402417
</Button>
403418
</div>
404419
</ModalFooter>
Lines changed: 33 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,37 @@
11
import React from 'react';
22

33
import {
4-
TextInput,
54
InfoSprinkle,
65
Label,
76
css,
87
spacing,
8+
KeylineCard,
99
fontFamilies,
10+
useId,
1011
} from '@mongodb-js/compass-components';
1112

12-
type QueryLabelProps = {
13-
tooltip: string;
14-
label: string;
15-
};
16-
1713
const queryLabelStyles = css({
1814
display: 'flex',
1915
gap: spacing[2],
2016
alignItems: 'center',
21-
height: '17px', // align with the Preview label
2217
});
2318

24-
const QueryLabel: React.FunctionComponent<QueryLabelProps> = ({
25-
tooltip,
26-
label,
27-
}) => {
28-
return (
29-
<div className={queryLabelStyles}>
30-
<Label htmlFor="readonly-filter">{label}</Label>
31-
<InfoSprinkle align="right">{tooltip}</InfoSprinkle>
32-
</div>
33-
);
34-
};
35-
36-
const textInputStyles = css({
37-
input: {
38-
fontFamily: fontFamilies.code,
39-
},
19+
const readOnlyFilterStyles = css({
20+
padding: spacing[2],
21+
overflow: 'scroll',
4022
width: '100%',
23+
whiteSpace: 'nowrap',
24+
display: 'inline-block',
25+
verticalAlign: 'middle',
26+
lineHeight: `${spacing[3] + 2}px`,
27+
'::-webkit-scrollbar': {
28+
display: 'none',
29+
},
30+
});
31+
32+
const codeStyles = css({
33+
fontSize: 14,
34+
fontFamily: fontFamilies.code,
4135
});
4236

4337
type ReadonlyFilterProps = {
@@ -49,21 +43,22 @@ export function ReadonlyFilter({
4943
queryLabel,
5044
filterQuery,
5145
}: ReadonlyFilterProps) {
46+
const readOnlyFilterId = useId();
5247
return (
53-
<TextInput
54-
id="readonly-filter"
55-
data-testid="readonly-filter"
56-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
57-
// @ts-ignore the label can be any component, but it's weirdly typed to string
58-
label={
59-
<QueryLabel
60-
label={queryLabel}
61-
tooltip="Return to the Documents tab to edit this query."
62-
/>
63-
}
64-
disabled={true}
65-
value={filterQuery}
66-
className={textInputStyles}
67-
/>
48+
<>
49+
<div className={queryLabelStyles}>
50+
<Label htmlFor={readOnlyFilterId}>{queryLabel}</Label>
51+
<InfoSprinkle align="right">
52+
Return to the Documents tab to edit this query.
53+
</InfoSprinkle>
54+
</div>
55+
<KeylineCard
56+
id={readOnlyFilterId}
57+
data-testid="readonly-filter"
58+
className={readOnlyFilterStyles}
59+
>
60+
<code className={codeStyles}>{filterQuery}</code>
61+
</KeylineCard>
62+
</>
6863
);
6964
}

0 commit comments

Comments
 (0)