Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(compass-crud): expand/collapse all COMPASS-8233 #6601

Merged
merged 5 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { DropdownMenuButton } from '@mongodb-js/compass-components';
import { css, DropdownMenuButton } from '@mongodb-js/compass-components';
import type { MenuAction } from '@mongodb-js/compass-components';

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

const containerStyles = css({
display: 'flex',
alignItems: 'center',
flex: 'none',
});

const defaultTitle = 'Output Options';

export const PipelineOutputOptionsMenu: React.FunctionComponent<{
onChangeOption: (option: PipelineOutputOption) => void;
buttonText?: string;
}> = ({ onChangeOption, buttonText }) => {
return (
<DropdownMenuButton<PipelineOutputOption>
data-testid="pipeline-output-options"
actions={pipelineOptionsActions}
onAction={onChangeOption}
buttonText={buttonText ?? 'Output Options'}
buttonProps={{
size: 'xsmall',
}}
></DropdownMenuButton>
<div className={containerStyles}>
<DropdownMenuButton<PipelineOutputOption>
data-testid="pipeline-output-options"
actions={pipelineOptionsActions}
onAction={onChangeOption}
buttonText={buttonText ?? defaultTitle}
buttonProps={{
size: 'xsmall',
title: buttonText || defaultTitle,
['aria-label']: buttonText || defaultTitle,
}}
></DropdownMenuButton>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,20 @@ type PipelinePaginationProps = {
const containerStyles = css({
display: 'flex',
alignItems: 'center',
gap: spacing[2],
gap: spacing[200],
});

const paginationStyles = css({
display: 'flex',
gap: spacing[1],
gap: spacing[100],
alignItems: 'baseline',
});

const prevNextStyles = css({
display: 'flex',
alignItems: 'center',
});

export const PipelinePagination: React.FunctionComponent<
PipelinePaginationProps
> = ({
Expand All @@ -56,7 +61,7 @@ export const PipelinePagination: React.FunctionComponent<
<PipelinePaginationCount />
</div>
)}
<div>
<div className={prevNextStyles}>
<IconButton
data-testid="pipeline-pagination-prev-action"
aria-label="Previous page"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,13 @@ export const PipelineResultsHeader: React.FunctionComponent<
<div className={containerStyles} data-testid="pipeline-results-header">
<div className={pipelineOptionsStyles}>
<Overline>All Results</Overline>
<PipelineOutputOptionsMenu
onChangeOption={handlePipelineOutputOptionsMenuChange}
/>
</div>
<div className={pipelinePaginationStyles}>
<PipelinePagination />
<PipelineOutputOptionsMenu
buttonText=""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should that default value be revisited?

Copy link
Contributor Author

@paula-stacho paula-stacho Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be. There are some other usages and I couldn't find them, that's why I chose not to change the default behaviour

onChangeOption={handlePipelineOutputOptionsMenuChange}
/>
<PipelineResultsViewControls
value={resultsViewType}
onChange={onChangeResultsView}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ describe('PipelineResultsViewControls', function () {
);
const container = screen.getByTestId('pipeline-results-view-controls');
expect(container).to.exist;
expect(within(container).getByText('View')).to.exist;
expect(within(container).getByLabelText('Document list')).to.exist;
expect(within(container).getByLabelText('JSON list')).to.exist;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
css,
SegmentedControl,
SegmentedControlOption,
Overline,
Icon,
spacing,
useId,
Expand All @@ -29,14 +28,6 @@ const PipelineResultsViewControls: React.FunctionComponent<{
className={containerStyles}
data-testid="pipeline-results-view-controls"
>
<Overline
as="label"
id={labelId}
htmlFor={controlId}
aria-label="Show documents as"
>
View
</Overline>
<SegmentedControl
id={controlId}
aria-labelledby={labelId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export type DropdownMenuButtonProps<Action extends string> = {
activeAction?: Action;
'data-testid'?: string;
buttonText: string;
buttonProps: ButtonProps;
buttonProps: ButtonProps & React.ButtonHTMLAttributes<HTMLButtonElement>;
hideOnNarrow?: boolean;
};

Expand Down Expand Up @@ -89,7 +89,6 @@ export function DropdownMenuButton<Action extends string>({
}) => {
return (
<Button
{...buttonProps}
ref={menuTriggerRef}
data-testid={dataTestId ? `${dataTestId}-show-actions` : undefined}
onClick={(evt) => {
Expand All @@ -98,10 +97,13 @@ export function DropdownMenuButton<Action extends string>({
}}
rightGlyph={<Icon glyph={'CaretDown'} />}
title={buttonText}
{...buttonProps}
>
<span className={hideOnNarrow ? hiddenOnNarrowStyles : undefined}>
{buttonText}
</span>
{buttonText && (
<span className={hideOnNarrow ? hiddenOnNarrowStyles : undefined}>
{buttonText}
</span>
)}
{children}
</Button>
);
Expand All @@ -120,7 +122,9 @@ export function DropdownMenuButton<Action extends string>({
data-testid={actionTestId(dataTestId, action)}
data-action={action}
data-menuitem={true}
glyph={<ActionGlyph glyph={icon} size={iconSize} />}
glyph={
icon ? <ActionGlyph glyph={icon} size={iconSize} /> : undefined
}
onClick={onClick}
>
{label}
Expand Down
47 changes: 47 additions & 0 deletions packages/compass-crud/src/components/crud-toolbar.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ describe('CrudToolbar Component', function () {
onResetClicked={noop}
onUpdateButtonClicked={noop}
onDeleteButtonClicked={noop}
onExpandAllClicked={noop}
onCollapseAllClicked={noop}
openExportFileDialog={noop}
outdated={false}
page={0}
Expand Down Expand Up @@ -368,6 +370,51 @@ describe('CrudToolbar Component', function () {
});
});

describe('Output Options', function () {
describe('table view', function () {
it('should be disabled', function () {
renderCrudToolbar({
activeDocumentView: 'Table',
});

expect(screen.getByTitle('Output Options')).to.have.attribute(
'aria-disabled',
'true'
);
});
});

describe('other views', function () {
it('should provide "Expand all documents"', function () {
const onExpandAllClicked = sinon.spy();
renderCrudToolbar({
activeDocumentView: 'JSON',
onExpandAllClicked,
});

userEvent.click(screen.getByTitle('Output Options'));
const expandAllBtn = screen.getByText('Expand all documents');
expect(expandAllBtn).to.be.visible;
userEvent.click(expandAllBtn);
expect(onExpandAllClicked).to.have.been.called;
});

it('should provide "Collapse all documents"', function () {
const onCollapseAllClicked = sinon.spy();
renderCrudToolbar({
activeDocumentView: 'JSON',
onCollapseAllClicked,
});

userEvent.click(screen.getByTitle('Output Options'));
const collapseAllBtn = screen.getByText('Collapse all documents');
expect(collapseAllBtn).to.be.visible;
userEvent.click(collapseAllBtn);
expect(onCollapseAllClicked).to.have.been.called;
});
});
});

it('should not render the outdated message', function () {
renderCrudToolbar();

Expand Down
50 changes: 44 additions & 6 deletions packages/compass-crud/src/components/crud-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,33 +32,42 @@ const crudToolbarStyles = css({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: spacing[3],
padding: spacing[3],
gap: spacing[300],
padding: spacing[300],
});

const crudBarStyles = css({
width: '100%',
display: 'flex',
gap: spacing[2],
gap: spacing[200],
justifyContent: 'space-between',
});

const toolbarLeftActionStyles = css({
display: 'flex',
alignItems: 'center',
gap: spacing[2],
gap: spacing[200],
});

const toolbarRightActionStyles = css({
display: 'flex',
alignItems: 'center',
gap: spacing[2],
gap: spacing[200],
});

const prevNextStyles = css({
display: 'flex',
alignItems: 'center',
});

const exportCollectionButtonStyles = css({
whiteSpace: 'nowrap',
});

const outputOptionsButtonStyles = css({
whiteSpace: 'nowrap',
});

const docsPerPageOptionStyles = css({
width: spacing[1600] + spacing[300],
});
Expand All @@ -69,6 +78,12 @@ const exportDataActions: MenuAction<ExportDataOption>[] = [
{ action: 'export-full-collection', label: 'Export the full collection' },
];

type ExpandControlsOption = 'expand-all' | 'collapse-all';
const expandControlsOptions: MenuAction<ExpandControlsOption>[] = [
{ action: 'expand-all', label: 'Expand all documents' },
{ action: 'collapse-all', label: 'Collapse all documents' },
];

const OUTDATED_WARNING = `The content is outdated and no longer in sync
with the current query. Press "Find" again to see the results for
the current query.`;
Expand Down Expand Up @@ -107,6 +122,8 @@ export type CrudToolbarProps = {
onResetClicked: () => void;
onUpdateButtonClicked: () => void;
onDeleteButtonClicked: () => void;
onExpandAllClicked: () => void;
onCollapseAllClicked: () => void;
openExportFileDialog: (exportFullCollection?: boolean) => void;
outdated: boolean;
page: number;
Expand Down Expand Up @@ -137,6 +154,8 @@ const CrudToolbar: React.FunctionComponent<CrudToolbarProps> = ({
onResetClicked,
onUpdateButtonClicked,
onDeleteButtonClicked,
onExpandAllClicked,
onCollapseAllClicked,
openExportFileDialog,
outdated,
page,
Expand Down Expand Up @@ -273,7 +292,7 @@ const CrudToolbar: React.FunctionComponent<CrudToolbarProps> = ({
</IconButton>
)}

<div>
<div className={prevNextStyles}>
<IconButton
data-testid="docs-toolbar-prev-page-btn"
aria-label="Previous Page"
Expand All @@ -293,6 +312,25 @@ const CrudToolbar: React.FunctionComponent<CrudToolbarProps> = ({
<Icon glyph="ChevronRight" />
</IconButton>
</div>

<DropdownMenuButton<ExpandControlsOption>
data-testid="crud-export-collection"
actions={expandControlsOptions}
onAction={(action: ExpandControlsOption) =>
action === 'expand-all'
? onExpandAllClicked()
: onCollapseAllClicked()
}
buttonText=""
buttonProps={{
className: outputOptionsButtonStyles,
size: 'xsmall',
title: 'Output Options',
['aria-label']: 'Output Options',
disabled: activeDocumentView === 'Table',
}}
/>

<ViewSwitcher
activeView={activeDocumentView}
onChange={viewSwitchHandler}
Expand Down
12 changes: 11 additions & 1 deletion packages/compass-crud/src/components/document-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import type { DocumentTableViewProps } from './table-view/document-table-view';
import DocumentTableView from './table-view/document-table-view';
import type { CrudToolbarProps } from './crud-toolbar';
import { CrudToolbar } from './crud-toolbar';
import type { Document } from 'bson';
import type { Document } from 'hadron-document';
import type { DOCUMENTS_STATUSES } from '../constants/documents-statuses';
import {
DOCUMENTS_STATUS_ERROR,
Expand Down Expand Up @@ -512,6 +512,14 @@ const DocumentList: React.FunctionComponent<DocumentListProps> = (props) => {
[view, setCurrentViewInitialScrollTop, scrollRef, viewChanged]
);

const onExpandAllClicked = useCallback(() => {
docs.forEach((doc) => !doc.expanded && doc.expand());
}, [docs]);

const onCollapseAllClicked = useCallback(() => {
docs.forEach((doc) => doc.expanded && doc.collapse());
}, [docs]);

return (
<div className={documentsContainerStyles} data-testid="compass-crud">
<WorkspaceContainer
Expand All @@ -533,6 +541,8 @@ const DocumentList: React.FunctionComponent<DocumentListProps> = (props) => {
onResetClicked={onResetClicked}
onUpdateButtonClicked={onUpdateButtonClicked}
onDeleteButtonClicked={onDeleteButtonClicked}
onExpandAllClicked={onExpandAllClicked}
onCollapseAllClicked={onCollapseAllClicked}
openExportFileDialog={openExportFileDialog}
outdated={outdated}
readonly={!isEditable}
Expand Down
Loading