Skip to content
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
@@ -0,0 +1,36 @@
import React from 'react';
import { Modal, H3, Body } from '@mongodb-js/compass-components';
import { connect } from 'react-redux';

import type { RootState } from '../../modules';
import { closeExplainModal } from '../../modules/explain';

type PipelineExplainProps = {
isModalOpen: boolean;
onCloseModal: () => void;
};

export const PipelineExplain: React.FunctionComponent<PipelineExplainProps> = ({
isModalOpen,
onCloseModal,
}) => {
return (
<Modal
setOpen={onCloseModal}
open={isModalOpen}
data-testid="pipeline-explain-modal"
>
<H3>Explain</H3>
<Body>Implementation in progress ...</Body>
</Modal>
);
};

const mapState = ({ explain: { isModalOpen } }: RootState) => ({
isModalOpen: isModalOpen,
});

const mapDispatch = {
onCloseModal: closeExplainModal,
};
export default connect(mapState, mapDispatch)(PipelineExplain);
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,21 @@ import type { SinonSpy } from 'sinon';
import { PipelineActions } from './pipeline-actions';

const initialEnableExport = process.env.COMPASS_ENABLE_AGGREGATION_EXPORT;
const initialEnableExplain = process.env.COMPASS_ENABLE_AGGREGATION_EXPLAIN;

