Skip to content

Commit

Permalink
feat(search-indexes): add search index signals COMPASS-7176 (#4972)
Browse files Browse the repository at this point in the history
  • Loading branch information
mabaasit authored Oct 12, 2023
1 parent 80ce9c1 commit 0420416
Show file tree
Hide file tree
Showing 18 changed files with 252 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ describe('FocusModeModalHeader', function () {
return render(
<FocusModeModalHeader
isEnabled
env={'on-prem'}
isSearchIndexesSupported={false}
onCreateSearchIndex={noop}
stage={{} as any}
stageIndex={0}
stages={[
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ import {
formatHotkey,
SignalPopover,
} from '@mongodb-js/compass-components';
import type { Signal } from '@mongodb-js/compass-components';
import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';
import { connect } from 'react-redux';
import type { RootState } from '../../modules';
import {
Expand All @@ -26,6 +25,8 @@ import { changeStageDisabled } from '../../modules/pipeline-builder/stage-editor
import type { StoreStage } from '../../modules/pipeline-builder/stage-editor';
import { getInsightForStage } from '../../utils/insights';
import { usePreference } from 'compass-preferences-model';
import { createSearchIndex } from '../../modules/search-indexes';
import type { ServerEnvironment } from '../../modules/env';

type Stage = {
idxInStore: number;
Expand All @@ -36,7 +37,10 @@ type FocusModeModalHeaderProps = {
stageIndex: number;
isEnabled: boolean;
stages: Stage[];
insight?: Signal;
stage?: StoreStage;
env: ServerEnvironment;
isSearchIndexesSupported: boolean;
onCreateSearchIndex: () => void;
onStageSelect: (index: number) => void;
onStageDisabledToggleClick: (index: number, newVal: boolean) => void;
onAddStageClick: (index: number) => void;
Expand Down Expand Up @@ -93,15 +97,29 @@ export const FocusModeModalHeader: React.FunctionComponent<
> = ({
stageIndex,
isEnabled,
insight,
stages,
env,
isSearchIndexesSupported,
stage,
onCreateSearchIndex,
onAddStageClick,
onStageSelect,
onStageDisabledToggleClick,
}) => {
const [menuOpen, setMenuOpen] = useState(false);
const showInsights = usePreference('showInsights', React);

const insight = useMemo(() => {
if (stage) {
return getInsightForStage(
stage,
env,
isSearchIndexesSupported,
onCreateSearchIndex
);
}
}, [stage, env, isSearchIndexesSupported, onCreateSearchIndex]);

const isFirst = stages[0].idxInStore === stageIndex;
const isLast = stages[stages.length - 1].idxInStore === stageIndex;

Expand Down Expand Up @@ -322,13 +340,16 @@ export default connect(
pipelineBuilder: {
stageEditor: { stages },
},
searchIndexes: { isSearchIndexesSupported },
} = state;
const stage = stages[stageIndex] as StoreStage;

return {
stageIndex,
isEnabled: !stage?.disabled,
insight: stage ? getInsightForStage(stage, env) : undefined,
stage,
env,
isSearchIndexesSupported,
stages: stages.reduce<Stage[]>((accumulator, stage, idxInStore) => {
if (stage.type === 'stage') {
accumulator.push({
Expand All @@ -344,5 +365,6 @@ export default connect(
onStageSelect: selectFocusModeStage,
onStageDisabledToggleClick: changeStageDisabled,
onAddStageClick: addStageInFocusMode,
onCreateSearchIndex: createSearchIndex,
}
)(FocusModeModalHeader);
Original file line number Diff line number Diff line change
@@ -1,51 +1,46 @@
import React from 'react';
import type { ComponentProps } from 'react';
import { render, screen } from '@testing-library/react';
import { expect } from 'chai';
import { Provider } from 'react-redux';
import sinon from 'sinon';

import configureStore from '../../../test/configure-store';
import { FocusMode } from './focus-mode';
import FocusMode from './focus-mode';
import { disableFocusMode, enableFocusMode } from '../../modules/focus-mode';

const renderFocusMode = (
props: Partial<ComponentProps<typeof FocusMode>> = {}
) => {
const renderFocusMode = () => {
const store = configureStore({
pipeline: [{ $match: { _id: 1 } }, { $limit: 10 }, { $out: 'out' }],
});
render(
<Provider
store={configureStore({
pipeline: [{ $match: { _id: 1 } }, { $limit: 10 }, { $out: 'out' }],
})}
>
<FocusMode
isModalOpen={true}
isAutoPreviewEnabled={true}
onCloseModal={() => {}}
{...props}
/>
<Provider store={store}>
<FocusMode />
</Provider>
);
return store;
};

describe('FocusMode', function () {
it('does not show modal when closed', function () {
renderFocusMode({ isModalOpen: false });
const store = renderFocusMode();
store.dispatch(disableFocusMode());
expect(() => {
screen.getByTestId('focus-mode-modal');
}).to.throw;
});

it('shows modal when open', function () {
renderFocusMode({ isModalOpen: true });
const store = renderFocusMode();
store.dispatch(enableFocusMode(0));
expect(screen.getByTestId('focus-mode-modal')).to.exist;
});

it('calls onCloseModal when close button is clicked', function () {
const onCloseModal = sinon.spy();
renderFocusMode({ onCloseModal, isModalOpen: true });

expect(onCloseModal).to.not.have.been.called;
it('hides modal when close button is clicked', function () {
const store = renderFocusMode();
store.dispatch(enableFocusMode(0));
screen.getByLabelText(/close modal/i).click();
expect(onCloseModal).to.have.been.calledOnce;

expect(() => {
screen.getByTestId('focus-mode-modal');
}).to.throw;
});
});
Original file line number Diff line number Diff line change
@@ -1,34 +1,25 @@
import React from 'react';
import type { ComponentProps } from 'react';
import { render, screen } from '@testing-library/react';
import { expect } from 'chai';
import { Provider } from 'react-redux';

import configureStore from '../../../test/configure-store';
import { StageToolbar } from './';
import StageToolbar from './';
import {
changeStageCollapsed,
changeStageDisabled,
} from '../../modules/pipeline-builder/stage-editor';

const renderStageToolbar = (
props: Partial<ComponentProps<typeof StageToolbar>> = {}
) => {
const renderStageToolbar = () => {
const store = configureStore({
pipeline: [{ $match: { _id: 1 } }, { $limit: 10 }, { $out: 'out' }],
});
render(
<Provider
store={configureStore({
pipeline: [{ $match: { _id: 1 } }, { $limit: 10 }, { $out: 'out' }],
})}
>
<StageToolbar
hasServerError={false}
hasSyntaxError={false}
index={0}
idxInPipeline={0}
isAutoPreviewing={false}
isCollapsed={false}
isDisabled={false}
onOpenFocusMode={() => {}}
{...props}
/>
<Provider store={store}>
<StageToolbar index={0} />
</Provider>
);
return store;
};

describe('StageToolbar', function () {
Expand All @@ -50,13 +41,15 @@ describe('StageToolbar', function () {
});
context('renders stage text', function () {
it('when stage is disabled', function () {
renderStageToolbar({ isDisabled: true });
const store = renderStageToolbar();
store.dispatch(changeStageDisabled(0, true));
expect(
screen.getByText('Stage disabled. Results not passed in the pipeline.')
).to.exist;
});
it('when stage is collapsed', function () {
renderStageToolbar({ isCollapsed: true });
const store = renderStageToolbar();
store.dispatch(changeStageCollapsed(0, true));
expect(
screen.getByText(
'A sample of the aggregated results from this stage will be shown below.'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useMemo } from 'react';
import { connect } from 'react-redux';
import {
Icon,
Expand All @@ -11,7 +11,6 @@ import {
IconButton,
SignalPopover,
} from '@mongodb-js/compass-components';
import type { Signal } from '@mongodb-js/compass-components';
import type { RootState } from '../../modules';
import ToggleStage from './toggle-stage';
import StageCollapser from './stage-collapser';
Expand All @@ -22,6 +21,8 @@ import OptionMenu from './option-menu';
import type { StoreStage } from '../../modules/pipeline-builder/stage-editor';
import { getInsightForStage } from '../../utils/insights';
import { usePreference } from 'compass-preferences-model';
import type { ServerEnvironment } from '../../modules/env';
import { createSearchIndex } from '../../modules/search-indexes';

const toolbarStyles = css({
width: '100%',
Expand Down Expand Up @@ -92,13 +93,12 @@ const rightStyles = css({

type StageToolbarProps = {
index: number;
idxInPipeline: number;
isAutoPreviewing?: boolean;
hasSyntaxError?: boolean;
hasServerError?: boolean;
isCollapsed?: boolean;
isDisabled?: boolean;
insight?: Signal;

stage: StoreStage;
env: ServerEnvironment;
isSearchIndexesSupported: boolean;
onCreateSearchIndex: () => void;

onOpenFocusMode: (index: number) => void;
onStageOperatorChange?: (
index: number,
Expand All @@ -113,40 +113,53 @@ const COLLAPSED_TEXT =

export function StageToolbar({
index,
idxInPipeline,
hasSyntaxError,
hasServerError,
isCollapsed,
isDisabled,
insight,
stage,
env,
isSearchIndexesSupported,
onCreateSearchIndex,
onOpenFocusMode,
onStageOperatorChange,
}: StageToolbarProps) {
const showInsights = usePreference('showInsights', React);
const darkMode = useDarkMode();

const insight = useMemo(
() =>
getInsightForStage(
stage,
env,
isSearchIndexesSupported,
onCreateSearchIndex
),
[stage, env, isSearchIndexesSupported, onCreateSearchIndex]
);

return (
<div
className={cx(
'stage-editor-toolbar',
toolbarStyles,
darkMode ? toolbarStylesDark : toolbarStylesLight,
hasSyntaxError && toolbarWarningStyles,
hasServerError && toolbarErrorStyles,
isCollapsed && collapsedToolbarStyles
hasSyntaxError(stage) && toolbarWarningStyles,
!!stage.serverError && toolbarErrorStyles,
stage.collapsed && collapsedToolbarStyles
)}
>
<div className={leftStyles}>
<div className={shortSpacedStyles}>
<StageCollapser index={index} />
<Body weight="medium">Stage {idxInPipeline + 1}</Body>
<Body weight="medium">Stage {stage.idxInPipeline + 1}</Body>
<StageOperatorSelect onChange={onStageOperatorChange} index={index} />
</div>
<ToggleStage index={index} />
{showInsights && insight && <SignalPopover signals={insight} />}
</div>
<div className={textStyles}>
{isDisabled ? DISABLED_TEXT : isCollapsed ? COLLAPSED_TEXT : null}
{stage.disabled
? DISABLED_TEXT
: stage.collapsed
? COLLAPSED_TEXT
: null}
</div>
<div className={rightStyles}>
<IconButton
Expand All @@ -173,19 +186,17 @@ export default connect(
pipelineBuilder: {
stageEditor: { stages },
},
searchIndexes: { isSearchIndexesSupported },
} = state;
const stage = stages[ownProps.index] as StoreStage;
return {
idxInPipeline: stage.idxInPipeline,
isAutoPreviewing: !!state.autoPreview,
hasSyntaxError: hasSyntaxError(stage),
hasServerError: !!stage.serverError,
isCollapsed: stage.collapsed,
isDisabled: stage.disabled,
insight: getInsightForStage(stage, env),
stage,
env,
isSearchIndexesSupported,
};
},
{
onOpenFocusMode: enableFocusMode,
onCreateSearchIndex: createSearchIndex,
}
)(StageToolbar);
2 changes: 2 additions & 0 deletions packages/compass-aggregations/src/modules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import focusMode from './focus-mode';
import sidePanel from './side-panel';
import collectionsFields from './collections-fields';
import insights from './insights';
import searchIndexes from './search-indexes';

/**
* The main application reducer.
Expand Down Expand Up @@ -84,6 +85,7 @@ const rootReducer = combineReducers({
sidePanel,
collectionsFields,
insights,
searchIndexes,
});

export type RootState = ReturnType<typeof rootReducer>;
Expand Down
Loading

0 comments on commit 0420416

Please sign in to comment.