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(search-indexes): add search index signals COMPASS-7176 #4972

Merged
merged 6 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
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(
() =>
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,
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to pass the stage here, if we pass also all stages and stageIndex? Or it can be accessed directly in the FocusModeModalHeader component?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The stages we pass to the component are mapped with only index and operator properties. The stage passed here is full 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
Loading