Skip to content

Commit 1bc37cc

Browse files
authored
feat(compass-crud): expand/collapse all COMPASS-8233 (#6601)
1 parent 917f113 commit 1bc37cc

File tree

9 files changed

+146
-39
lines changed

9 files changed

+146
-39
lines changed
Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import { DropdownMenuButton } from '@mongodb-js/compass-components';
2+
import { css, DropdownMenuButton } from '@mongodb-js/compass-components';
33
import type { MenuAction } from '@mongodb-js/compass-components';
44

55
export type PipelineOutputOption = 'expand' | 'collapse';
@@ -8,19 +8,31 @@ const pipelineOptionsActions: MenuAction<PipelineOutputOption>[] = [
88
{ action: 'expand', label: 'Expand all fields' },
99
];
1010

11+
const containerStyles = css({
12+
display: 'flex',
13+
alignItems: 'center',
14+
flex: 'none',
15+
});
16+
17+
const defaultTitle = 'Output Options';
18+
1119
export const PipelineOutputOptionsMenu: React.FunctionComponent<{
1220
onChangeOption: (option: PipelineOutputOption) => void;
1321
buttonText?: string;
1422
}> = ({ onChangeOption, buttonText }) => {
1523
return (
16-
<DropdownMenuButton<PipelineOutputOption>
17-
data-testid="pipeline-output-options"
18-
actions={pipelineOptionsActions}
19-
onAction={onChangeOption}
20-
buttonText={buttonText ?? 'Output Options'}
21-
buttonProps={{
22-
size: 'xsmall',
23-
}}
24-
></DropdownMenuButton>
24+
<div className={containerStyles}>
25+
<DropdownMenuButton<PipelineOutputOption>
26+
data-testid="pipeline-output-options"
27+
actions={pipelineOptionsActions}
28+
onAction={onChangeOption}
29+
buttonText={buttonText ?? defaultTitle}
30+
buttonProps={{
31+
size: 'xsmall',
32+
title: buttonText || defaultTitle,
33+
['aria-label']: buttonText || defaultTitle,
34+
}}
35+
></DropdownMenuButton>
36+
</div>
2537
);
2638
};

packages/compass-aggregations/src/components/pipeline-results-workspace/pipeline-pagination.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,20 @@ type PipelinePaginationProps = {
2626
const containerStyles = css({
2727
display: 'flex',
2828
alignItems: 'center',
29-
gap: spacing[2],
29+
gap: spacing[200],
3030
});
3131

3232
const paginationStyles = css({
3333
display: 'flex',
34-
gap: spacing[1],
34+
gap: spacing[100],
3535
alignItems: 'baseline',
3636
});
3737

38+
const prevNextStyles = css({
39+
display: 'flex',
40+
alignItems: 'center',
41+
});
42+
3843
export const PipelinePagination: React.FunctionComponent<
3944
PipelinePaginationProps
4045
> = ({
@@ -56,7 +61,7 @@ export const PipelinePagination: React.FunctionComponent<
5661
<PipelinePaginationCount />
5762
</div>
5863
)}
59-
<div>
64+
<div className={prevNextStyles}>
6065
<IconButton
6166
data-testid="pipeline-pagination-prev-action"
6267
aria-label="Previous page"

packages/compass-aggregations/src/components/pipeline-results-workspace/pipeline-results-header.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,13 @@ export const PipelineResultsHeader: React.FunctionComponent<
6666
<div className={containerStyles} data-testid="pipeline-results-header">
6767
<div className={pipelineOptionsStyles}>
6868
<Overline>All Results</Overline>
69-
<PipelineOutputOptionsMenu
70-
onChangeOption={handlePipelineOutputOptionsMenuChange}
71-
/>
7269
</div>
7370
<div className={pipelinePaginationStyles}>
7471
<PipelinePagination />
72+
<PipelineOutputOptionsMenu
73+
buttonText=""
74+
onChangeOption={handlePipelineOutputOptionsMenuChange}
75+
/>
7576
<PipelineResultsViewControls
7677
value={resultsViewType}
7778
onChange={onChangeResultsView}

packages/compass-aggregations/src/components/pipeline-results-workspace/pipeline-results-view-controls.spec.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ describe('PipelineResultsViewControls', function () {
1717
);
1818
const container = screen.getByTestId('pipeline-results-view-controls');
1919
expect(container).to.exist;
20-
expect(within(container).getByText('View')).to.exist;
2120
expect(within(container).getByLabelText('Document list')).to.exist;
2221
expect(within(container).getByLabelText('JSON list')).to.exist;
2322
});

packages/compass-aggregations/src/components/pipeline-results-workspace/pipeline-results-view-controls.tsx

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
css,
44
SegmentedControl,
55
SegmentedControlOption,
6-
Overline,
76
Icon,
87
spacing,
98
useId,
@@ -29,14 +28,6 @@ const PipelineResultsViewControls: React.FunctionComponent<{
2928
className={containerStyles}
3029
data-testid="pipeline-results-view-controls"
3130
>
32-
<Overline
33-
as="label"
34-
id={labelId}
35-
htmlFor={controlId}
36-
aria-label="Show documents as"
37-
>
38-
View
39-
</Overline>
4031
<SegmentedControl
4132
id={controlId}
4233
aria-labelledby={labelId}

packages/compass-components/src/components/actions/dropdown-menu-button.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export type DropdownMenuButtonProps<Action extends string> = {
2727
activeAction?: Action;
2828
'data-testid'?: string;
2929
buttonText: string;
30-
buttonProps: ButtonProps;
30+
buttonProps: ButtonProps & React.ButtonHTMLAttributes<HTMLButtonElement>;
3131
hideOnNarrow?: boolean;
3232
};
3333

@@ -89,7 +89,6 @@ export function DropdownMenuButton<Action extends string>({
8989
}) => {
9090
return (
9191
<Button
92-
{...buttonProps}
9392
ref={menuTriggerRef}
9493
data-testid={dataTestId ? `${dataTestId}-show-actions` : undefined}
9594
onClick={(evt) => {
@@ -98,10 +97,13 @@ export function DropdownMenuButton<Action extends string>({
9897
}}
9998
rightGlyph={<Icon glyph={'CaretDown'} />}
10099
title={buttonText}
100+
{...buttonProps}
101101
>
102-
<span className={hideOnNarrow ? hiddenOnNarrowStyles : undefined}>
103-
{buttonText}
104-
</span>
102+
{buttonText && (
103+
<span className={hideOnNarrow ? hiddenOnNarrowStyles : undefined}>
104+
{buttonText}
105+
</span>
106+
)}
105107
{children}
106108
</Button>
107109
);
@@ -120,7 +122,9 @@ export function DropdownMenuButton<Action extends string>({
120122
data-testid={actionTestId(dataTestId, action)}
121123
data-action={action}
122124
data-menuitem={true}
123-
glyph={<ActionGlyph glyph={icon} size={iconSize} />}
125+
glyph={
126+
icon ? <ActionGlyph glyph={icon} size={iconSize} /> : undefined
127+
}
124128
onClick={onClick}
125129
>
126130
{label}

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ describe('CrudToolbar Component', function () {
7171
onResetClicked={noop}
7272
onUpdateButtonClicked={noop}
7373
onDeleteButtonClicked={noop}
74+
onExpandAllClicked={noop}
75+
onCollapseAllClicked={noop}
7476
openExportFileDialog={noop}
7577
outdated={false}
7678
page={0}
@@ -368,6 +370,51 @@ describe('CrudToolbar Component', function () {
368370
});
369371
});
370372

373+
describe('Output Options', function () {
374+
describe('table view', function () {
375+
it('should be disabled', function () {
376+
renderCrudToolbar({
377+
activeDocumentView: 'Table',
378+
});
379+
380+
expect(screen.getByTitle('Output Options')).to.have.attribute(
381+
'aria-disabled',
382+
'true'
383+
);
384+
});
385+
});
386+
387+
describe('other views', function () {
388+
it('should provide "Expand all documents"', function () {
389+
const onExpandAllClicked = sinon.spy();
390+
renderCrudToolbar({
391+
activeDocumentView: 'JSON',
392+
onExpandAllClicked,
393+
});
394+
395+
userEvent.click(screen.getByTitle('Output Options'));
396+
const expandAllBtn = screen.getByText('Expand all documents');
397+
expect(expandAllBtn).to.be.visible;
398+
userEvent.click(expandAllBtn);
399+
expect(onExpandAllClicked).to.have.been.called;
400+
});
401+
402+
it('should provide "Collapse all documents"', function () {
403+
const onCollapseAllClicked = sinon.spy();
404+
renderCrudToolbar({
405+
activeDocumentView: 'JSON',
406+
onCollapseAllClicked,
407+
});
408+
409+
userEvent.click(screen.getByTitle('Output Options'));
410+
const collapseAllBtn = screen.getByText('Collapse all documents');
411+
expect(collapseAllBtn).to.be.visible;
412+
userEvent.click(collapseAllBtn);
413+
expect(onCollapseAllClicked).to.have.been.called;
414+
});
415+
});
416+
});
417+
371418
it('should not render the outdated message', function () {
372419
renderCrudToolbar();
373420

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

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,33 +32,42 @@ const crudToolbarStyles = css({
3232
display: 'flex',
3333
flexDirection: 'column',
3434
alignItems: 'center',
35-
gap: spacing[3],
36-
padding: spacing[3],
35+
gap: spacing[300],
36+
padding: spacing[300],
3737
});
3838

3939
const crudBarStyles = css({
4040
width: '100%',
4141
display: 'flex',
42-
gap: spacing[2],
42+
gap: spacing[200],
4343
justifyContent: 'space-between',
4444
});
4545

4646
const toolbarLeftActionStyles = css({
4747
display: 'flex',
4848
alignItems: 'center',
49-
gap: spacing[2],
49+
gap: spacing[200],
5050
});
5151

5252
const toolbarRightActionStyles = css({
5353
display: 'flex',
5454
alignItems: 'center',
55-
gap: spacing[2],
55+
gap: spacing[200],
56+
});
57+
58+
const prevNextStyles = css({
59+
display: 'flex',
60+
alignItems: 'center',
5661
});
5762

5863
const exportCollectionButtonStyles = css({
5964
whiteSpace: 'nowrap',
6065
});
6166

67+
const outputOptionsButtonStyles = css({
68+
whiteSpace: 'nowrap',
69+
});
70+
6271
const docsPerPageOptionStyles = css({
6372
width: spacing[1600] + spacing[300],
6473
});
@@ -69,6 +78,12 @@ const exportDataActions: MenuAction<ExportDataOption>[] = [
6978
{ action: 'export-full-collection', label: 'Export the full collection' },
7079
];
7180

81+
type ExpandControlsOption = 'expand-all' | 'collapse-all';
82+
const expandControlsOptions: MenuAction<ExpandControlsOption>[] = [
83+
{ action: 'expand-all', label: 'Expand all documents' },
84+
{ action: 'collapse-all', label: 'Collapse all documents' },
85+
];
86+
7287
const OUTDATED_WARNING = `The content is outdated and no longer in sync
7388
with the current query. Press "Find" again to see the results for
7489
the current query.`;
@@ -107,6 +122,8 @@ export type CrudToolbarProps = {
107122
onResetClicked: () => void;
108123
onUpdateButtonClicked: () => void;
109124
onDeleteButtonClicked: () => void;
125+
onExpandAllClicked: () => void;
126+
onCollapseAllClicked: () => void;
110127
openExportFileDialog: (exportFullCollection?: boolean) => void;
111128
outdated: boolean;
112129
page: number;
@@ -137,6 +154,8 @@ const CrudToolbar: React.FunctionComponent<CrudToolbarProps> = ({
137154
onResetClicked,
138155
onUpdateButtonClicked,
139156
onDeleteButtonClicked,
157+
onExpandAllClicked,
158+
onCollapseAllClicked,
140159
openExportFileDialog,
141160
outdated,
142161
page,
@@ -273,7 +292,7 @@ const CrudToolbar: React.FunctionComponent<CrudToolbarProps> = ({
273292
</IconButton>
274293
)}
275294

276-
<div>
295+
<div className={prevNextStyles}>
277296
<IconButton
278297
data-testid="docs-toolbar-prev-page-btn"
279298
aria-label="Previous Page"
@@ -293,6 +312,25 @@ const CrudToolbar: React.FunctionComponent<CrudToolbarProps> = ({
293312
<Icon glyph="ChevronRight" />
294313
</IconButton>
295314
</div>
315+
316+
<DropdownMenuButton<ExpandControlsOption>
317+
data-testid="crud-export-collection"
318+
actions={expandControlsOptions}
319+
onAction={(action: ExpandControlsOption) =>
320+
action === 'expand-all'
321+
? onExpandAllClicked()
322+
: onCollapseAllClicked()
323+
}
324+
buttonText=""
325+
buttonProps={{
326+
className: outputOptionsButtonStyles,
327+
size: 'xsmall',
328+
title: 'Output Options',
329+
['aria-label']: 'Output Options',
330+
disabled: activeDocumentView === 'Table',
331+
}}
332+
/>
333+
296334
<ViewSwitcher
297335
activeView={activeDocumentView}
298336
onChange={viewSwitchHandler}

packages/compass-crud/src/components/document-list.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import type { DocumentTableViewProps } from './table-view/document-table-view';
2222
import DocumentTableView from './table-view/document-table-view';
2323
import type { CrudToolbarProps } from './crud-toolbar';
2424
import { CrudToolbar } from './crud-toolbar';
25-
import type { Document } from 'bson';
25+
import type { Document } from 'hadron-document';
2626
import type { DOCUMENTS_STATUSES } from '../constants/documents-statuses';
2727
import {
2828
DOCUMENTS_STATUS_ERROR,
@@ -512,6 +512,14 @@ const DocumentList: React.FunctionComponent<DocumentListProps> = (props) => {
512512
[view, setCurrentViewInitialScrollTop, scrollRef, viewChanged]
513513
);
514514

515+
const onExpandAllClicked = useCallback(() => {
516+
docs.forEach((doc) => !doc.expanded && doc.expand());
517+
}, [docs]);
518+
519+
const onCollapseAllClicked = useCallback(() => {
520+
docs.forEach((doc) => doc.expanded && doc.collapse());
521+
}, [docs]);
522+
515523
return (
516524
<div className={documentsContainerStyles} data-testid="compass-crud">
517525
<WorkspaceContainer
@@ -533,6 +541,8 @@ const DocumentList: React.FunctionComponent<DocumentListProps> = (props) => {
533541
onResetClicked={onResetClicked}
534542
onUpdateButtonClicked={onUpdateButtonClicked}
535543
onDeleteButtonClicked={onDeleteButtonClicked}
544+
onExpandAllClicked={onExpandAllClicked}
545+
onCollapseAllClicked={onCollapseAllClicked}
536546
openExportFileDialog={openExportFileDialog}
537547
outdated={outdated}
538548
readonly={!isEditable}

0 commit comments

Comments
 (0)