describe('PipelineActions', function () {
describe('options visible', function () {
let onRunAggregationSpy: SinonSpy;
let onToggleOptionsSpy: SinonSpy;
let onExportAggregationResultsSpy: SinonSpy;
let onExplainAggregationSpy: SinonSpy;
beforeEach(function () {
process.env.COMPASS_ENABLE_AGGREGATION_EXPORT = 'true';
process.env.COMPASS_ENABLE_AGGREGATION_EXPLAIN = 'true';
onRunAggregationSpy = spy();
onToggleOptionsSpy = spy();
onExportAggregationResultsSpy = spy();
onExplainAggregationSpy = spy();
render(
<PipelineActions
isOptionsVisible={true}
Expand All @@ -27,12 +31,16 @@ describe('PipelineActions', function () {
onRunAggregation={onRunAggregationSpy}
onToggleOptions={onToggleOptionsSpy}
onExportAggregationResults={onExportAggregationResultsSpy}
isExplainButtonDisabled={false}
onExplainAggregation={onExplainAggregationSpy}
onUpdateView={() => {}}
/>
);
});

afterEach(function () {
process.env.COMPASS_ENABLE_AGGREGATION_EXPORT = initialEnableExport;
process.env.COMPASS_ENABLE_AGGREGATION_EXPLAIN = initialEnableExplain;
});

it('calls onRunAggregation callback on click', function () {
Expand All @@ -55,6 +63,15 @@ describe('PipelineActions', function () {
expect(onExportAggregationResultsSpy.calledOnce).to.be.true;
});

it('calls onExplainAggregation on click', function () {
const button = screen.getByTestId(
'pipeline-toolbar-explain-aggregation-button'
);
expect(button).to.exist;
userEvent.click(button);
expect(onExplainAggregationSpy.calledOnce).to.be.true;
});

it('calls onToggleOptions on click', function () {
const button = screen.getByTestId('pipeline-toolbar-options-button');
expect(button).to.exist;
Expand All @@ -81,6 +98,8 @@ describe('PipelineActions', function () {
onRunAggregation={onRunAggregationSpy}
onToggleOptions={onToggleOptionsSpy}
onExportAggregationResults={() => {}}
onUpdateView={() => {}}
onExplainAggregation={() => {}}
/>
);
});
Expand All @@ -100,12 +119,16 @@ describe('PipelineActions', function () {
describe('disables actions when pipeline is invalid', function () {
let onRunAggregationSpy: SinonSpy;
let onExportAggregationResultsSpy: SinonSpy;
let onExplainAggregationSpy: SinonSpy;
beforeEach(function () {
process.env.COMPASS_ENABLE_AGGREGATION_EXPORT = 'true';
process.env.COMPASS_ENABLE_AGGREGATION_EXPLAIN = 'true';
onRunAggregationSpy = spy();
onExportAggregationResultsSpy = spy();
onExplainAggregationSpy = spy();
render(
<PipelineActions
isExplainButtonDisabled={true}
isExportButtonDisabled={true}
isRunButtonDisabled={true}
isOptionsVisible={true}
Expand All @@ -114,12 +137,15 @@ describe('PipelineActions', function () {
onRunAggregation={onRunAggregationSpy}
onToggleOptions={() => {}}
onExportAggregationResults={onExportAggregationResultsSpy}
onExplainAggregation={onExplainAggregationSpy}
onUpdateView={() => {}}
/>
);
});

afterEach(function () {
process.env.COMPASS_ENABLE_AGGREGATION_EXPORT = initialEnableExport;
process.env.COMPASS_ENABLE_AGGREGATION_EXPLAIN = initialEnableExplain;
});

it('run action disabled', function () {
Expand All @@ -143,5 +169,17 @@ describe('PipelineActions', function () {
});
expect(onExportAggregationResultsSpy.calledOnce).to.be.false;
});

it('explain action disabled', function () {
const button = screen.getByTestId(
'pipeline-toolbar-explain-aggregation-button'
);
expect(button.getAttribute('disabled')).to.exist;

userEvent.click(button, undefined, {
skipPointerEventsCheck: true,
});
expect(onExplainAggregationSpy.calledOnce).to.be.false;
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from '../../../modules/aggregation';
import { isEmptyishStage } from '../../../modules/stage';
import { updateView } from '../../../modules/update-view';
import { openExplainModal } from '../../../modules/explain';

const containerStyles = css({
display: 'flex',
Expand Down Expand Up @@ -51,6 +52,9 @@ type PipelineActionsProps = {
isUpdateViewButtonDisabled?: boolean;
onUpdateView: () => void;

isExplainButtonDisabled?: boolean;
onExplainAggregation: () => void;

isOptionsVisible?: boolean;
onToggleOptions: () => void;
};
Expand All @@ -63,22 +67,26 @@ export const PipelineActions: React.FunctionComponent<PipelineActionsProps> = ({
isExportButtonDisabled,
showUpdateViewButton,
isUpdateViewButtonDisabled,
isExplainButtonDisabled,
onUpdateView,
onRunAggregation,
onToggleOptions,
onExportAggregationResults,
onExplainAggregation,
}) => {
const optionsIcon = isOptionsVisible ? 'CaretDown' : 'CaretRight';
const showExportButton =
process?.env?.COMPASS_ENABLE_AGGREGATION_EXPORT === 'true' &&
_showExportButton;
const showExplainButton =
process?.env?.COMPASS_ENABLE_AGGREGATION_EXPLAIN === 'true';
const optionsLabel = isOptionsVisible ? 'Less Options' : 'More Options';
return (
<div className={containerStyles}>
{showUpdateViewButton && (
<Button
aria-label="Update view"
data-testid="pipeline-toolbar-export-aggregation-button"
data-testid="pipeline-toolbar-update-view-aggregation-button"
variant="primary"
size="small"
onClick={onUpdateView}
Expand All @@ -87,6 +95,18 @@ export const PipelineActions: React.FunctionComponent<PipelineActionsProps> = ({
Update view
</Button>
)}
{showExplainButton && (
<Button
aria-label="Explain aggregation"
data-testid="pipeline-toolbar-explain-aggregation-button"
variant="default"
size="small"
onClick={onExplainAggregation}
disabled={isExplainButtonDisabled}
>
Explain
</Button>
)}
{!showUpdateViewButton && showExportButton && (
<Button
aria-label="Export aggregation"
Expand Down Expand Up @@ -146,6 +166,7 @@ const mapState = ({ pipeline, editViewName, isModified }: RootState) => {

return {
isRunButtonDisabled: isPipelineInvalid || isStageStateEmpty,
isExplainButtonDisabled: isPipelineInvalid,
isExportButtonDisabled:
isMergeOrOutPipeline || isPipelineInvalid || isStageStateEmpty,
showUpdateViewButton: Boolean(editViewName),
Expand All @@ -158,6 +179,7 @@ const mapDispatch = {
onUpdateView: updateView,
onRunAggregation: runAggregation,
onExportAggregationResults: exportAggregationResults,
onExplainAggregation: openExplainModal,
};

export default connect(mapState, mapDispatch)(PipelineActions);
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import ConfirmNewPipeline from './modals/confirm-new-pipeline';
import styles from './pipeline.module.less';

import PipelineToolbar from '../pipeline-toolbar';
import PipelineExplain from '../pipeline-explain';
import PipelineBuilderWorkspace from '../pipeline-builder-workspace';
import PipelineResultsWorkspace from '../pipeline-results-workspace';
import {
Expand Down Expand Up @@ -126,7 +127,7 @@ class Pipeline extends PureComponent {
toggleInputDocumentsCollapsed: PropTypes.func.isRequired,
refreshInputDocuments: PropTypes.func.isRequired,
showExportButton: PropTypes.bool.isRequired,
showRunButton: PropTypes.bool.isRequired
showRunButton: PropTypes.bool.isRequired,
};

static defaultProps = {
Expand Down Expand Up @@ -362,6 +363,9 @@ class Pipeline extends PureComponent {
{this.renderModifyingViewSourceError()}
{this.renderPipelineWorkspace()}
{this.renderSavePipeline()}
{process?.env?.COMPASS_ENABLE_AGGREGATION_EXPLAIN === 'true' && (
<PipelineExplain />
)}
<Settings
isAtlasDeployed={this.props.isAtlasDeployed}
isExpanded={this.props.settings.isExpanded}
Expand Down
51 changes: 51 additions & 0 deletions packages/compass-aggregations/src/modules/explain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { Reducer } from 'redux';

export enum ActionTypes {
ModalOpened = 'compass-aggregations/modalOpened',
ModalClosed = 'compass-aggregations/modalClosed',
}

type ModalOpenedAction = {
type: ActionTypes.ModalOpened;
};

type ModalClosedAction = {
type: ActionTypes.ModalClosed;
};

export type Actions =
| ModalOpenedAction
| ModalClosedAction;

export type State = {
isModalOpen: boolean;
};

export const INITIAL_STATE: State = {
isModalOpen: false,
};

const reducer: Reducer<State, Actions> = (state = INITIAL_STATE, action) => {
switch (action.type) {
case ActionTypes.ModalOpened:
return {
isModalOpen: true,
};
case ActionTypes.ModalClosed:
return {
isModalOpen: false,
};
default:
return state;
}
};

export const openExplainModal = (): ModalOpenedAction => ({
type: ActionTypes.ModalOpened,
});

export const closeExplainModal = (): ModalClosedAction => ({
type: ActionTypes.ModalClosed,
});

export default reducer;
11 changes: 9 additions & 2 deletions packages/compass-aggregations/src/modules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ import aggregation, {
import countDocuments, {
INITIAL_STATE as COUNT_INITIAL_STATE
} from './count-documents';

import explain, {
INITIAL_STATE as EXPLAIN_INITIAL_STATE
} from './explain';

import workspace, {
INITIAL_STATE as WORKSPACE_INITIAL_STATE
} from './workspace';
Expand Down Expand Up @@ -176,6 +181,7 @@ export const INITIAL_STATE = {
aggregation: AGGREGATION_INITIAL_STATE,
workspace: WORKSPACE_INITIAL_STATE,
countDocuments: COUNT_INITIAL_STATE,
explain: EXPLAIN_INITIAL_STATE,
};

/**
Expand Down Expand Up @@ -255,7 +261,8 @@ const appReducer = combineReducers({
aggregation,
workspace,
countDocuments,
aggregationWorkspaceId
aggregationWorkspaceId,
explain,
});

export type RootState = ReturnType<typeof appReducer>;
Expand Down Expand Up @@ -291,7 +298,7 @@ const doNamespaceChanged = (state: RootState, action: AnyAction) => {
*
* @returns {Object} The new state.
*/
const doReset= (state: RootState) => ({
const doReset = (state: RootState) => ({
...INITIAL_STATE,
aggregationWorkspaceId: state.aggregationWorkspaceId
});
Expand Down
1 change: 1 addition & 0 deletions packages/compass-aggregations/src/stores/store.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ describe('Aggregation Store', function() {
aggregation: INITIAL_STATE.aggregation,
workspace: INITIAL_STATE.workspace,
countDocuments: INITIAL_STATE.countDocuments,
explain: INITIAL_STATE.explain,
});
});
});
Expand Down
1 change: 1 addition & 0 deletions packages/compass/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ declare module 'process' {
*/
COMPASS_SHOW_NEW_AGGREGATION_TOOLBAR?: 'true';
COMPASS_ENABLE_AGGREGATION_EXPORT?: 'true' | 'false';
COMPASS_ENABLE_AGGREGATION_EXPLAIN?: 'true' | 'false';

/**
* Permanent feature flag for debugging.
Expand Down