Skip to content

Commit 38dea55

Browse files
authored
feat(stage-wizard): setup button and panel in aggregations toolbar COMPASS-6653 (#4225)
1 parent dca77c3 commit 38dea55

File tree

13 files changed

+296
-37
lines changed

13 files changed

+296
-37
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React from 'react';
2+
import type { ComponentProps } from 'react';
3+
import { AggregationSidePanel } from './index';
4+
import { render, screen } from '@testing-library/react';
5+
import { expect } from 'chai';
6+
import configureStore from '../../../test/configure-store';
7+
import { Provider } from 'react-redux';
8+
import sinon from 'sinon';
9+
10+
const renderAggregationSidePanel = (
11+
props: Partial<ComponentProps<typeof AggregationSidePanel>> = {}
12+
) => {
13+
return render(
14+
<Provider store={configureStore()}>
15+
<AggregationSidePanel onCloseSidePanel={() => {}} {...props} />
16+
</Provider>
17+
);
18+
};
19+
20+
describe('aggregation side panel', function () {
21+
describe('header', function () {
22+
it('renders title', function () {
23+
renderAggregationSidePanel();
24+
expect(screen.getByText('Stage Wizard')).to.exist;
25+
});
26+
it('renders close button', function () {
27+
renderAggregationSidePanel();
28+
expect(screen.getByLabelText('Hide Side Panel')).to.exist;
29+
});
30+
it('calls onCloseSidePanel when close button is clicked', function () {
31+
const onCloseSidePanel = sinon.spy();
32+
renderAggregationSidePanel({ onCloseSidePanel });
33+
screen.getByLabelText('Hide Side Panel').click();
34+
expect(onCloseSidePanel).to.have.been.calledOnce;
35+
});
36+
});
37+
});
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import React from 'react';
2+
import {
3+
Body,
4+
css,
5+
cx,
6+
Icon,
7+
IconButton,
8+
KeylineCard,
9+
palette,
10+
spacing,
11+
useDarkMode,
12+
} from '@mongodb-js/compass-components';
13+
import { connect } from 'react-redux';
14+
import { toggleSidePanel } from '../../modules/side-panel';
15+
16+
const containerStyles = css({
17+
height: '100%',
18+
paddingLeft: spacing[2],
19+
paddingRight: spacing[2],
20+
paddingTop: spacing[1],
21+
borderBottomRightRadius: 0,
22+
borderBottomLeftRadius: 0,
23+
borderBottom: 'none',
24+
backgroundColor: palette.gray.light3,
25+
});
26+
27+
const darkModeContainerStyles = css({
28+
backgroundColor: palette.gray.dark3,
29+
});
30+
31+
const headerStyles = css({
32+
display: 'flex',
33+
alignItems: 'center',
34+
});
35+
36+
const closeButtonStyles = css({
37+
marginLeft: 'auto',
38+
});
39+
40+
const titleStylesDark = css({
41+
color: palette.green.light2,
42+
});
43+
44+
const titleStylesLight = css({
45+
color: palette.green.dark2,
46+
});
47+
48+
const contentStyles = css({
49+
display: 'flex',
50+
justifyContent: 'center',
51+
alignItems: 'center',
52+
height: '100%',
53+
});
54+
55+
type AggregationSidePanelProps = {
56+
onCloseSidePanel: () => void;
57+
};
58+
59+
export const AggregationSidePanel = ({
60+
onCloseSidePanel,
61+
}: AggregationSidePanelProps) => {
62+
const darkMode = useDarkMode();
63+
return (
64+
<KeylineCard
65+
data-testid="aggregation-side-panel"
66+
className={cx(containerStyles, darkMode && darkModeContainerStyles)}
67+
>
68+
<div className={headerStyles}>
69+
<Body
70+
weight="medium"
71+
className={darkMode ? titleStylesDark : titleStylesLight}
72+
>
73+
Stage Wizard
74+
</Body>
75+
<IconButton
76+
className={closeButtonStyles}
77+
title="Hide Side Panel"
78+
aria-label="Hide Side Panel"
79+
onClick={() => onCloseSidePanel()}
80+
>
81+
<Icon glyph="X" />
82+
</IconButton>
83+
</div>
84+
<div className={contentStyles}>
85+
<Body>Feature in progress ...</Body>
86+
</div>
87+
</KeylineCard>
88+
);
89+
};
90+
91+
export default connect(null, {
92+
onCloseSidePanel: toggleSidePanel,
93+
})(AggregationSidePanel);

packages/compass-aggregations/src/components/pipeline-builder-workspace/pipeline-as-text-workspace/index.tsx

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
import React from 'react';
2-
import {
3-
css,
4-
spacing,
5-
palette,
6-
useDarkMode,
7-
cx,
8-
} from '@mongodb-js/compass-components';
2+
import { css, KeylineCard } from '@mongodb-js/compass-components';
93
import { connect } from 'react-redux';
104
import { Resizable } from 're-resizable';
115

@@ -16,19 +10,7 @@ import type { RootState } from '../../../modules';
1610

1711
const containerStyles = css({
1812
display: 'flex',
19-
marginLeft: spacing[3],
20-
marginRight: spacing[3],
2113
height: '100%',
22-
23-
// align with stage editor design
24-
border: `1px solid ${palette.gray.light2}`,
25-
borderRadius: '4px',
26-
boxShadow: `1px 1px 1px ${palette.gray.light2}`,
27-
});
28-
29-
const containerDarkStyles = css({
30-
borderColor: palette.gray.dark2,
31-
boxShadow: `1px 1px 1px ${palette.gray.dark2}`,
3214
});
3315

3416
const noPreviewEditorStyles = css({
@@ -50,25 +32,20 @@ const containerDataTestId = 'pipeline-as-text-workspace';
5032
export const PipelineAsTextWorkspace: React.FunctionComponent<
5133
PipelineAsTextWorkspaceProps
5234
> = ({ isAutoPreview }) => {
53-
const darkMode = useDarkMode();
54-
5535
if (!isAutoPreview) {
5636
return (
57-
<div
37+
<KeylineCard
5838
data-testid={containerDataTestId}
59-
className={cx(containerStyles, darkMode && containerDarkStyles)}
39+
className={containerStyles}
6040
>
6141
<div className={noPreviewEditorStyles}>
6242
<PipelineEditor />
6343
</div>
64-
</div>
44+
</KeylineCard>
6545
);
6646
}
6747
return (
68-
<div
69-
data-testid={containerDataTestId}
70-
className={cx(containerStyles, darkMode && containerDarkStyles)}
71-
>
48+
<KeylineCard data-testid={containerDataTestId} className={containerStyles}>
7249
<Resizable
7350
defaultSize={{
7451
width: '50%',
@@ -88,7 +65,7 @@ export const PipelineAsTextWorkspace: React.FunctionComponent<
8865
<div className={resultsStyles}>
8966
<PipelinePreview />
9067
</div>
91-
</div>
68+
</KeylineCard>
9269
);
9370
};
9471

packages/compass-aggregations/src/components/pipeline-builder-workspace/pipeline-as-text-workspace/pipeline-editor.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const containerStyles = css({
3232
paddingBottom: spacing[2],
3333
gap: spacing[2],
3434
marginRight: spacing[1],
35+
borderRadius: spacing[2],
3536
});
3637

3738
const containerDarkStyles = css({

packages/compass-aggregations/src/components/pipeline-builder-workspace/pipeline-builder-ui-workspace.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
moveStage,
1111
} from '../../modules/pipeline-builder/stage-editor';
1212
import type { RootState } from '../../modules';
13-
import { css, spacing } from '@mongodb-js/compass-components';
13+
import { css } from '@mongodb-js/compass-components';
1414

1515
import {
1616
DndContext,
@@ -35,8 +35,6 @@ const pipelineWorkspaceStyles = css({
3535
flexDirection: 'column',
3636
width: '100%',
3737
flexGrow: 1,
38-
paddingRight: spacing[3],
39-
paddingLeft: spacing[3],
4038
});
4139

4240
const stageContainerStyles = css({

packages/compass-aggregations/src/components/pipeline-builder-workspace/pipeline-builder-workspace.spec.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ const renderBuilderWorkspace = (
1111
) => {
1212
return render(
1313
<Provider store={configureStore()}>
14-
<PipelineBuilderWorkspace pipelineMode="as-text" {...props} />
14+
<PipelineBuilderWorkspace
15+
isPanelOpen={false}
16+
pipelineMode="as-text"
17+
{...props}
18+
/>
1519
</Provider>
1620
);
1721
};
@@ -30,4 +34,18 @@ describe('PipelineBuilderWorkspace', function () {
3034
expect(within(container).getByTestId('pipeline-as-text-workspace')).to
3135
.exist;
3236
});
37+
38+
it('renders side panel when enabled in builder ui mode', function () {
39+
renderBuilderWorkspace({ pipelineMode: 'builder-ui', isPanelOpen: true });
40+
const container = screen.getByTestId('pipeline-builder-workspace');
41+
expect(within(container).getByTestId('aggregation-side-panel')).to.exist;
42+
});
43+
44+
it('does not render side panel when enabled in as text mode', function () {
45+
renderBuilderWorkspace({ pipelineMode: 'as-text', isPanelOpen: true });
46+
const container = screen.getByTestId('pipeline-builder-workspace');
47+
expect(() => {
48+
within(container).getByTestId('aggregation-side-panel');
49+
}).to.throw;
50+
});
3351
});
Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,77 @@
11
import React from 'react';
22
import { connect } from 'react-redux';
3-
import { css, spacing } from '@mongodb-js/compass-components';
3+
import { css, cx, spacing } from '@mongodb-js/compass-components';
44
import type { RootState } from '../../modules';
55
import type { PipelineMode } from '../../modules/pipeline-builder/pipeline-mode';
66
import PipelineBuilderUIWorkspace from './pipeline-builder-ui-workspace';
77
import PipelineAsTextWorkspace from './pipeline-as-text-workspace';
8+
import AggregationSidePanel from '../aggregation-side-panel';
89

910
const containerStyles = css({
11+
display: 'flex',
12+
overflow: 'hidden',
13+
paddingRight: spacing[3],
14+
paddingLeft: spacing[3],
15+
gap: spacing[2],
1016
height: '100%',
17+
});
18+
19+
const workspaceStyles = css({
1120
paddingBottom: spacing[3],
21+
width: '100%',
22+
overflow: 'auto',
23+
});
24+
25+
const workspaceWithSidePanelEnabledStyles = css({
26+
width: '75%',
27+
});
28+
29+
const sidePanelStyles = css({
30+
width: '25%',
1231
});
1332

1433
type PipelineBuilderWorkspaceProps = {
1534
pipelineMode: PipelineMode;
35+
isPanelOpen: boolean;
1636
};
1737

1838
export const PipelineBuilderWorkspace: React.FunctionComponent<
1939
PipelineBuilderWorkspaceProps
20-
> = ({ pipelineMode }) => {
40+
> = ({ pipelineMode, isPanelOpen }) => {
2141
const workspace =
2242
pipelineMode === 'builder-ui' ? (
2343
<PipelineBuilderUIWorkspace />
2444
) : (
2545
<PipelineAsTextWorkspace />
2646
);
47+
48+
const isSidePanelEnabled = isPanelOpen && pipelineMode === 'builder-ui';
49+
2750
return (
2851
<div className={containerStyles} data-testid="pipeline-builder-workspace">
29-
{workspace}
52+
<div
53+
className={cx(
54+
workspaceStyles,
55+
isSidePanelEnabled && workspaceWithSidePanelEnabledStyles
56+
)}
57+
>
58+
{workspace}
59+
</div>
60+
{isSidePanelEnabled && (
61+
<div className={sidePanelStyles}>
62+
<AggregationSidePanel />
63+
</div>
64+
)}
3065
</div>
3166
);
3267
};
3368

34-
const mapState = ({ pipelineBuilder: { pipelineMode } }: RootState) => ({
69+
const mapState = ({
70+
pipelineBuilder: { pipelineMode },
71+
sidePanel: { isPanelOpen },
72+
}: RootState) => ({
3573
pipelineMode,
74+
isPanelOpen,
3675
});
3776

3877
export default connect(mapState)(PipelineBuilderWorkspace);

packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/pipeline-extra-settings.spec.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const renderPipelineExtraSettings = (
1919
onToggleAutoPreview={() => {}}
2020
onChangePipelineMode={() => {}}
2121
onToggleSettings={() => {}}
22+
onToggleSidePanel={() => {}}
2223
{...props}
2324
/>
2425
);
@@ -65,4 +66,28 @@ describe('PipelineExtraSettings', function () {
6566
const container = screen.getByTestId('pipeline-toolbar-extra-settings');
6667
expect(within(container).getByTestId('pipeline-builder-toggle')).to.exist;
6768
});
69+
70+
it('calls onToggleSidePanel when clicked', function () {
71+
const onToggleSidePanelSpy = spy();
72+
renderPipelineExtraSettings({ onToggleSidePanel: onToggleSidePanelSpy });
73+
const container = screen.getByTestId('pipeline-toolbar-extra-settings');
74+
const button = within(container).getByTestId(
75+
'pipeline-toolbar-side-panel-button'
76+
);
77+
expect(button).to.exist;
78+
expect(onToggleSidePanelSpy.calledOnce).to.be.false;
79+
userEvent.click(button);
80+
expect(onToggleSidePanelSpy.calledOnce).to.be.true;
81+
});
82+
83+
it('disables toggle side panel button in text mode', function () {
84+
renderPipelineExtraSettings({
85+
pipelineMode: 'as-text',
86+
});
87+
expect(
88+
screen
89+
.getByTestId('pipeline-toolbar-side-panel-button')
90+
.getAttribute('aria-disabled')
91+
).to.equal('true');
92+
});
6893
});

0 commit comments

Comments
 (0)