Skip to content

Commit 19993bf

Browse files
feat(compass-indexes): Add view support to indexes tab COMPASS-9667 (#7182)
* add banners to indexes tab * update banner to include data explorer versions * fetching search indexes for view * fix indexes-toolbar * update ui * update indexes-toolbar test * add tests to indexes.spec.tsx * only fetch indexes for version 8.1+ * fix conditionals * change or to and * add to useEffect conditional * remove fetch for version * add verison check back but remove from store * pr review changes * move function * fix indexes-toolbar.spec * fix initial state import * cleanup comparisons * remove de errors * add missing dep * installed for lockfile * replace button onClick with href Co-authored-by: Sergey Petushkov <[email protected]> * ran prettier --------- Co-authored-by: Sergey Petushkov <[email protected]>
1 parent 37f34fa commit 19993bf

File tree

12 files changed

+391
-517
lines changed

12 files changed

+391
-517
lines changed

package-lock.json

Lines changed: 2 additions & 441 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/compass-indexes/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
"@mongodb-js/compass-workspaces": "^0.51.0",
7979
"@mongodb-js/mongodb-constants": "^0.12.2",
8080
"@mongodb-js/shell-bson-parser": "^1.2.0",
81+
"@mongodb-js/connection-info": "^0.17.1",
8182
"bson": "^6.10.4",
8283
"compass-preferences-model": "^2.50.0",
8384
"lodash": "^4.17.21",

packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -113,27 +113,49 @@ describe('IndexesToolbar Component', function () {
113113
});
114114
});
115115
});
116-
117-
it('should not render a warning', function () {
118-
expect(screen.queryByText('Readonly views may not contain indexes')).to
119-
.not.exist;
120-
});
121116
});
122117

