Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { expect } from 'chai';
import sinon from 'sinon';

import { IndexesToolbar } from './indexes-toolbar';
import type { Document } from 'mongodb';

describe('IndexesToolbar Component', function () {
const renderIndexesToolbar = (
Expand All @@ -27,7 +26,6 @@ describe('IndexesToolbar Component', function () {
onRefreshIndexes={() => {}}
isSearchIndexesSupported={false}
isRefreshing={false}
collectionStats={{ index_count: 0, index_size: 0, pipeline: [] }}
onIndexViewChanged={() => {}}
onCreateRegularIndexClick={() => {}}
onCreateSearchIndexClick={() => {}}
Expand Down Expand Up @@ -144,21 +142,26 @@ describe('IndexesToolbar Component', function () {
});

describe('and pipeline is not queryable', function () {
it('should disable the create search index button', function () {
const pipelineMock: Document[] = [
{ $project: { newField: 'testValue' } },
];
const mockCollectionStats = {
index_count: 0,
index_size: 0,
pipeline: pipelineMock,
};
it('should hide the toolbar when there are no search indexes', function () {
renderIndexesToolbar({
isReadonlyView: true,
serverVersion: '8.1.0',
indexView: 'search-indexes',
isViewPipelineSearchQueryable: false,
hasSearchIndexes: false,
});

expect(screen.queryByTestId('indexes-toolbar')).to.not.exist;
expect(screen.queryByText('Create Search Index')).to.not.exist;
});

it('should render the create search index button disabled when there are existing search indexes', function () {
renderIndexesToolbar({
isReadonlyView: true,
serverVersion: '8.1.0',
indexView: 'search-indexes',
collectionStats: mockCollectionStats,
isViewPipelineSearchQueryable: false,
hasSearchIndexes: true,
});

expect(screen.getByText('Create Search Index')).to.be.visible;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ import { createSearchIndexOpened } from '../../modules/search-indexes';
import { createIndexOpened } from '../../modules/create-index';
import type { IndexView } from '../../modules/index-view';
import { indexViewChanged } from '../../modules/index-view';
import type { CollectionStats } from '../../modules/collection-stats';
import type { Document } from 'mongodb';
import { VIEW_PIPELINE_UTILS } from '@mongodb-js/mongodb-constants';
import { selectIsViewSearchCompatible } from '../../utils/is-view-search-compatible';
import { shouldShowIndexesToolbarButtons } from '../../utils/should-show-indexes-toolbar-buttons';
import {
useSearchActivationProgramP1,
useTelemetry,
Expand All @@ -44,6 +44,9 @@ const toolbarButtonsContainer = css({
const indexesToolbarContainerStyles = css({
padding: spacing[400],
paddingBottom: 0,
display: 'flex',
flexDirection: 'column',
gap: spacing[200],
});

const alignSelfEndStyles = css({
Expand Down Expand Up @@ -87,8 +90,8 @@ type IndexesToolbarProps = {
onCreateSearchIndexClick: () => void;
writeStateDescription?: string;
isSearchIndexesSupported: boolean;
// via withPreferences:
collectionStats: CollectionStats;
hasSearchIndexes?: boolean;
isViewPipelineSearchQueryable?: boolean;
};

export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
Expand All @@ -107,7 +110,8 @@ export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
onRefreshIndexes,
onIndexViewChanged,
serverVersion,
collectionStats,
hasSearchIndexes = false,
isViewPipelineSearchQueryable = true,
}) => {
Comment thread
Copilot marked this conversation as resolved.
const {
readWrite: preferencesReadWrite,
Expand All @@ -134,24 +138,23 @@ export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
) : (
<Icon glyph="Refresh" title="Refresh Indexes" />
);
const isViewPipelineSearchQueryable =
isReadonlyView && collectionStats?.pipeline
? VIEW_PIPELINE_UTILS.isPipelineSearchQueryable(
collectionStats.pipeline as Document[]
)
: true;

const showToolbarButtons = shouldShowIndexesToolbarButtons({
isReadonlyView,
serverVersion,
isSearchManagementActive,
isViewPipelineSearchQueryable,
hasSearchIndexes,
});
const pipelineNotSearchQueryableDescription =
'Search indexes can only be created on views containing $match stages with the $expr operator, $addFields, or $set';

return (
<div
className={indexesToolbarContainerStyles}
data-testid="indexes-toolbar-container"
>
{(!isReadonlyView ||
(VIEW_PIPELINE_UTILS.isVersionSearchCompatibleForViewsCompass(
serverVersion
) &&
isSearchManagementActive)) && (
{showToolbarButtons && (
<div data-testid="indexes-toolbar">
<div className={toolbarButtonsContainer}>
{showCreateIndexButton && (
Expand Down Expand Up @@ -402,27 +405,30 @@ export const CreateIndexButton: React.FunctionComponent<
);
};

const mapState = ({
namespace,
isWritable,
isReadonlyView,
isSearchIndexesSupported,
description,
serverVersion,
searchIndexes,
indexView,
collectionStats,
}: RootState) => ({
namespace,
isWritable,
isReadonlyView,
isSearchIndexesSupported,
writeStateDescription: description,
indexView,
serverVersion,
searchIndexes,
collectionStats,
});
const mapState = (state: RootState) => {
const { isViewPipelineSearchQueryable } = selectIsViewSearchCompatible(state);
const {
namespace,
isWritable,
isReadonlyView,
isSearchIndexesSupported,
description,
serverVersion,
searchIndexes,
indexView,
} = state;
return {
namespace,
isWritable,
isReadonlyView,
isSearchIndexesSupported,
writeStateDescription: description,
indexView,
serverVersion,
hasSearchIndexes: searchIndexes.indexes.length > 0,
isViewPipelineSearchQueryable,
};
};

const mapDispatch = {
onCreateRegularIndexClick: () => createIndexOpened(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ describe('SearchIndexesTable Component', function () {
expect(openCreateSpy.callCount).to.equal(1);
});

it('renders the zero state with button disabled if there are no indexes and isReadOnlyView with non searchable pipeline', function () {
it('renders the standard indexes incompatible empty state if there are no indexes and isReadOnlyView with non searchable pipeline', function () {
const pipelineMock: Document[] = [{ $project: { newField: 'testValue' } }];
const mockCollectionStats = {
index_count: 0,
Expand All @@ -195,15 +195,10 @@ describe('SearchIndexesTable Component', function () {
{ collectionStats: mockCollectionStats }
);

expect(() => {
screen.getByTestId('search-indexes-list');
}).to.throw();

const button = screen.getByTestId('create-atlas-search-index-button');
expect(button).to.exist;
expect(button.closest('button')?.getAttribute('aria-disabled')).to.equal(
'true'
);
expect(screen.queryByTestId('search-indexes-list')).to.not.exist;
expect(screen.queryByTestId('create-atlas-search-index-button')).to.not
.exist;
expect(screen.getByText('No standard indexes')).to.exist;
});

context('renders list with action', function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { selectIsViewSearchCompatible } from '../../utils/is-view-search-compati

import { useSearchIndexesTable } from './use-search-indexes-table';
import { COLUMNS, COLUMNS_WITH_ACTIONS } from './search-indexes-columns';
import ViewStandardIndexesIncompatibleEmptyState from '../view-incompatible-components/view-standard-indexes-incompatible-empty-state';

type SearchIndexesTableProps = {
namespace: string;
Expand Down Expand Up @@ -209,6 +210,9 @@ export const SearchIndexesTable: React.FunctionComponent<
}

if (indexes.length === 0) {
if (isReadonlyView && !isViewPipelineSearchQueryable) {
return <ViewStandardIndexesIncompatibleEmptyState />;
}
Comment thread
DarshanaVenkatesh marked this conversation as resolved.
return (
<ZeroState
onOpenCreateModalClick={onOpenCreateModalClick}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const ViewPipelineIncompatibleBanner = ({
variant={hasNoSearchIndexes ? 'warning' : 'danger'}
data-testid="view-not-search-compatible-banner"
>
{!hasNoSearchIndexes && (

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Oh my! 🙈

{hasNoSearchIndexes && (
<>
<b>Looking for search indexes?</b> <br />
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { expect } from 'chai';

import { shouldShowIndexesToolbarButtons } from './should-show-indexes-toolbar-buttons';

const baseParams = {
isReadonlyView: false,
serverVersion: '8.1.0',
isSearchManagementActive: true,
isViewPipelineSearchQueryable: true,
hasSearchIndexes: false,
};

describe('shouldShowIndexesToolbarButtons', function () {
it('always shows the toolbar for non-views', function () {
expect(
shouldShowIndexesToolbarButtons({
...baseParams,
isReadonlyView: false,
// even when nothing search-related applies
isSearchManagementActive: false,
isViewPipelineSearchQueryable: false,
})
).to.be.true;
});

describe('for a readonly view', function () {
const viewParams = { ...baseParams, isReadonlyView: true };

it('hides the toolbar when the server version is below 8.1', function () {
expect(
shouldShowIndexesToolbarButtons({
...viewParams,
serverVersion: '8.0.0',
})
).to.be.false;
});

it('hides the toolbar when search management is not active', function () {
expect(
shouldShowIndexesToolbarButtons({
...viewParams,
isSearchManagementActive: false,
})
).to.be.false;
});

it('shows the toolbar for a search-queryable view', function () {
expect(
shouldShowIndexesToolbarButtons({
...viewParams,
isViewPipelineSearchQueryable: true,
})
).to.be.true;
});

describe('when the pipeline is not search-queryable', function () {
const incompatibleViewParams = {
...viewParams,
isViewPipelineSearchQueryable: false,
};

it('hides the toolbar when there are no existing search indexes', function () {
expect(
shouldShowIndexesToolbarButtons({
...incompatibleViewParams,
hasSearchIndexes: false,
})
).to.be.false;
});

it('shows the toolbar when there are existing search indexes', function () {
expect(
shouldShowIndexesToolbarButtons({
...incompatibleViewParams,
hasSearchIndexes: true,
})
).to.be.true;
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { VIEW_PIPELINE_UTILS } from '@mongodb-js/mongodb-constants';

/**
* Returns true when the indexes toolbar action buttons (create index, refresh,
* segmented control, etc.) should be shown.
*/
export function shouldShowIndexesToolbarButtons({
isReadonlyView,
serverVersion,
isSearchManagementActive,
isViewPipelineSearchQueryable,
hasSearchIndexes,
}: {
isReadonlyView: boolean;
serverVersion: string;
isSearchManagementActive: boolean;
isViewPipelineSearchQueryable: boolean;
hasSearchIndexes: boolean;
}): boolean {
// Non-views always show the toolbar.
if (!isReadonlyView) {
return true;
}

// Views need 8.1+ and search management enabled.
if (
!VIEW_PIPELINE_UTILS.isVersionSearchCompatibleForViewsCompass(
serverVersion
) ||
!isSearchManagementActive
) {
return false;
}

// Search-queryable views, or incompatible views that still have existing

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

incompatible here refers to !isViewPipelineSearchQueryable only, nothing with isVersionSearchCompatibleForViewsCompass ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yup, for isVersionSearchCompatibleForViewsCompass we don't return the toolbar buttons and just display a banner telling them to upgrade.

// indexes show toolbar.
return isViewPipelineSearchQueryable || hasSearchIndexes;
}
Loading