Skip to content

Commit 51b2fab

Browse files
authored
feat: Added code equivalent functionality CLOUDP-311784 (#6882)
* wip * implemented code equivalent * added db and collection name + tests * added link * added code viewer to query flow section and fixed tests * add spacing for query flow section bottom * fixed how to get db + collection name * moved func out, escaped quotes, fixed tests
1 parent f1b976e commit 51b2fab

File tree

8 files changed

+225
-52
lines changed

8 files changed

+225
-52
lines changed

packages/compass-indexes/src/components/create-index-fields.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ function CreateIndexFields({
108108
size="default"
109109
clearable={false}
110110
overflow="scroll-x"
111+
inputValue={field.name}
111112
onChange={(fieldName: string | null) =>
112113
onSelectFieldName(index, fieldName)
113114
}

packages/compass-indexes/src/components/create-index-form/create-index-form.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
import { usePreference } from 'compass-preferences-model/provider';
2121
import IndexFlowSection from './index-flow-section';
2222
import QueryFlowSection from './query-flow-section';
23+
import toNS from 'mongodb-ns';
2324

2425
const createIndexModalFieldsStyles = css({
2526
margin: `${spacing[600]}px 0 ${spacing[800]}px 0`,
@@ -86,6 +87,8 @@ function CreateIndexForm({
8687
const showIndexesGuidanceQueryFlow =
8788
showIndexesGuidanceVariant && currentTab === 'QueryFlow';
8889

90+
const { database: dbName, collection: collectionName } = toNS(namespace);
91+
8992
return (
9093
<>
9194
<div
@@ -120,6 +123,9 @@ function CreateIndexForm({
120123
// Variant UI
121124
showIndexesGuidanceVariant && showIndexesGuidanceIndexFlow ? (
122125
<IndexFlowSection
126+
fields={fields}
127+
dbName={dbName}
128+
collectionName={collectionName}
123129
createIndexFieldsComponent={
124130
<CreateIndexFields
125131
schemaFields={schemaFieldNames}
@@ -155,6 +161,8 @@ function CreateIndexForm({
155161
<QueryFlowSection
156162
schemaFields={schemaFields}
157163
serverVersion={serverVersion}
164+
dbName={dbName}
165+
collectionName={collectionName}
158166
/>
159167
)}
160168

packages/compass-indexes/src/components/create-index-form/index-flow-section.spec.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ describe('IndexFlowSection', () => {
88
render(
99
<IndexFlowSection
1010
createIndexFieldsComponent={createIndexFieldsComponent ?? null}
11+
fields={[]}
12+
dbName={'fakeDBName'}
13+
collectionName={'fakeCollectionName'}
1114
/>
1215
);
1316
};

packages/compass-indexes/src/components/create-index-form/index-flow-section.tsx

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import {
1111
fontFamilies,
1212
InfoSprinkle,
1313
} from '@mongodb-js/compass-components';
14-
import React from 'react';
14+
import React, { useState } from 'react';
15+
import type { Field } from '../../modules/create-index';
16+
import MDBCodeViewer from './mdb-code-viewer';
1517

1618
const flexContainerStyles = css({
1719
display: 'flex',
@@ -70,12 +72,41 @@ const coveredQueriesHeaderStyles = css({
7072
});
7173

7274
export type IndexFlowSectionProps = {
75+
fields: Field[];
7376
createIndexFieldsComponent: JSX.Element | null;
77+
dbName: string;
78+
collectionName: string;
7479
};
7580

7681
const IndexFlowSection = ({
7782
createIndexFieldsComponent,
83+
fields,
84+
dbName,
85+
collectionName,
7886
}: IndexFlowSectionProps) => {
87+
const [isCodeEquivalentToggleChecked, setIsCodeEquivalentToggleChecked] =
88+
useState(false);
89+
90+
const areAllFieldsFilledIn = fields.every((field) => {
91+
return field.name && field.type;
92+
});
93+
94+
const isCoveredQueriesButtonDisabled =
95+
!areAllFieldsFilledIn ||
96+
fields.some((field) => {
97+
return field.type === '2dsphere' || field.type === 'text';
98+
});
99+
100+
const indexNameTypeMap = fields.reduce<Record<string, string>>(
101+
(accumulator, currentValue) => {
102+
if (currentValue.name && currentValue.type) {
103+
accumulator[currentValue.name] = currentValue.type;
104+
}
105+
return accumulator;
106+
},
107+
{}
108+
);
109+
79110
return (
80111
<div>
81112
<div
@@ -96,17 +127,22 @@ const IndexFlowSection = ({
96127
size="xsmall"
97128
id="code-equivalent-toggle"
98129
aria-label="Toggle Code Equivalent"
99-
onChange={() => {
100-
() => {
101-
// TODO in CLOUDP-311784
102-
};
103-
}}
104-
// checked={false}
130+
onChange={(value) => setIsCodeEquivalentToggleChecked(value)}
131+
checked={isCodeEquivalentToggleChecked}
132+
disabled={!areAllFieldsFilledIn}
105133
/>
106134
</div>
107135
</div>
108136
<div className={indexFieldsCalloutStyles}>
109-
{createIndexFieldsComponent}
137+
{isCodeEquivalentToggleChecked ? (
138+
<MDBCodeViewer
139+
dbName={dbName}
140+
collectionName={collectionName}
141+
indexNameTypeMap={indexNameTypeMap}
142+
/>
143+
) : (
144+
createIndexFieldsComponent
145+
)}
110146

111147
<div className={buttonContainerStyles}>
112148
<Button
@@ -116,6 +152,7 @@ const IndexFlowSection = ({
116152
// TODO in CLOUDP-311783 generate optimal queries
117153
}}
118154
size="small"
155+
disabled={isCoveredQueriesButtonDisabled}
119156
>
120157
Show covered queries
121158
</Button>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import React from 'react';
2+
import { render, screen } from '@mongodb-js/testing-library-compass';
3+
import MDBCodeViewer from './mdb-code-viewer';
4+
import { expect } from 'chai';
5+
6+
describe('MDBCodeViewer', () => {
7+
const dbName = 'testDB';
8+
const defaultCollectionName = 'testCollection';
9+
const defaultIndexNameTypeMap = {
10+
field1: '1 (asc)',
11+
field2: '-1 (desc)',
12+
field3: '2dsphere',
13+
field4: 'text',
14+
};
15+
16+
const renderComponent = ({
17+
collectionName = defaultCollectionName,
18+
indexNameTypeMap = defaultIndexNameTypeMap,
19+
}: {
20+
collectionName?: string;
21+
indexNameTypeMap?: { [key: string]: string };
22+
}) => {
23+
render(
24+
<MDBCodeViewer
25+
dbName={dbName}
26+
collectionName={collectionName}
27+
indexNameTypeMap={indexNameTypeMap}
28+
/>
29+
);
30+
};
31+
32+
it('shows the db name, collection name, and field names, and field types', () => {
33+
renderComponent({});
34+
const codeElement = screen.getByTestId('mdb-code-viewer');
35+
expect(codeElement).to.have.text(
36+
'db.getSiblingDB("testDB").getCollection("testCollection").createIndex({ "field1": 1, "field2": -1, "field3": "2dsphere", "field4": "text"});'
37+
);
38+
});
39+
40+
it('shows the escaped version of collection name and field name when there are quotes', () => {
41+
renderComponent({
42+
collectionName: 'collection"With"quotes',
43+
indexNameTypeMap: { 'field"With"quotes': '1' },
44+
});
45+
const codeElement = screen.getByTestId('mdb-code-viewer');
46+
expect(codeElement).to.have.text(
47+
'db.getSiblingDB("testDB").getCollection("collection\\"With\\"quotes").createIndex({ "field\\"With\\"quotes": 1});'
48+
);
49+
});
50+
51+
it('renders the link to the MongoDB documentation', () => {
52+
renderComponent({});
53+
const linkElement = screen.getByText('here');
54+
expect(linkElement).to.be.visible;
55+
});
56+
});
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { Code, Link, css, spacing } from '@mongodb-js/compass-components';
2+
import React from 'react';
3+
4+
const containerStyles = css({
5+
display: 'flex',
6+
flexDirection: 'column',
7+
});
8+
9+
const programmingLanguageLinkStyles = css({
10+
marginLeft: 'auto',
11+
marginTop: spacing[100],
12+
});
13+
14+
const NUMERIC_INDEX_TYPES = [-1, 1];
15+
16+
const escapeText = (text: string) => {
17+
return text.replaceAll('"', '\\"');
18+
};
19+
20+
const generateCode = ({
21+
dbName,
22+
collectionName,
23+
indexNameTypeMap,
24+
}: {
25+
dbName: string;
26+
collectionName: string;
27+
indexNameTypeMap: { [key: string]: string };
28+
}) => {
29+
let codeStr = `db.getSiblingDB("${dbName}").getCollection("${escapeText(
30+
collectionName
31+
)}").createIndex({\n`;
32+
33+
Object.entries(indexNameTypeMap).forEach(([name, type], index) => {
34+
// Replacing everything inside the parenthesis i.e. (asc)
35+
let parsedType = escapeText(type.replace(/\(.*?\)/g, '')).trim();
36+
if (!NUMERIC_INDEX_TYPES.includes(Number(parsedType))) {
37+
parsedType = `"${parsedType}"`;
38+
}
39+
const parsedName = escapeText(name).trim();
40+
41+
codeStr += ` "${parsedName}": ${parsedType}`;
42+
43+
if (index !== Object.keys(indexNameTypeMap).length - 1) {
44+
codeStr += ',';
45+
}
46+
47+
codeStr += '\n';
48+
});
49+
50+
codeStr += `});`;
51+
return codeStr;
52+
};
53+
54+
const MDBCodeViewer = ({
55+
dbName,
56+
collectionName,
57+
indexNameTypeMap,
58+
dataTestId,
59+
}: {
60+
dbName: string;
61+
collectionName: string;
62+
indexNameTypeMap: { [key: string]: string };
63+
dataTestId?: string;
64+
}) => {
65+
const GeneratedCode = generateCode({
66+
dbName,
67+
collectionName,
68+
indexNameTypeMap,
69+
});
70+
71+
return (
72+
<div className={containerStyles}>
73+
<Code data-testid={dataTestId || 'mdb-code-viewer'} language="javascript">
74+
{GeneratedCode}
75+
</Code>
76+
<span className={programmingLanguageLinkStyles}>
77+
View programming language driver syntax{' '}
78+
<Link
79+
href="https://www.mongodb.com/docs/manual/core/indexes/create-index/"
80+
target="_blank"
81+
rel="noreferrer noopener"
82+
>
83+
here
84+
</Link>
85+
.
86+
</span>
87+
</div>
88+
);
89+
};
90+
91+
export default MDBCodeViewer;

packages/compass-indexes/src/components/create-index-form/query-flow-section.spec.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import { expect } from 'chai';
55

66
describe('QueryFlowSection', () => {
77
const renderComponent = () => {
8-
render(<QueryFlowSection schemaFields={[]} serverVersion="5.0.0" />);
8+
render(
9+
<QueryFlowSection
10+
schemaFields={[]}
11+
serverVersion="5.0.0"
12+
dbName={'fakeDBName'}
13+
collectionName={'fakeCollectionName'}
14+
/>
15+
);
916
};
1017
it('renders the input query section with a code editor', () => {
1118
renderComponent();
@@ -26,10 +33,4 @@ describe('QueryFlowSection', () => {
2633
);
2734
expect(codeElement).to.be.visible;
2835
});
29-
30-
it('renders the link to the MongoDB documentation', () => {
31-
renderComponent();
32-
const linkElement = screen.getByText('here');
33-
expect(linkElement).to.be.visible;
34-
});
3536
});

0 commit comments

Comments
 (0)