123118
describe('when it is a readonly view', function () {
124-
beforeEach(function () {
125-
renderIndexesToolbar({
126-
isReadonlyView: true,
119+
describe('and server version is < 8.1+', function () {
120+
beforeEach(function () {
121+
renderIndexesToolbar({
122+
isReadonlyView: true,
123+
indexView: 'search-indexes',
124+
});
127125
});
128-
});
129126

130-
it('should not render the create index button', function () {
131-
expect(screen.queryByText('Create Index')).to.not.exist;
127+
it('should not render the create index button', function () {
128+
expect(screen.queryByText('Create Index')).to.not.exist;
129+
});
130+
131+
it('should not render the create search index button', function () {
132+
expect(screen.queryByText('Create Search Index')).to.not.exist;
133+
});
134+
135+
it('should not render the refresh button', function () {
136+
expect(screen.queryByText('Refresh')).to.not.exist;
137+
});
132138
});
139+
describe('and server version is > 8.1+', function () {
140+
beforeEach(function () {
141+
renderIndexesToolbar({
142+
isReadonlyView: true,
143+
serverVersion: '8.1.0',
144+
indexView: 'search-indexes',
145+
});
146+
});
147+
148+
it('should not render the create index button', function () {
149+
expect(screen.queryByText('Create Index')).to.not.exist;
150+
});
133151

134-
it('should render a warning', function () {
135-
expect(screen.getByText('Readonly views may not contain indexes.')).to.be
136-
.visible;
152+
it('should render the create search index button <8.1', function () {
153+
expect(screen.getByText('Create Search Index')).to.be.visible;
154+
});
155+
156+
it('should not render the refresh button', function () {
157+
expect(screen.queryByText('Refresh')).to.be.visible;
158+
});
137159
});
138160
});
139161

@@ -332,5 +354,21 @@ describe('IndexesToolbar Component', function () {
332354
expect(segmentControl.closest('button')).to.have.attr('disabled');
333355
expect(onChangeViewCallback).to.not.have.been.calledOnce;
334356
});
357+
358+
describe('and readonly view >8.1', function () {
359+
beforeEach(function () {
360+
renderIndexesToolbar({
361+
isReadonlyView: true,
362+
serverVersion: '8.1.0',
363+
});
364+
});
365+
366+
it('it renders tabs with Indexes disabled', function () {
367+
const indexesTab = screen.getByText('Indexes');
368+
expect(indexesTab).to.be.visible;
369+
expect(indexesTab.closest('button')).to.have.attr('disabled');
370+
expect(screen.getByText('Search Indexes')).to.be.visible;
371+
});
372+
});
335373
});
336374
});

packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx

Lines changed: 75 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,32 @@
11
import React, { useCallback } from 'react';
22
import { connect } from 'react-redux';
33
import {
4-
withPreferences,
54
usePreference,
5+
withPreferences,
66
} from 'compass-preferences-model/provider';
77
import {
88
Button,
9-
ErrorSummary,
10-
Tooltip,
11-
WarningSummary,
12-
Link,
139
css,
14-
spacing,
10+
DropdownMenuButton,
11+
ErrorSummary,
1512
Icon,
16-
SpinLoader,
17-
SignalPopover,
13+
Link,
1814
PerformanceSignals,
19-
DropdownMenuButton,
2015
SegmentedControl,
2116
SegmentedControlOption,
17+
SignalPopover,
18+
spacing,
19+
SpinLoader,
20+
Tooltip,
2221
} from '@mongodb-js/compass-components';
2322
import { useConnectionInfo } from '@mongodb-js/compass-connections/provider';
2423
import semver from 'semver';
2524

2625
import type { RootState } from '../../modules';
27-
import { createSearchIndexOpened } from '../../modules/search-indexes';
26+
import {
27+
isVersionSearchCompatibleForViews,
28+
createSearchIndexOpened,
29+
} from '../../modules/search-indexes';
2830
import { getAtlasSearchIndexesLink } from '../../utils/atlas-search-indexes-link';
2931
import { createIndexOpened } from '../../modules/create-index';
3032
import type { IndexView } from '../../modules/index-view';
@@ -109,7 +111,10 @@ export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
109111
const isSearchManagementActive = usePreference('enableAtlasSearchIndexes');
110112
const { atlasMetadata } = useConnectionInfo();
111113
const showInsights = usePreference('showInsights') && !errorMessage;
112-
const showCreateIndexButton = !isReadonlyView && !readOnly && !errorMessage;
114+
const showCreateIndexButton =
115+
(!isReadonlyView || isVersionSearchCompatibleForViews(serverVersion)) &&
116+
!readOnly &&
117+
!errorMessage;
113118
const refreshButtonIcon = isRefreshing ? (
114119
<div className={spinnerStyles}>
115120
<SpinLoader title="Refreshing Indexes" />
@@ -123,7 +128,9 @@ export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
123128
className={indexesToolbarContainerStyles}
124129
data-testid="indexes-toolbar-container"
125130
>
126-
{!isReadonlyView && (
131+
{(!isReadonlyView ||
132+
(isVersionSearchCompatibleForViews(serverVersion) &&
133+
isSearchManagementActive)) && (
127134
<div data-testid="indexes-toolbar">
128135
<div className={toolbarButtonsContainer}>
129136
{showCreateIndexButton && (
@@ -139,7 +146,9 @@ export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
139146
isWritable={isWritable}
140147
onCreateRegularIndexClick={onCreateRegularIndexClick}
141148
onCreateSearchIndexClick={onCreateSearchIndexClick}
142-
></CreateIndexButton>
149+
isReadonlyView={isReadonlyView}
150+
indexView={indexView}
151+
/>
143152
</div>
144153
}
145154
>
@@ -173,6 +182,7 @@ export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
173182
signals={PerformanceSignals.get('too-many-indexes')}
174183
/>
175184
)}
185+
176186
{isSearchManagementActive && (
177187
<SegmentedControl
178188
size="xsmall"
@@ -182,13 +192,34 @@ export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
182192
value={indexView}
183193
data-testid="indexes-segment-controls"
184194
>
185-
<SegmentedControlOption
186-
data-testid="regular-indexes-tab"
187-
value="regular-indexes"
188-
>
189-
Indexes
190-
</SegmentedControlOption>
191-
{!isSearchIndexesSupported && (
195+
{isReadonlyView && (
196+
<Tooltip
197+
align="top"
198+
justify="end"
199+
enabled={true}
200+
renderMode="portal"
201+
trigger={
202+
<SegmentedControlOption
203+
data-testid="regular-indexes-tab"
204+
value="regular-indexes"
205+
disabled={isReadonlyView}
206+
>
207+
Indexes
208+
</SegmentedControlOption>
209+
}
210+
>
211+
Readonly views may not contain standard indexes.
212+
</Tooltip>
213+
)}
214+
{!isReadonlyView && (
215+
<SegmentedControlOption
216+
data-testid="regular-indexes-tab"
217+
value="regular-indexes"
218+
>
219+
Indexes
220+
</SegmentedControlOption>
221+
)}
222+
{!isSearchIndexesSupported && !isReadonlyView && (
192223
<Tooltip
193224
align="top"
194225
justify="end"
@@ -227,7 +258,7 @@ export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
227258
)}
228259
</Tooltip>
229260
)}
230-
{isSearchIndexesSupported && (
261+
{(isSearchIndexesSupported || isReadonlyView) && (
231262
<SegmentedControlOption
232263
data-testid="search-indexes-tab"
233264
value="search-indexes"
@@ -240,14 +271,8 @@ export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
240271
</div>
241272
</div>
242273
)}
243-
{isReadonlyView ? (
244-
<WarningSummary
245-
warnings={['Readonly views may not contain indexes.']}
246-
/>
247-
) : (
248-
!!errorMessage && (
249-
<ErrorSummary data-testid="indexes-error" errors={[errorMessage]} />
250-
)
274+
{!!errorMessage && isSearchManagementActive && (
275+
<ErrorSummary data-testid="indexes-error" errors={[errorMessage]} />
251276
)}
252277
</div>
253278
);
@@ -259,6 +284,8 @@ type CreateIndexButtonProps = {
259284
isWritable: boolean;
260285
onCreateRegularIndexClick: () => void;
261286
onCreateSearchIndexClick: () => void;
287+
isReadonlyView: boolean;
288+
indexView: IndexView;
262289
};
263290

264291
type CreateIndexActions = 'createRegularIndex' | 'createSearchIndex';
@@ -271,6 +298,8 @@ export const CreateIndexButton: React.FunctionComponent<
271298
isWritable,
272299
onCreateRegularIndexClick,
273300
onCreateSearchIndexClick,
301+
isReadonlyView,
302+
indexView,
274303
}) => {
275304
const onActionDispatch = useCallback(
276305
(action: CreateIndexActions) => {
@@ -284,7 +313,23 @@ export const CreateIndexButton: React.FunctionComponent<
284313
[onCreateRegularIndexClick, onCreateSearchIndexClick]
285314
);
286315

287-
if (isSearchIndexesSupported && isSearchManagementActive) {
316+
if (isReadonlyView && isSearchManagementActive) {
317+
if (indexView === 'search-indexes') {
318+
return (
319+
<Button
320+
disabled={!isWritable}
321+
onClick={onCreateSearchIndexClick}
322+
variant="primary"
323+
size="small"
324+
>
325+
Create Search Index
326+
</Button>
327+
);
328+
}
329+
330+
return null;
331+
}
332+
if (!isReadonlyView && isSearchIndexesSupported && isSearchManagementActive) {
288333
return (
289334
<DropdownMenuButton
290335
data-testid="multiple-index-types-creation-dropdown"

packages/compass-indexes/src/components/indexes/indexes.spec.tsx

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,8 @@ const renderIndexes = async (
4545

4646
if (props) {
4747
const state = store.getState();
48-
49-
const allProps: Partial<RootState> = {
50-
indexView: props.indexView ?? 'regular-indexes',
51-
regularIndexes: {
52-
...state.regularIndexes,
53-
...props.regularIndexes,
54-
},
55-
searchIndexes: {
56-
...state.searchIndexes,
57-
...props.searchIndexes,
58-
},
59-
};
60-
61-
Object.assign(store.getState(), allProps);
48+
const newState = { ...state, ...props };
49+
Object.assign(store.getState(), newState);
6250
}
6351

6452
render(
@@ -332,5 +320,76 @@ describe('Indexes Component', function () {
332320

333321
expect(getSearchIndexesStub.callCount).to.equal(2);
334322
});
323+
324+
it('renders search indexes list if isReadonlyView 8.1+ and has indexes', async function () {
325+
const getSearchIndexesStub = sinon.stub().resolves(searchIndexes);
326+
const dataProvider = {
327+
getSearchIndexes: getSearchIndexesStub,
328+
};
329+
await renderIndexes(undefined, dataProvider, {
330+
indexView: 'search-indexes',
331+
isReadonlyView: true,
332+
serverVersion: '8.1.0',
333+
});
334+
335+
await waitFor(() => {
336+
expect(screen.getByTestId('search-indexes-list')).to.exist;
337+
});
338+
});
339+
340+
it('renders correct empty state if isReadonlyView 8.1+ and has no indexes', async function () {
341+
const getSearchIndexesStub = sinon.stub().resolves([]);
342+
const dataProvider = {
343+
getSearchIndexes: getSearchIndexesStub,
344+
};
345+
await renderIndexes(undefined, dataProvider, {
346+
indexView: 'search-indexes',
347+
isReadonlyView: true,
348+
serverVersion: '8.1.0',
349+
});
350+
351+
expect(screen.getByText('No search indexes yet')).to.be.visible;
352+
expect(screen.getByText('Create Atlas Search Index')).to.be.visible;
353+
});
354+
355+
it('renders correct empty state if isReadonlyView 8.0 and has no indexes', async function () {
356+
const getSearchIndexesStub = sinon.stub().resolves([]);
357+
const dataProvider = {
358+
getSearchIndexes: getSearchIndexesStub,
359+
};
360+
await renderIndexes(undefined, dataProvider, {
361+
indexView: 'search-indexes',
362+
isReadonlyView: true,
363+
serverVersion: '8.0.0',
364+
});
365+
366+
expect(
367+
screen.queryByText(
368+
/Upgrade your cluster or manage search indexes on views in the Atlas UI./i
369+
)
370+
).to.exist;
371+
expect(screen.queryByText('No standard indexes')).to.exist;
372+
expect(screen.queryByText('Create Atlas Search Index')).to.not.exist;
373+
});
374+
375+
it('renders correct empty state if isReadonlyView <8.0 and has no indexes', async function () {
376+
const getSearchIndexesStub = sinon.stub().resolves([]);
377+
const dataProvider = {
378+
getSearchIndexes: getSearchIndexesStub,
379+
};
380+
await renderIndexes(undefined, dataProvider, {
381+
indexView: 'search-indexes',
382+
isReadonlyView: true,
383+
serverVersion: '7.0.0',
384+
});
385+
386+
expect(
387+
screen.queryByText(
388+
/Upgrade your cluster to create search indexes on views./i
389+
)
390+
).to.exist;
391+
expect(screen.queryByText('No standard indexes')).to.exist;
392+
expect(screen.queryByText('Create Atlas Search Index')).to.not.exist;
393+
});
335394
});
336395
});

0 commit comments

Comments
 (0)