Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
useConnectionSupports,
} from '@mongodb-js/compass-connections/provider';
import { usePreference } from 'compass-preferences-model/provider';
import IndexFlowSection from './index-flow-section';

const createIndexModalFieldsStyles = css({
margin: `${spacing[600]}px 0 ${spacing[800]}px 0`,
Expand Down Expand Up @@ -79,13 +80,40 @@ function CreateIndexForm({
});
}, [schemaFields]);

const showIndexesGuidanceIndexFlow =
showIndexesGuidanceVariant && currentTab === 'IndexFlow';
const showIndexesGuidanceQueryFlow =
showIndexesGuidanceVariant && currentTab === 'QueryFlow';

const RenderCreateIndexFields = () => {
if (fields.length > 0 && !showIndexesGuidanceQueryFlow) {
return (
<CreateIndexFields
schemaFields={schemaFieldNames}
fields={fields}
serverVersion={serverVersion}
isRemovable={!(fields.length > 1)}
onSelectFieldNameClick={onSelectFieldNameClick}
onSelectFieldTypeClick={onSelectFieldTypeClick}
onAddFieldClick={onAddFieldClick}
onRemoveFieldClick={onRemoveFieldClick}
/>
);
}
return null;
};

return (
<>
<div
className={createIndexModalFieldsStyles}
data-testid="create-index-form"
>
{showIndexesGuidanceVariant ? (
{!showIndexesGuidanceVariant ? (
<Body weight="medium" className={indexFieldsHeaderStyles}>
Index fields
</Body>
) : (
<RadioBoxGroup
aria-labelledby="index-flows"
data-testid="create-index-form-flows"
Expand All @@ -103,27 +131,18 @@ function CreateIndexForm({
Start with a Query
</RadioBox>
</RadioBoxGroup>
) : (
<Body weight="medium" className={indexFieldsHeaderStyles}>
Index fields
</Body>
)}

{/* Only show the fields if user is in the Start with an index flow or if they're in the control */}
{fields.length > 0 &&
(!showIndexesGuidanceVariant || currentTab === 'IndexFlow') ? (
<CreateIndexFields
schemaFields={schemaFieldNames}
fields={fields}
serverVersion={serverVersion}
isRemovable={!(fields.length > 1)}
onSelectFieldNameClick={onSelectFieldNameClick}
onSelectFieldTypeClick={onSelectFieldTypeClick}
onAddFieldClick={onAddFieldClick}
onRemoveFieldClick={onRemoveFieldClick}
{showIndexesGuidanceVariant && showIndexesGuidanceIndexFlow ? (
<IndexFlowSection
createIndexFieldsComponent={RenderCreateIndexFields()}
/>
) : null}
) : (
RenderCreateIndexFields()
)}
</div>

{/* TODO in CLOUDP-314036: update the accordion design */}
<Accordion data-testid="create-index-modal-toggle-options" text="Options">
<div
data-testid="create-index-modal-options"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react';
import { render, screen } from '@mongodb-js/testing-library-compass';
import IndexFlowSection from './index-flow-section';
import { expect } from 'chai';

describe('IndexFlowSection', () => {
const renderComponent = (createIndexFieldsComponent?: JSX.Element) => {
render(
<IndexFlowSection
createIndexFieldsComponent={createIndexFieldsComponent ?? null}
/>
);
};
it('renders the Input Index header', () => {
renderComponent();
expect(screen.getByText('Input Index')).to.be.visible;
});

it('renders the Code Equivalent toggle', () => {
renderComponent();
expect(screen.getByLabelText('Toggle Code Equivalent')).to.be.visible;
});

it('renders the Show me covered queries button', () => {
renderComponent();
expect(screen.getByText('Show me covered queries')).to.be.visible;
});

it('renders the Covered Queries header', () => {
renderComponent();
expect(screen.getByText('Covered Queries')).to.be.visible;
});

it('renders the provided createIndexFieldsComponent', () => {
const mockComponent = (
<div data-testid="mock-component">Mock Component</div>
);
renderComponent(mockComponent);
expect(screen.getByTestId('mock-component')).to.be.visible;
});

it('renders the covered queries examples', () => {
renderComponent();
expect(screen.getByTestId('index-flow-section-covered-queries-examples')).to
.exist;
});

it('renders the optimal query examples', () => {
renderComponent();
expect(screen.getByTestId('index-flow-section-optimal-query-examples')).to
.exist;
});

it('renders the Learn More link', () => {
renderComponent();
const link = screen.getByText('Learn More');
expect(link).to.be.visible;
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import {
Body,
Button,
css,
cx,
Icon,
Label,
Link,
palette,
spacing,
Toggle,
Tooltip,
fontFamilies,
} from '@mongodb-js/compass-components';
import React from 'react';

const flexContainerStyles = css({
display: 'flex',
alignItems: 'center',
});

const indexFieldsHeaderContainerStyles = css({
justifyContent: 'space-between',
marginBottom: spacing[200],
});

const indexFieldsCalloutStyles = css({
border: `1px solid ${palette.gray.light2}`,
borderRadius: '12px',
padding: spacing[600],
marginBottom: spacing[600],
});

const codeEquivalentToggleLabelStyles = css({
marginRight: spacing[100],
fontWeight: 'normal',
});

const coveredQueriesHeaderContainerStyles = css({
marginBottom: spacing[200],
});

const coveredQueriesCalloutStyles = css({
border: `1px solid ${palette.gray.light2}`,
background: palette.gray.light3,
borderRadius: '12px',
padding: spacing[600],
marginBottom: spacing[600],
});

const buttonContainerStyles = css({
display: 'flex',
justifyContent: 'right',
});

const coveredQueriesButtonStyles = css({
float: 'right',
marginTop: spacing[400],
});

const underlineStyles = css({
textDecoration: 'underline',
});

const codeStyles = css({
fontFamily: fontFamilies.code,
});

const infoWithCircleIconStyles = css({
color: palette.gray.dark1,
marginLeft: spacing[200],
});

export type IndexFlowSectionProps = {
createIndexFieldsComponent: JSX.Element | null;
};

const IndexFlowSection = ({
createIndexFieldsComponent,
}: IndexFlowSectionProps) => {
return (
<div>
<div
className={cx(indexFieldsHeaderContainerStyles, flexContainerStyles)}
>
<Body baseFontSize={16} weight="medium">
Input Index
</Body>
<div className={flexContainerStyles}>
<Label
className={codeEquivalentToggleLabelStyles}
htmlFor="code-equivalent-toggle"
>
Code Equivalent
</Label>

<Toggle
size="xsmall"
id="code-equivalent-toggle"
aria-label="Toggle Code Equivalent"
onChange={() => {
() => {
// TODO in CLOUDP-311784
};
}}
// checked={false}
/>
</div>
</div>
<div className={indexFieldsCalloutStyles}>
{createIndexFieldsComponent}

<div className={buttonContainerStyles}>
<Button
className={coveredQueriesButtonStyles}
onClick={() => {
// TODO in CLOUDP-311782 generate covered queries
// TODO in CLOUDP-311783 generate optimal queries
}}
size="small"
>
Show me covered queries
</Button>
</div>
</div>

<div
className={cx(coveredQueriesHeaderContainerStyles, flexContainerStyles)}
>
<Body baseFontSize={16} weight="medium">
Covered Queries
</Body>
<Tooltip
enabled={true}
trigger={
<span>
<Icon
glyph="InfoWithCircle"
className={cx(infoWithCircleIconStyles, flexContainerStyles)}
data-testid="index-flow-section-covered-queries-tooltip"
/>
</span>
}
triggerEvent="hover"
align="top"
justify="middle"
>
A covered query is a query that can be satisfied entirely using an
index and does not have to examine any documents. If a query is
covered, it is highly performant.
</Tooltip>
</div>

<div className={coveredQueriesCalloutStyles}>
{/* Covered Queries, clean up with actual covered queries examples in CLOUDP-311782 */}
<Body
className={codeStyles}
data-testid="index-flow-section-covered-queries-examples"
>
{`{ awards.wins:3 }`} <br />
{`{ awards.wins:3, imdb.rating:5 }`} <br />
{`{ awards.wins:3, imdb.rating:5, awards.nominations:8 }`} <br />
</Body>
<p>
<span className={underlineStyles}>
Follow the Equality, Sort, Range (ESR) Rule and this index is
optimal for queries that have this pattern:
</span>
{/* Optimal queries, clean up with actual optimal queries in CLOUDP-311783 */}
<Body
className={codeStyles}
data-testid="index-flow-section-optimal-query-examples"
>
{`{ awards.wins : 5, imdb.rating: {$gt : 5} }.sort({ awards.nominations : 1 }`}
</Body>
</p>

<Link href="https://www.mongodb.com/docs/manual/core/query-optimization/">
Learn More
</Link>
</div>
</div>
);
};

export default IndexFlowSection;
Loading