Skip to content

Commit 25e2968

Browse files
committed
wip
1 parent 83132c5 commit 25e2968

File tree

5 files changed

+405
-21
lines changed

5 files changed

+405
-21
lines changed

packages/compass-components/src/components/context-menu.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,13 @@ export function ContextMenu({ menu }: ContextMenuWrapperProps) {
4242
<Menu
4343
renderMode="inline"
4444
open={menu.isOpen}
45-
setOpen={menu.close}
45+
setOpen={(open) => {
46+
if (!open) {
47+
menu.close();
48+
}
49+
}}
4650
justify="start"
51+
data-testid="context-menu"
4752
>
4853
{itemGroups.map(
4954
(itemGroup: ContextMenuItemGroup, groupIndex: number) => {
@@ -60,8 +65,8 @@ export function ContextMenu({ menu }: ContextMenuWrapperProps) {
6065
data-text={item.label}
6166
data-testid={`menu-group-${groupIndex}-item-${itemIndex}`}
6267
onClick={(evt: React.MouseEvent) => {
63-
item.onAction?.(evt);
6468
menu.close();
69+
item.onAction?.(evt);
6570
}}
6671
>
6772
{item.label}

packages/compass-crud/src/components/crud-toolbar.spec.tsx

Lines changed: 268 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ import React from 'react';
22
import { expect } from 'chai';
33
import sinon from 'sinon';
44
import {
5-
fireEvent,
65
render,
76
screen,
8-
cleanup,
97
within,
108
userEvent,
119
} from '@mongodb-js/testing-library-compass';
@@ -71,7 +69,7 @@ describe('CrudToolbar Component', function () {
7169
onResetClicked={noop}
7270
onUpdateButtonClicked={noop}
7371
onDeleteButtonClicked={noop}
74-
onExpandAllClicked={noop}
72+
onExpandAllClicked={props?.onExpandAllClicked || noop}
7573
onCollapseAllClicked={noop}
7674
openExportFileDialog={noop}
7775
outdated={false}
@@ -91,10 +89,6 @@ describe('CrudToolbar Component', function () {
9189
);
9290
}
9391

94-
afterEach(function () {
95-
cleanup();
96-
});
97-
9892
beforeEach(async function () {
9993
preferences = await createSandboxFromDefaultPreferences();
10094
});
@@ -128,7 +122,7 @@ describe('CrudToolbar Component', function () {
128122
});
129123

130124
expect(getPageSpy.called).to.be.false;
131-
fireEvent.click(screen.getByTestId('docs-toolbar-next-page-btn'));
125+
userEvent.click(screen.getByTestId('docs-toolbar-next-page-btn'));
132126

133127
expect(getPageSpy.calledOnce).to.be.true;
134128
expect(getPageSpy.firstCall.args[0]).to.equal(1);
@@ -141,7 +135,7 @@ describe('CrudToolbar Component', function () {
141135
});
142136

143137
expect(getPageSpy.called).to.be.false;
144-
fireEvent.click(screen.getByTestId('docs-toolbar-prev-page-btn'));
138+
userEvent.click(screen.getByTestId('docs-toolbar-prev-page-btn'));
145139

146140
expect(screen.getByTestId('docs-toolbar-prev-page-btn')).to.have.attribute(
147141
'aria-disabled',
@@ -164,7 +158,7 @@ describe('CrudToolbar Component', function () {
164158
end: 50,
165159
});
166160
expect(getPageSpy.called).to.be.false;
167-
fireEvent.click(screen.getByTestId('docs-toolbar-next-page-btn'));
161+
userEvent.click(screen.getByTestId('docs-toolbar-next-page-btn'));
168162

169163
expect(
170164
screen.getByTestId('docs-toolbar-next-page-btn')
@@ -184,7 +178,7 @@ describe('CrudToolbar Component', function () {
184178
end: 25,
185179
});
186180
expect(getPageSpy.called).to.be.false;
187-
fireEvent.click(screen.getByTestId('docs-toolbar-next-page-btn'));
181+
userEvent.click(screen.getByTestId('docs-toolbar-next-page-btn'));
188182

189183
expect(
190184
screen.getByTestId('docs-toolbar-next-page-btn')
@@ -209,7 +203,7 @@ describe('CrudToolbar Component', function () {
209203
);
210204

211205
expect(getPageSpy.called).to.be.false;
212-
fireEvent.click(screen.getByTestId('docs-toolbar-prev-page-btn'));
206+
userEvent.click(screen.getByTestId('docs-toolbar-prev-page-btn'));
213207

214208
expect(getPageSpy.calledOnce).to.be.true;
215209
expect(getPageSpy.firstCall.args[0]).to.equal(0);
@@ -226,7 +220,7 @@ describe('CrudToolbar Component', function () {
226220
});
227221

228222
expect(getPageSpy.called).to.be.false;
229-
fireEvent.click(screen.getByTestId('docs-toolbar-next-page-btn'));
223+
userEvent.click(screen.getByTestId('docs-toolbar-next-page-btn'));
230224

231225
expect(screen.getByTestId('docs-toolbar-next-page-btn')).to.have.attribute(
232226
'aria-disabled',
@@ -248,7 +242,7 @@ describe('CrudToolbar Component', function () {
248242
expect(nextButton).to.have.attribute('aria-disabled', 'false');
249243

250244
expect(getPageSpy.called).to.be.false;
251-
fireEvent.click(nextButton);
245+
userEvent.click(nextButton);
252246

253247
expect(getPageSpy.calledOnce).to.be.true;
254248
expect(getPageSpy.firstCall.args[0]).to.equal(3);
@@ -295,8 +289,8 @@ describe('CrudToolbar Component', function () {
295289
});
296290

297291
expect(exportSpy.called).to.be.false;
298-
fireEvent.click(screen.getByText('Export Data'));
299-
fireEvent.click(screen.getByText('Export the full collection'));
292+
userEvent.click(screen.getByText('Export Data'));
293+
userEvent.click(screen.getByText('Export the full collection'));
300294

301295
expect(exportSpy.calledOnce).to.be.true;
302296
expect(exportSpy.firstCall.args[0]).to.be.true;
@@ -527,4 +521,262 @@ describe('CrudToolbar Component', function () {
527521
expect(stub).to.be.calledWithExactly(75);
528522
});
529523
});
524+
525+
describe('context menu', function () {
526+
beforeEach(async function () {
527+
await preferences.savePreferences({ enableImportExport: true });
528+
});
529+
530+
it('should open context menu on right click', async function () {
531+
renderCrudToolbar();
532+
533+
const toolbar = screen.getByTestId('query-bar').closest('div');
534+
await userEvent.pointer({ target: toolbar!, keys: '[MouseRight]' });
535+
536+
const contextMenu = screen.getByTestId('context-menu');
537+
expect(within(contextMenu).getByText('Expand all documents')).to.be
538+
.visible;
539+
expect(within(contextMenu).getByText('Refresh')).to.be.visible;
540+
});
541+
542+
it('should call onExpandAllClicked when "Expand all documents" is clicked', async function () {
543+
const onExpandAllClicked = sinon.spy();
544+
renderCrudToolbar({ onExpandAllClicked });
545+
546+
const toolbar = screen.getByTestId('query-bar').closest('div');
547+
userEvent.click(toolbar!, { button: 2 });
548+
549+
const contextMenu = screen.getByTestId('context-menu');
550+
const expandMenuItem = within(contextMenu).getByText(
551+
'Expand all documents'
552+
);
553+
userEvent.click(expandMenuItem);
554+
555+
expect(onExpandAllClicked).to.have.been.calledOnce;
556+
});
557+
558+
it('should call onCollapseAllClicked when "Collapse all documents" is clicked', function () {
559+
const onCollapseAllClicked = sinon.spy();
560+
renderCrudToolbar({ onCollapseAllClicked });
561+
562+
const toolbar = screen.getByTestId('query-bar').closest('div');
563+
userEvent.click(toolbar!, { button: 2 });
564+
565+
const contextMenu = screen.getByTestId('context-menu');
566+
const collapseMenuItem = within(contextMenu).getByText(
567+
'Collapse all documents'
568+
);
569+
userEvent.click(collapseMenuItem);
570+
571+
expect(onCollapseAllClicked).to.have.been.calledOnce;
572+
});
573+
574+
it('should call insertDataHandler with "import-file" when "Import JSON or CSV file" is clicked', function () {
575+
const insertDataHandler = sinon.spy();
576+
renderCrudToolbar({ insertDataHandler });
577+
578+
const toolbar = screen.getByTestId('query-bar').closest('div');
579+
userEvent.click(toolbar!, { button: 2 });
580+
581+
const contextMenu = screen.getByTestId('context-menu');
582+
const importMenuItem = within(contextMenu).getByText(
583+
'Import JSON or CSV file'
584+
);
585+
userEvent.click(importMenuItem);
586+
587+
expect(insertDataHandler).to.have.been.calledOnceWithExactly(
588+
'import-file'
589+
);
590+
});
591+
592+
it('should call insertDataHandler with "insert-document" when "Insert document..." is clicked', function () {
593+
const insertDataHandler = sinon.spy();
594+
renderCrudToolbar({ insertDataHandler });
595+
596+
const toolbar = screen.getByTestId('query-bar').closest('div');
597+
userEvent.click(toolbar!, { button: 2 });
598+
599+
const contextMenu = screen.getByTestId('context-menu');
600+
const insertMenuItem =
601+
within(contextMenu).getByText('Insert document...');
602+
userEvent.click(insertMenuItem);
603+
604+
expect(insertDataHandler).to.have.been.calledOnceWithExactly(
605+
'insert-document'
606+
);
607+
});
608+
609+
it('should call openExportFileDialog with false when "Export query results..." is clicked', function () {
610+
const openExportFileDialog = sinon.spy();
611+
renderCrudToolbar({ openExportFileDialog });
612+
613+
const toolbar = screen.getByTestId('query-bar').closest('div');
614+
userEvent.click(toolbar!, { button: 2 });
615+
616+
const contextMenu = screen.getByTestId('context-menu');
617+
const exportQueryMenuItem = within(contextMenu).getByText(
618+
'Export query results...'
619+
);
620+
userEvent.click(exportQueryMenuItem);
621+
622+
expect(openExportFileDialog).to.have.been.calledOnceWithExactly(false);
623+
});
624+
625+
it('should call openExportFileDialog with true when "Export full collection..." is clicked', function () {
626+
const openExportFileDialog = sinon.spy();
627+
renderCrudToolbar({ openExportFileDialog });
628+
629+
const toolbar = screen.getByTestId('query-bar').closest('div');
630+
userEvent.click(toolbar!, { button: 2 });
631+
632+
const contextMenu = screen.getByTestId('context-menu');
633+
const exportCollectionMenuItem = within(contextMenu).getByText(
634+
'Export full collection...'
635+
);
636+
userEvent.click(exportCollectionMenuItem);
637+
638+
expect(openExportFileDialog).to.have.been.calledOnceWithExactly(true);
639+
});
640+
641+
it('should call onUpdateButtonClicked when "Bulk update" is clicked', function () {
642+
const onUpdateButtonClicked = sinon.spy();
643+
renderCrudToolbar({ onUpdateButtonClicked });
644+
645+
const toolbar = screen.getByTestId('query-bar').closest('div');
646+
userEvent.click(toolbar!, { button: 2 });
647+
648+
const contextMenu = screen.getByTestId('context-menu');
649+
const updateMenuItem = within(contextMenu).getByText('Bulk update');
650+
userEvent.click(updateMenuItem);
651+
652+
expect(onUpdateButtonClicked).to.have.been.calledOnce;
653+
});
654+
655+
it('should call onDeleteButtonClicked when "Bulk delete" is clicked', function () {
656+
const onDeleteButtonClicked = sinon.spy();
657+
renderCrudToolbar({ onDeleteButtonClicked });
658+
659+
const toolbar = screen.getByTestId('query-bar').closest('div');
660+
userEvent.click(toolbar!, { button: 2 });
661+
662+
const contextMenu = screen.getByTestId('context-menu');
663+
const deleteMenuItem = within(contextMenu).getByText('Bulk delete');
664+
userEvent.click(deleteMenuItem);
665+
666+
expect(onDeleteButtonClicked).to.have.been.calledOnce;
667+
});
668+
669+
it('should call refreshDocuments when "Refresh" is clicked', function () {
670+
const refreshDocuments = sinon.spy();
671+
renderCrudToolbar({ refreshDocuments });
672+
673+
const toolbar = screen.getByTestId('query-bar').closest('div');
674+
userEvent.click(toolbar!, { button: 2 });
675+
676+
const contextMenu = screen.getByTestId('context-menu');
677+
const refreshMenuItem = within(contextMenu).getByText('Refresh');
678+
userEvent.click(refreshMenuItem);
679+
680+
expect(refreshDocuments).to.have.been.calledOnce;
681+
});
682+
683+
describe('conditional menu items', function () {
684+
it('should not show import/export items when enableImportExport is false', async function () {
685+
await preferences.savePreferences({ enableImportExport: false });
686+
renderCrudToolbar();
687+
688+
const toolbar = screen.getByTestId('query-bar').closest('div');
689+
userEvent.click(toolbar!, { button: 2 });
690+
691+
const contextMenu = screen.getByTestId('context-menu');
692+
expect(within(contextMenu).queryByText('Import JSON or CSV file')).to
693+
.not.exist;
694+
expect(within(contextMenu).queryByText('Export query results...')).to
695+
.not.exist;
696+
expect(within(contextMenu).queryByText('Export full collection...')).to
697+
.not.exist;
698+
});
699+
700+
it('should not show insert document item when readonly is true', function () {
701+
renderCrudToolbar({ readonly: true });
702+
703+
const toolbar = screen.getByTestId('query-bar').closest('div');
704+
userEvent.click(toolbar!, { button: 2 });
705+
706+
const contextMenu = screen.getByTestId('context-menu');
707+
expect(within(contextMenu).queryByText('Insert document...')).to.not
708+
.exist;
709+
});
710+
711+
it('should not show bulk operations when readonly is true', function () {
712+
renderCrudToolbar({ readonly: true });
713+
714+
const toolbar = screen.getByTestId('query-bar').closest('div');
715+
userEvent.click(toolbar!, { button: 2 });
716+
717+
const contextMenu = screen.getByTestId('context-menu');
718+
expect(within(contextMenu).queryByText('Bulk update')).to.not.exist;
719+
expect(within(contextMenu).queryByText('Bulk delete')).to.not.exist;
720+
});
721+
722+
it('should not show bulk operations when isWritable is false', function () {
723+
renderCrudToolbar({ isWritable: false });
724+
725+
const toolbar = screen.getByTestId('query-bar').closest('div');
726+
userEvent.click(toolbar!, { button: 2 });
727+
728+
const contextMenu = screen.getByTestId('context-menu');
729+
expect(within(contextMenu).queryByText('Bulk update')).to.not.exist;
730+
expect(within(contextMenu).queryByText('Bulk delete')).to.not.exist;
731+
});
732+
733+
it('should not show bulk operations when query has skip', function () {
734+
renderCrudToolbar({ querySkip: 10 });
735+
736+
const toolbar = screen.getByTestId('query-bar').closest('div');
737+
userEvent.click(toolbar!, { button: 2 });
738+
739+
const contextMenu = screen.getByTestId('context-menu');
740+
expect(within(contextMenu).queryByText('Bulk update')).to.not.exist;
741+
});
742+
743+
it('should not show bulk operations when query has limit', function () {
744+
renderCrudToolbar({ queryLimit: 10 });
745+
746+
const toolbar = screen.getByTestId('query-bar').closest('div');
747+
userEvent.click(toolbar!, { button: 2 });
748+
749+
const contextMenu = screen.getByTestId('context-menu');
750+
expect(within(contextMenu).queryByText('Bulk update')).to.not.exist;
751+
expect(within(contextMenu).queryByText('Bulk delete')).to.not.exist;
752+
});
753+
754+
it('should show all applicable items when conditions are met', function () {
755+
renderCrudToolbar({
756+
readonly: false,
757+
isWritable: true,
758+
querySkip: 0,
759+
queryLimit: 0,
760+
});
761+
762+
const toolbar = screen.getByTestId('query-bar').closest('div');
763+
userEvent.click(toolbar!, { button: 2 });
764+
765+
const contextMenu = screen.getByTestId('context-menu');
766+
expect(within(contextMenu).getByText('Expand all documents')).to.be
767+
.visible;
768+
expect(within(contextMenu).getByText('Import JSON or CSV file')).to.be
769+
.visible;
770+
expect(within(contextMenu).getByText('Insert document...')).to.be
771+
.visible;
772+
expect(within(contextMenu).getByText('Export query results...')).to.be
773+
.visible;
774+
expect(within(contextMenu).getByText('Export full collection...')).to.be
775+
.visible;
776+
expect(within(contextMenu).getByText('Bulk update')).to.be.visible;
777+
expect(within(contextMenu).getByText('Bulk delete')).to.be.visible;
778+
expect(within(contextMenu).getByText('Refresh')).to.be.visible;
779+
});
780+
});
781+
});
530782
});

0 commit comments

Comments
 (0)