diff --git a/package-lock.json b/package-lock.json
index d8c564b0eab..c102a834a70 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -70008,10 +70008,8 @@
"hadron-app": "^5.0.0",
"hadron-app-registry": "^9.0.0",
"lodash.contains": "^2.4.3",
- "lodash.isundefined": "^3.0.1",
"lodash.map": "^4.6.0",
"lodash.max": "^4.0.1",
- "lodash.pick": "^4.4.0",
"mocha": "^8.4.0",
"mongodb": "^4.6.0",
"mongodb-data-service": "^22.0.0",
@@ -107552,10 +107550,8 @@
"hadron-app-registry": "^9.0.0",
"hadron-react-components": "^6.0.0",
"lodash.contains": "^2.4.3",
- "lodash.isundefined": "^3.0.1",
"lodash.map": "^4.6.0",
"lodash.max": "^4.0.1",
- "lodash.pick": "^4.4.0",
"mocha": "^8.4.0",
"mongodb": "^4.6.0",
"mongodb-data-service": "^22.0.0",
diff --git a/packages/compass-aggregations/src/components/pipeline-explain/index.spec.tsx b/packages/compass-aggregations/src/components/pipeline-explain/index.spec.tsx
index 0c24e10db7e..5f539d7a676 100644
--- a/packages/compass-aggregations/src/components/pipeline-explain/index.spec.tsx
+++ b/packages/compass-aggregations/src/components/pipeline-explain/index.spec.tsx
@@ -147,7 +147,8 @@ describe('PipelineExplain', function () {
name: /ascending index/i, // host_id index direction 1
})
).to.exist;
- expect(within(indexContent1).getByText(/location \(2dsphere\)/i)).to.exist;
+ expect(within(indexContent1).getByText(/location/i)).to.exist;
+ expect(within(indexContent1).getByText(/\(2dsphere\)/i)).to.exist;
// Toggle second accordian
userEvent.click(
@@ -164,6 +165,7 @@ describe('PipelineExplain', function () {
name: /descending index/i, // city_id index direction -1
})
).to.exist;
- expect(within(indexContent2).getByText(/title \(text\)/i)).to.exist;
+ expect(within(indexContent2).getByText(/title/i)).to.exist;
+ expect(within(indexContent2).getByText(/\(text\)/i)).to.exist;
});
});
diff --git a/packages/compass-components/src/components/index-icon.tsx b/packages/compass-components/src/components/index-icon.tsx
index 5ad63b3c392..3f66ad02155 100644
--- a/packages/compass-components/src/components/index-icon.tsx
+++ b/packages/compass-components/src/components/index-icon.tsx
@@ -9,7 +9,7 @@ const IndexIcon = ({ direction }: { direction: IndexDirection }) => {
) : direction === -1 ? (
) : (
- <>({String(direction)})>
+ ({String(direction)})
);
};
diff --git a/packages/compass-e2e-tests/helpers/selectors.ts b/packages/compass-e2e-tests/helpers/selectors.ts
index a33bc7475f5..09d0c1e2cd3 100644
--- a/packages/compass-e2e-tests/helpers/selectors.ts
+++ b/packages/compass-e2e-tests/helpers/selectors.ts
@@ -770,10 +770,18 @@ export const ExplainDocumentsReturnedSummary =
'[data-test-id="documents-returned-summary"]';
// Indexes tab
-export const IndexList = '[data-test-id="index-list"]';
-export const IndexComponent = '[data-test-id="index-list"] tr';
-export const IndexFieldName = '[data-testid="index-field-name"]';
-export const IndexFieldType = '[data-testid="index-field-type"]';
+export const IndexList = '[data-testid="indexes-list"]';
+export const IndexComponent = (name: string): string => {
+ return `[data-testid="index-row-${name}"]`;
+};
+export const IndexFieldName = '[data-testid="index-name-field"]';
+export const IndexFieldType = '[data-testid="index-type-field"]';
+export const IndexToggleOptions =
+ '[data-testid="create-index-modal-toggle-options"]';
+export const IndexToggleIsWildcard =
+ '[data-testid="create-index-modal-use-wildcard-checkbox-fieldset"] #create-index-modal-use-wildcard-checkbox-label';
+export const IndexWildcardProjectionEditor =
+ '[data-testid="create-index-modal-use-wildcard-checkbox-fieldset"] .ace_editor';
export const CreateIndexButton =
'[data-testid="open-create-index-modal-button"]';
@@ -791,13 +799,6 @@ export const CreateIndexModalFieldTypeSelectMenu = (idx: number): string => {
return `[data-testid="create-index-fields-type-${idx}"] #create-index-fields-type-select-${idx}-menu`;
};
-export const IndexToggleOptions =
- '[data-testid="create-index-modal-toggle-options"]';
-export const IndexToggleIsWildcard =
- '[data-testid="create-index-modal-use-wildcard-checkbox-fieldset"] #create-index-modal-use-wildcard-checkbox-label';
-export const IndexWildcardProjectionEditor =
- '[data-testid="create-index-modal-use-wildcard-checkbox-fieldset"] .ace_editor';
-
export const CreateIndexErrorMessage = `${CreateIndexModal} [role="alert"]`;
export const CreateIndexConfirmButton = `${CreateIndexModal} [role=dialog] > div:nth-child(2) button:first-child`;
export const CreateIndexCancelButton = `${CreateIndexModal} [role=dialog] > div:nth-child(2) button:last-child`;
@@ -808,13 +809,7 @@ export const DropIndexModalConfirmName =
export const DropIndexModalConfirmButton =
'[data-testid="drop_index_modal"] [role=dialog] > div:nth-child(2) button:first-child';
-export const indexComponent = (indexName: string): string => {
- return `[data-test-id="index-component-${indexName}"]`;
-};
-
-export const dropIndexButton = (indexName: string): string => {
- return `[data-testid="drop-index-button-${indexName}"]`;
-};
+export const DropIndexButton = '[data-testid="drop-index-button"]';
// Validation tab
export const AddRuleButton = '[data-test-id="add-rule-button"]';
diff --git a/packages/compass-e2e-tests/tests/collection-indexes-tab.test.ts b/packages/compass-e2e-tests/tests/collection-indexes-tab.test.ts
index 0ebc8673777..799f225c552 100644
--- a/packages/compass-e2e-tests/tests/collection-indexes-tab.test.ts
+++ b/packages/compass-e2e-tests/tests/collection-indexes-tab.test.ts
@@ -41,10 +41,12 @@ describe('Collection indexes tab', function () {
const element = await browser.$(Selectors.IndexList);
await element.waitForDisplayed();
- const indexes = await browser.$$(Selectors.IndexComponent);
+ const indexes = await browser.$$(Selectors.IndexComponent('_id_'));
expect(indexes).to.have.lengthOf(1);
- const indexFieldNameElement = await browser.$(Selectors.IndexFieldName);
+ const indexFieldNameElement = await browser.$(
+ `${Selectors.IndexComponent('_id_')} ${Selectors.IndexFieldName}`
+ );
expect(await indexFieldNameElement.getText()).to.equal('_id_');
});
@@ -81,10 +83,15 @@ describe('Collection indexes tab', function () {
await createModal.waitForDisplayed({ reverse: true });
- const indexComponent = await browser.$(Selectors.indexComponent('i_text'));
+ const indexComponentSelector = Selectors.IndexComponent('i_text');
+
+ const indexComponent = await browser.$(indexComponentSelector);
await indexComponent.waitForDisplayed();
- await browser.clickVisible(Selectors.dropIndexButton('i_text'));
+ await browser.hover(indexComponentSelector);
+ await browser.clickVisible(
+ `${indexComponentSelector} ${Selectors.DropIndexButton}`
+ );
const dropModal = await browser.$(Selectors.DropIndexModal);
await dropModal.waitForDisplayed();
@@ -157,10 +164,10 @@ describe('Collection indexes tab', function () {
await createModal.waitForDisplayed({ reverse: true });
- const indexComponent = await browser.$(Selectors.indexComponent('$**_1'));
+ const indexComponent = await browser.$(Selectors.IndexComponent('$**_1'));
await indexComponent.waitForDisplayed();
- const indexFieldTypeSelector = `${Selectors.indexComponent('$**_1')} ${
+ const indexFieldTypeSelector = `${Selectors.IndexComponent('$**_1')} ${
Selectors.IndexFieldType
}`;
const indexFieldTypeElement = await browser.$(indexFieldTypeSelector);
@@ -212,11 +219,15 @@ describe('Collection indexes tab', function () {
await createModal.waitForDisplayed({ reverse: true });
const indexComponent = await browser.$(
- Selectors.indexComponent('columnstore')
+ Selectors.IndexComponent('columnstore')
);
await indexComponent.waitForDisplayed();
- await browser.clickVisible(Selectors.dropIndexButton('columnstore'));
+ await browser.clickVisible(
+ `${Selectors.IndexComponent('columnstore')} ${
+ Selectors.DropIndexButton
+ }`
+ );
const dropModal = await browser.$(Selectors.DropIndexModal);
await dropModal.waitForDisplayed();
diff --git a/packages/compass-e2e-tests/tests/database-collections-tab.test.ts b/packages/compass-e2e-tests/tests/database-collections-tab.test.ts
index f201e44b2bb..227459592ee 100644
--- a/packages/compass-e2e-tests/tests/database-collections-tab.test.ts
+++ b/packages/compass-e2e-tests/tests/database-collections-tab.test.ts
@@ -250,7 +250,7 @@ describe('Database collections tab', function () {
await browser.navigateToCollectionTab('test', collectionName, 'Indexes');
- const typeElementSelector = `${Selectors.indexComponent(indexName)} ${
+ const typeElementSelector = `${Selectors.IndexComponent(indexName)} ${
Selectors.IndexFieldType
}`;
const typeElement = await browser.$(typeElementSelector);
diff --git a/packages/compass-indexes/package.json b/packages/compass-indexes/package.json
index 1ae92b2fbc0..0abd12a4946 100644
--- a/packages/compass-indexes/package.json
+++ b/packages/compass-indexes/package.json
@@ -80,10 +80,8 @@
"hadron-app": "^5.0.0",
"hadron-app-registry": "^9.0.0",
"lodash.contains": "^2.4.3",
- "lodash.isundefined": "^3.0.1",
"lodash.map": "^4.6.0",
"lodash.max": "^4.0.1",
- "lodash.pick": "^4.4.0",
"mocha": "^8.4.0",
"mongodb": "^4.6.0",
"mongodb-data-service": "^22.0.0",
diff --git a/packages/compass-indexes/src/components/drop-column/drop-column.jsx b/packages/compass-indexes/src/components/drop-column/drop-column.jsx
deleted file mode 100644
index 769ba93bd91..00000000000
--- a/packages/compass-indexes/src/components/drop-column/drop-column.jsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
-import { IconButton, Icon } from '@mongodb-js/compass-components';
-
-/**
- * Component for the drop column.
- */
-class DropColumn extends PureComponent {
- static displayName = 'DropColumn';
-
- static propTypes = {
- indexName: PropTypes.string.isRequired,
- isReadonly: PropTypes.bool.isRequired,
- isWritable: PropTypes.bool.isRequired,
- localAppRegistry: PropTypes.object.isRequired,
- };
-
- /**
- * Show drop index modal when drop button is clicked.
- *
- * @param {Object} evt - The click event.
- */
- clickDropHandler(evt) {
- evt.preventDefault();
- evt.stopPropagation();
- this.props.localAppRegistry.emit(
- 'toggle-drop-index-modal',
- true,
- this.props.indexName
- );
- }
-
- /**
- * Is the index droppable?
- *
- * @returns {Boolean} If the index can be dropped.
- */
- isDroppable() {
- return (
- this.props.isWritable &&
- this.props.indexName !== '_id_' &&
- !this.props.isReadonly
- );
- }
-
- /**
- * Render the drop column.
- *
- * @returns {React.Component} The drop column.
- */
- render() {
- return (
-
- {this.isDroppable() ? (
-
-
-
- ) : null}
- |
- );
- }
-}
-
-export default DropColumn;
diff --git a/packages/compass-indexes/src/components/drop-column/index.js b/packages/compass-indexes/src/components/drop-column/index.js
deleted file mode 100644
index f699f609125..00000000000
--- a/packages/compass-indexes/src/components/drop-column/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import DropColumn from './drop-column';
-export default DropColumn;
diff --git a/packages/compass-indexes/src/components/index-component/index-component.jsx b/packages/compass-indexes/src/components/index-component/index-component.jsx
deleted file mode 100644
index 6b7f5bf6188..00000000000
--- a/packages/compass-indexes/src/components/index-component/index-component.jsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
-import NameColumn from '../name-column';
-import TypeColumn from '../type-column';
-import SizeColumn from '../size-column';
-import UsageColumn from '../usage-column';
-import PropertyColumn from '../property-column';
-import DropColumn from '../drop-column';
-
-import { css, spacing, uiColors } from '@mongodb-js/compass-components';
-
-const containerStyles = css({
- backgroundColor: uiColors.white,
- boxShadow: '0 2px 10px 0 rgba(0, 0, 0, 0.075)',
- td: {
- padding: spacing[3],
- },
-});
-
-/**
- * Component for the index.
- */
-class IndexComponent extends PureComponent {
- static displayName = 'IndexComponent';
- static propTypes = {
- index: PropTypes.object.isRequired,
- isReadonly: PropTypes.bool.isRequired,
- isWritable: PropTypes.bool.isRequired,
- localAppRegistry: PropTypes.object.isRequired,
- nameChanged: PropTypes.func.isRequired,
- openLink: PropTypes.func.isRequired,
- };
-
- /**
- * Render the index.
- *
- * @returns {React.Component} The index.
- */
- render() {
- return (
-
-
-
-
-
-
-
-
- );
- }
-}
-
-export default IndexComponent;
diff --git a/packages/compass-indexes/src/components/index-component/index-component.spec.jsx b/packages/compass-indexes/src/components/index-component/index-component.spec.jsx
deleted file mode 100644
index 47832a218a5..00000000000
--- a/packages/compass-indexes/src/components/index-component/index-component.spec.jsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import React from 'react';
-import { mount } from 'enzyme';
-import { expect } from 'chai';
-import AppRegistry from 'hadron-app-registry';
-
-import IndexComponent from '../index-component';
-
-import NameColumn from '../name-column';
-import TypeColumn from '../type-column';
-import SizeColumn from '../size-column';
-import UsageColumn from '../usage-column';
-import PropertyColumn from '../property-column';
-import DropColumn from '../drop-column';
-
-const index = {
- name: 'a',
- type: 'regular',
- fields: { serialize: () => [] },
- size: 10,
- relativeSize: 10,
- properties: [],
-};
-
-describe('index-component [Component]', function () {
- const localAppRegistry = new AppRegistry();
- let component;
-
- describe('render', function () {
- beforeEach(function () {
- component = mount(
- {}}
- nameChanged={() => {}}
- openLink={() => {}}
- index={index}
- />
- );
- });
- afterEach(function () {
- component = null;
- });
- it('renders the type column', function () {
- expect(component.find(TypeColumn)).to.be.present();
- });
- it('renders the size column', function () {
- expect(component.find(SizeColumn)).to.be.present();
- });
- it('renders the usage column', function () {
- expect(component.find(UsageColumn)).to.be.present();
- });
- it('renders the property column', function () {
- expect(component.find(PropertyColumn)).to.be.present();
- });
- it('renders the drop column', function () {
- expect(component.find(DropColumn)).to.be.present();
- });
- it('renders the name column', function () {
- expect(component.find(NameColumn)).to.be.present();
- });
- });
-});
diff --git a/packages/compass-indexes/src/components/index-component/index.js b/packages/compass-indexes/src/components/index-component/index.js
deleted file mode 100644
index 3fb0007ed3c..00000000000
--- a/packages/compass-indexes/src/components/index-component/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import IndexComponent from './index-component';
-export default IndexComponent;
diff --git a/packages/compass-indexes/src/components/index-header-column/index-header-column.jsx b/packages/compass-indexes/src/components/index-header-column/index-header-column.jsx
deleted file mode 100644
index 698032ad0ca..00000000000
--- a/packages/compass-indexes/src/components/index-header-column/index-header-column.jsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
-import { css, cx, uiColors, spacing } from '@mongodb-js/compass-components';
-
-import { DEFAULT, DESC, ASC } from '../../modules/indexes';
-
-const headerStyles = css({
- paddingTop: spacing[2],
- paddingBottom: spacing[2],
- paddingRight: spacing[4],
- paddingLeft: spacing[4],
-});
-
-const sortStyles = css({
- display: 'inline-block',
- color: uiColors.gray.base,
-});
-
-/**
- * Component for an index header column.
- */
-class IndexHeaderColumn extends PureComponent {
- static displayName = 'IndexHeaderColumn';
- static propTypes = {
- indexes: PropTypes.array.isRequired,
- sortOrder: PropTypes.string.isRequired,
- sortColumn: PropTypes.string.isRequired,
- dataTestId: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
- sortIndexes: PropTypes.func.isRequired,
- };
-
- /**
- * Handle the index sort click.
- *
- * @param {Event} evt - The event.
- */
- handleIndexSort(evt) {
- evt.preventDefault();
- evt.stopPropagation();
- let order;
- if (this.props.sortColumn === this.props.name) {
- order = this.props.sortOrder === ASC ? DESC : ASC;
- } else {
- order = this.props.sortColumn === DEFAULT ? ASC : DESC;
- }
- this.props.sortIndexes(this.props.indexes, this.props.name, order);
- }
-
- /**
- * Render the index header column.
- *
- * @returns {React.Component} The index header column.
- */
- render() {
- return (
-
- {this.props.name}
- {this.props.sortColumn === this.props.name && (
-
- )}
- |
- );
- }
-}
-
-export default IndexHeaderColumn;
diff --git a/packages/compass-indexes/src/components/index-header-column/index-header-column.spec.jsx b/packages/compass-indexes/src/components/index-header-column/index-header-column.spec.jsx
deleted file mode 100644
index 6ce69ad24ba..00000000000
--- a/packages/compass-indexes/src/components/index-header-column/index-header-column.spec.jsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import React from 'react';
-import { mount } from 'enzyme';
-import { expect } from 'chai';
-import sinon from 'sinon';
-
-import IndexHeaderColumn from '../index-header-column';
-
-describe('index-header-column [Component]', function () {
- let component;
- let sortSpy;
- describe('not active', function () {
- beforeEach(function () {
- sortSpy = sinon.spy();
- component = mount(
-
- );
- });
- afterEach(function () {
- component = null;
- sortSpy = null;
- });
- it('renders the correct root classname', function () {
- expect(component.find('[data-test-id="testid"]')).to.be.present();
- });
- });
- describe('active', function () {
- beforeEach(function () {
- sortSpy = sinon.spy();
- component = mount(
-
- );
- });
- afterEach(function () {
- component = null;
- sortSpy = null;
- });
- it('renders the correct root classname', function () {
- expect(component.find('[data-test-id="testid"]')).to.be.present();
- });
- });
-});
diff --git a/packages/compass-indexes/src/components/index-header-column/index.js b/packages/compass-indexes/src/components/index-header-column/index.js
deleted file mode 100644
index 0c720ada8c1..00000000000
--- a/packages/compass-indexes/src/components/index-header-column/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import IndexHeaderColumn from './index-header-column';
-export default IndexHeaderColumn;
diff --git a/packages/compass-indexes/src/components/index-header/index-header.jsx b/packages/compass-indexes/src/components/index-header/index-header.jsx
deleted file mode 100644
index 7e75b444371..00000000000
--- a/packages/compass-indexes/src/components/index-header/index-header.jsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
-import { css } from '@mongodb-js/compass-components';
-import IndexHeaderColumn from '../index-header-column';
-
-const containerStyles = css({
- textAlign: 'left',
-});
-
-/**
- * Component for the index header.
- */
-class IndexHeader extends PureComponent {
- static displayName = 'IndexHeader';
- static propTypes = {
- isWritable: PropTypes.bool.isRequired,
- isReadonly: PropTypes.bool.isRequired,
- indexes: PropTypes.array.isRequired,
- sortOrder: PropTypes.string.isRequired,
- sortColumn: PropTypes.string.isRequired,
- sortIndexes: PropTypes.func.isRequired,
- };
-
- /**
- * Render the index header.
- *
- * @returns {React.Component} The index header.
- */
- render() {
- return (
-
-
-
-
-
-
-
- {!this.props.isReadonly && this.props.isWritable ? | : null}
-
-
- );
- }
-}
-
-export default IndexHeader;
diff --git a/packages/compass-indexes/src/components/index-header/index-header.spec.jsx b/packages/compass-indexes/src/components/index-header/index-header.spec.jsx
deleted file mode 100644
index e39f59987d3..00000000000
--- a/packages/compass-indexes/src/components/index-header/index-header.spec.jsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import React from 'react';
-import { mount } from 'enzyme';
-import { expect } from 'chai';
-
-import IndexHeader from '../index-header';
-
-describe('index-header [Component]', function () {
- let component;
- describe('isReadable', function () {
- beforeEach(function () {
- component = mount(
- {}}
- />
- );
- });
- afterEach(function () {
- component = null;
- });
-
- it('renders name column header', function () {
- expect(
- component.find('[data-test-id="index-header-name"]')
- ).to.be.present();
- });
- it('renders type column header', function () {
- expect(
- component.find('[data-test-id="index-header-type"]')
- ).to.be.present();
- });
- it('renders size column header', function () {
- expect(
- component.find('[data-test-id="index-header-size"]')
- ).to.be.present();
- });
- it('renders usage column header', function () {
- expect(
- component.find('[data-test-id="index-header-usage"]')
- ).to.be.present();
- });
- it('renders properties column header', function () {
- expect(
- component.find('[data-test-id="index-header-properties"]')
- ).to.be.present();
- });
- });
- describe('isReadonly', function () {
- beforeEach(function () {
- component = mount(
- {}}
- />
- );
- });
- afterEach(function () {
- component = null;
- });
-
- it('renders name column header', function () {
- expect(
- component.find('[data-test-id="index-header-name"]')
- ).to.be.present();
- });
- it('renders type column header', function () {
- expect(
- component.find('[data-test-id="index-header-type"]')
- ).to.be.present();
- });
- it('renders size column header', function () {
- expect(
- component.find('[data-test-id="index-header-size"]')
- ).to.be.present();
- });
- it('renders usage column header', function () {
- expect(
- component.find('[data-test-id="index-header-usage"]')
- ).to.be.present();
- });
- it('renders properties column header', function () {
- expect(
- component.find('[data-test-id="index-header-properties"]')
- ).to.be.present();
- });
- });
-});
diff --git a/packages/compass-indexes/src/components/index-header/index.js b/packages/compass-indexes/src/components/index-header/index.js
deleted file mode 100644
index e20ae7ac2a0..00000000000
--- a/packages/compass-indexes/src/components/index-header/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import IndexHeader from './index-header';
-export default IndexHeader;
diff --git a/packages/compass-indexes/src/components/index-list/index-list.jsx b/packages/compass-indexes/src/components/index-list/index-list.jsx
deleted file mode 100644
index b3e6943a8d6..00000000000
--- a/packages/compass-indexes/src/components/index-list/index-list.jsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
-import map from 'lodash.map';
-import IndexComponent from '../index-component';
-
-/**
- * Component for the index list.
- */
-class IndexList extends PureComponent {
- static displayName = 'IndexList';
-
- static propTypes = {
- indexes: PropTypes.array.isRequired,
- isReadonly: PropTypes.bool.isRequired,
- isWritable: PropTypes.bool.isRequired,
- localAppRegistry: PropTypes.object.isRequired,
- nameChanged: PropTypes.func.isRequired,
- openLink: PropTypes.func.isRequired,
- };
-
- /**
- * Render the index list.
- *
- * @returns {React.Component} The index list.
- */
- render() {
- const indexes = map(this.props.indexes, (model) => {
- return (
-
- );
- });
- return {indexes};
- }
-}
-
-export default IndexList;
diff --git a/packages/compass-indexes/src/components/index-list/index-list.spec.jsx b/packages/compass-indexes/src/components/index-list/index-list.spec.jsx
deleted file mode 100644
index 90bff74b5f9..00000000000
--- a/packages/compass-indexes/src/components/index-list/index-list.spec.jsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react';
-import { mount } from 'enzyme';
-import { expect } from 'chai';
-import AppRegistry from 'hadron-app-registry';
-
-import IndexList from '../index-list';
-import IndexComponent from '../index-component';
-
-const indexes = [
- {
- name: 'a',
- type: 'regular',
- fields: { serialize: () => [] },
- size: 10,
- relativeSize: 10,
- properties: [],
- },
- {
- name: 'b',
- type: 'regular',
- fields: { serialize: () => [] },
- size: 20,
- relativeSize: 20,
- properties: [],
- },
-];
-
-describe('index-list [Component]', function () {
- const localAppRegistry = new AppRegistry();
- let component;
-
- describe('render', function () {
- beforeEach(function () {
- component = mount(
- {}}
- nameChanged={() => {}}
- indexes={indexes}
- openLink={() => {}}
- />
- );
- });
- afterEach(function () {
- component = null;
- });
- it('renders the list', function () {
- expect(component.find(IndexComponent)).to.have.lengthOf(2);
- });
- });
-});
diff --git a/packages/compass-indexes/src/components/index-list/index.js b/packages/compass-indexes/src/components/index-list/index.js
deleted file mode 100644
index 00e0f1e12d3..00000000000
--- a/packages/compass-indexes/src/components/index-list/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import IndexList from './index-list';
-export default IndexList;
diff --git a/packages/compass-indexes/src/components/indexes-table/badge-with-icon-link.spec.tsx b/packages/compass-indexes/src/components/indexes-table/badge-with-icon-link.spec.tsx
new file mode 100644
index 00000000000..2f5587767c7
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes-table/badge-with-icon-link.spec.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import { cleanup, render, screen, within } from '@testing-library/react';
+import { expect } from 'chai';
+
+import BadgeWithIconLink from './badge-with-icon-link';
+
+describe('BadgeWithIconLink Component', function () {
+ before(cleanup);
+ afterEach(cleanup);
+ it('render a badge with icon', function () {
+ render();
+ const container = screen.getByTestId('mongodb-badge');
+ expect(within(container).getByText(/mongodb/i)).to.exist;
+ const infoIcon = within(container).getByRole('img', {
+ name: /info with circle icon/i,
+ });
+ expect(infoIcon).to.exist;
+ expect(infoIcon.closest('a')?.href).to.equal('https://mongodb.com/');
+ });
+});
diff --git a/packages/compass-indexes/src/components/indexes-table/badge-with-icon-link.tsx b/packages/compass-indexes/src/components/indexes-table/badge-with-icon-link.tsx
new file mode 100644
index 00000000000..7a59e8674a0
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes-table/badge-with-icon-link.tsx
@@ -0,0 +1,59 @@
+import React from 'react';
+import {
+ css,
+ Badge,
+ BadgeVariant,
+ Icon,
+ Link,
+ uiColors,
+ spacing,
+ focusRingStyles,
+ focusRingVisibleStyles,
+} from '@mongodb-js/compass-components';
+
+const badgeStyles = css({
+ gap: spacing[2],
+});
+
+const linkStyles = css(
+ {
+ lineHeight: 0,
+ color: uiColors.white,
+ span: {
+ // LG uses backgroundImage instead of textDecoration
+ backgroundImage: 'none !important',
+ },
+ '&:focus': focusRingVisibleStyles,
+ },
+ focusRingStyles
+);
+
+type BadgeWithIconLinkProps = {
+ text: string;
+ link: string;
+};
+
+const BadgeWithIconLink: React.FunctionComponent = ({
+ text,
+ link,
+}) => {
+ return (
+
+ {text}
+
+
+
+
+ );
+};
+
+export default BadgeWithIconLink;
diff --git a/packages/compass-indexes/src/components/indexes-table/drop-field.spec.tsx b/packages/compass-indexes/src/components/indexes-table/drop-field.spec.tsx
new file mode 100644
index 00000000000..d1353a807f4
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes-table/drop-field.spec.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import { cleanup, render, screen } from '@testing-library/react';
+import { expect } from 'chai';
+import { spy } from 'sinon';
+import userEvent from '@testing-library/user-event';
+
+import DropField from './drop-field';
+
+describe('DropField Component', function () {
+ before(cleanup);
+ afterEach(cleanup);
+ it('renders delete button', function () {
+ const onDeleteSpy = spy();
+ render();
+ const button = screen.getByTestId('drop-index-button');
+ expect(button).to.exist;
+ expect(button.getAttribute('aria-label')).to.equal(
+ 'Drop Index artist_id_index'
+ );
+ expect(onDeleteSpy.callCount).to.equal(0);
+ userEvent.click(button);
+ expect(onDeleteSpy.callCount).to.equal(1);
+ });
+});
diff --git a/packages/compass-indexes/src/components/indexes-table/drop-field.tsx b/packages/compass-indexes/src/components/indexes-table/drop-field.tsx
new file mode 100644
index 00000000000..f6ee3a42d00
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes-table/drop-field.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import { IconButton, Icon } from '@mongodb-js/compass-components';
+
+type DropFieldProps = {
+ name: string;
+ onDelete: () => void;
+};
+
+const DropField: React.FunctionComponent = ({
+ name,
+ onDelete,
+}) => {
+ return (
+
+
+
+ );
+};
+
+export default DropField;
diff --git a/packages/compass-indexes/src/components/indexes-table/indexes-table.spec.tsx b/packages/compass-indexes/src/components/indexes-table/indexes-table.spec.tsx
new file mode 100644
index 00000000000..b569c9af300
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes-table/indexes-table.spec.tsx
@@ -0,0 +1,202 @@
+import React from 'react';
+import { cleanup, render, screen, within } from '@testing-library/react';
+import { expect } from 'chai';
+import userEvent from '@testing-library/user-event';
+import { spy } from 'sinon';
+
+import { IndexesTable } from './indexes-table';
+import type { IndexModel } from './indexes-table';
+
+const indexes: IndexModel[] = [
+ {
+ cardinality: 'single',
+ name: '_id_',
+ size: 12,
+ relativeSize: 20,
+ type: 'hashed',
+ extra: {},
+ properties: ['unique'],
+ fields: {
+ serialize() {
+ return [
+ {
+ field: '_id',
+ value: 1,
+ },
+ ];
+ },
+ },
+ },
+ {
+ cardinality: 'compound',
+ name: 'album_id_artist_id',
+ size: 20,
+ relativeSize: 25,
+ type: 'text',
+ extra: {},
+ properties: [],
+ fields: {
+ serialize() {
+ return [
+ {
+ field: 'album_id',
+ value: 1,
+ },
+ {
+ field: 'artist_id',
+ value: -1,
+ },
+ ];
+ },
+ },
+ },
+ {
+ cardinality: 'compound',
+ name: 'partial_with_ttl',
+ size: 20,
+ relativeSize: 25,
+ type: 'text',
+ extra: {
+ expireAfterSeconds: 3600,
+ partialFilterExpression: {
+ play_count: 30,
+ },
+ },
+ properties: ['ttl', 'partial'],
+ fields: {
+ serialize() {
+ return [
+ {
+ field: 'views',
+ value: 1,
+ },
+ ];
+ },
+ },
+ },
+ {
+ cardinality: 'single',
+ name: 'wildcard_index',
+ size: 20,
+ relativeSize: 25,
+ type: 'wildcard',
+ extra: {
+ wildcardProjection: {
+ fieldA: true,
+ _id: false,
+ },
+ },
+ properties: [],
+ fields: {
+ serialize() {
+ return [
+ {
+ field: '$**',
+ value: 1,
+ },
+ ];
+ },
+ },
+ },
+];
+
+const renderIndexList = (
+ props: Partial> = {}
+) => {
+ render(
+ {}}
+ onDeleteIndex={() => {}}
+ {...props}
+ />
+ );
+};
+
+describe('IndexesTable Component', function () {
+ before(cleanup);
+ afterEach(cleanup);
+
+ it('renders indexes list', function () {
+ renderIndexList({ canDeleteIndex: true, indexes: indexes });
+
+ const indexesList = screen.getByTestId('indexes-list');
+ expect(indexesList).to.exist;
+
+ // Renders indexes list (table rows)
+ indexes.forEach((index) => {
+ const indexRow = screen.getByTestId(`index-row-${index.name}`);
+ expect(indexRow, 'it renders each index in a row').to.exist;
+
+ // Renders index fields (table cells)
+ [
+ 'index-name-field',
+ 'index-type-field',
+ 'index-size-field',
+ 'index-usage-field',
+ 'index-property-field',
+ 'index-drop-field',
+ ].forEach((indexCell) => {
+ // For _id index we always hide drop index field
+ if (index.name !== '_id_' && indexCell !== 'index-drop-field') {
+ expect(within(indexRow).getByTestId(indexCell)).to.exist;
+ } else {
+ expect(() => {
+ within(indexRow).getByTestId(indexCell);
+ }).to.throw;
+ }
+ });
+ });
+ });
+
+ it('does not render delete button when a user can not delete indexes', function () {
+ renderIndexList({ canDeleteIndex: false, indexes: indexes });
+ const indexesList = screen.getByTestId('indexes-list');
+ expect(indexesList).to.exist;
+ indexes.forEach((index) => {
+ const indexRow = screen.getByTestId(`index-row-${index.name}`);
+ expect(() => {
+ within(indexRow).getByTestId('index-drop-field');
+ }).to.throw;
+ });
+ });
+
+ ['Name and Definition', 'Type', 'Size', 'Usage', 'Properties'].forEach(
+ (column) => {
+ it(`sorts table by ${column}`, function () {
+ const onSortTableSpy = spy();
+ renderIndexList({
+ canDeleteIndex: true,
+ indexes: indexes,
+ onSortTable: onSortTableSpy,
+ });
+
+ const indexesList = screen.getByTestId('indexes-list');
+
+ const columnheader = within(indexesList).getByTestId(
+ `index-header-${column}`
+ );
+ const sortButton = within(columnheader).getByRole('button', {
+ name: /sort/i,
+ });
+
+ expect(onSortTableSpy.callCount).to.equal(0);
+
+ userEvent.click(sortButton);
+ expect(onSortTableSpy.callCount).to.equal(1);
+ expect(onSortTableSpy.getCalls()[0].args).to.deep.equal([
+ column,
+ 'desc',
+ ]);
+
+ userEvent.click(sortButton);
+ expect(onSortTableSpy.callCount).to.equal(2);
+ expect(onSortTableSpy.getCalls()[1].args).to.deep.equal([
+ column,
+ 'asc',
+ ]);
+ });
+ }
+ );
+});
diff --git a/packages/compass-indexes/src/components/indexes-table/indexes-table.tsx b/packages/compass-indexes/src/components/indexes-table/indexes-table.tsx
new file mode 100644
index 00000000000..58107d05988
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes-table/indexes-table.tsx
@@ -0,0 +1,162 @@
+import React, { useMemo } from 'react';
+import {
+ css,
+ Table,
+ TableHeader,
+ Row,
+ Cell,
+ cx,
+ spacing,
+} from '@mongodb-js/compass-components';
+
+import NameField from './name-field';
+import TypeField from './type-field';
+import SizeField from './size-field';
+import UsageField from './usage-field';
+import PropertyField from './property-field';
+import DropField from './drop-field';
+
+// When row is hovered, we show the delete button
+const rowStyles = css({
+ ':hover': {
+ '.delete-cell': {
+ button: {
+ opacity: 1,
+ },
+ },
+ },
+});
+// When row is not hovered, we hide the delete button
+const deleteFieldStyles = css({
+ button: {
+ opacity: 0,
+ '&:focus': {
+ opacity: 1,
+ },
+ },
+});
+
+const tableHeaderStyles = css({
+ borderWidth: 0,
+ borderBottomWidth: 3,
+ '> div': {
+ justifyContent: 'space-between',
+ },
+});
+
+const cellStyles = css({
+ verticalAlign: 'top',
+});
+
+const nameFieldStyles = css({
+ paddingTop: spacing[2],
+ paddingBottom: spacing[2],
+});
+
+// todo: move to redux store when converting that to ts
+export type IndexModel = {
+ name: string;
+ fields: {
+ serialize: () => { field: string; value: number | string }[];
+ };
+ type: 'geo' | 'hashed' | 'text' | 'wildcard' | 'clustered' | 'columnstore';
+ cardinality: 'single' | 'compound';
+ properties: ('unique' | 'sparse' | 'partial' | 'ttl' | 'collation')[];
+ extra: Record>;
+ size: number;
+ relativeSize: number;
+ usageCount?: number;
+ usageSince?: Date;
+};
+
+type IndexesTableProps = {
+ darkMode?: boolean;
+ indexes: IndexModel[];
+ canDeleteIndex: boolean;
+ onSortTable: (name: string, direction: 'asc' | 'desc') => void;
+ onDeleteIndex: (name: string) => void;
+};
+
+export const IndexesTable: React.FunctionComponent = ({
+ indexes,
+ canDeleteIndex,
+ onSortTable,
+ onDeleteIndex,
+}) => {
+ const columns = useMemo(() => {
+ const _columns = [
+ 'Name and Definition',
+ 'Type',
+ 'Size',
+ 'Usage',
+ 'Properties',
+ ].map((name) => {
+ return (
+ {
+ onSortTable(name, direction);
+ }}
+ />
+ );
+ });
+ // The delete column
+ if (canDeleteIndex) {
+ _columns.push();
+ }
+ return _columns;
+ }, [canDeleteIndex, onSortTable]);
+
+ return (
+
+ {({ datum: index }) => (
+
+
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+ {/* Delete column is conditional */}
+ {index.name !== '_id_' && canDeleteIndex && (
+
+
+ onDeleteIndex(index.name)}
+ />
+
+ |
+ )}
+
+ )}
+
+ );
+};
diff --git a/packages/compass-indexes/src/components/indexes-table/name-field.spec.tsx b/packages/compass-indexes/src/components/indexes-table/name-field.spec.tsx
new file mode 100644
index 00000000000..a0a243e08bf
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes-table/name-field.spec.tsx
@@ -0,0 +1,60 @@
+import React from 'react';
+import { cleanup, render, screen, within } from '@testing-library/react';
+import { expect } from 'chai';
+import userEvent from '@testing-library/user-event';
+
+import NameField from './name-field';
+
+describe('NameField Component', function () {
+ before(cleanup);
+ afterEach(cleanup);
+ it('renders name with keys', function () {
+ const name = 'album_artist_title_index';
+ const keys = [
+ {
+ field: 'album_id',
+ value: 1,
+ },
+ {
+ field: 'artist_id',
+ value: -1,
+ },
+ {
+ field: 'title',
+ value: 'text',
+ },
+ ];
+ render();
+
+ const accordianButton = screen.getByRole('button', {
+ name: `Show/Hide index ${name} keys`,
+ });
+
+ expect(accordianButton).to.exist;
+ userEvent.click(accordianButton);
+
+ const keysList = screen.getByRole('list');
+
+ const albumBadge = within(keysList).getByTestId('album_id-key');
+ expect(albumBadge).to.exist;
+ expect(
+ within(albumBadge).getByRole('img', {
+ name: /ascending index/i,
+ })
+ ).to.exist;
+ expect(albumBadge.textContent).to.equal('album_id');
+
+ const artistBadge = within(keysList).getByTestId('artist_id-key');
+ expect(artistBadge).to.exist;
+ expect(
+ within(artistBadge).getByRole('img', {
+ name: /descending index/i,
+ })
+ ).to.exist;
+ expect(artistBadge.textContent).to.equal('artist_id');
+
+ const titleBadge = within(keysList).getByTestId('title-key');
+ expect(titleBadge).to.exist;
+ expect(titleBadge.textContent).to.equal('title(text)');
+ });
+});
diff --git a/packages/compass-indexes/src/components/indexes-table/name-field.tsx b/packages/compass-indexes/src/components/indexes-table/name-field.tsx
new file mode 100644
index 00000000000..271bb5556e5
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes-table/name-field.tsx
@@ -0,0 +1,53 @@
+import React from 'react';
+import {
+ spacing,
+ css,
+ Accordion,
+ Badge,
+ BadgeVariant,
+ IndexIcon,
+} from '@mongodb-js/compass-components';
+
+import type { IndexModel } from './indexes-table';
+
+const keyListStyles = css({
+ marginTop: spacing[1],
+ marginBottom: spacing[1],
+});
+
+const keyItemStyles = css({
+ paddingTop: spacing[1],
+ paddingLeft: spacing[4],
+});
+
+const badgeStyles = css({
+ gap: spacing[1],
+});
+
+type NameFieldProps = {
+ name: string;
+ keys: ReturnType;
+};
+
+const NameField: React.FunctionComponent = ({ name, keys }) => {
+ return (
+
+
+ {keys.map(({ field, value }) => (
+ -
+
+ {field}
+
+
+
+ ))}
+
+
+ );
+};
+
+export default NameField;
diff --git a/packages/compass-indexes/src/components/indexes-table/property-field.spec.tsx b/packages/compass-indexes/src/components/indexes-table/property-field.spec.tsx
new file mode 100644
index 00000000000..6eb9b4c7481
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes-table/property-field.spec.tsx
@@ -0,0 +1,87 @@
+import React from 'react';
+import { cleanup, render, screen, within } from '@testing-library/react';
+import { expect } from 'chai';
+
+import PropertyField, { getPropertyTooltip } from './property-field';
+import getIndexHelpLink from '../../utils/index-link-helper';
+
+describe('PropertyField', function () {
+ before(cleanup);
+ afterEach(cleanup);
+ describe('PropertyField Component', function () {
+ it('renders index properties', function () {
+ render(
+
+ );
+
+ ['ttl', 'partial'].forEach((type) => {
+ const badge = screen.getByTestId(`${type}-badge`);
+ expect(badge).to.exist;
+ expect(badge.textContent).to.equal(type);
+ const infoIcon = within(badge).getByRole('img', {
+ name: /info with circle icon/i,
+ });
+ expect(infoIcon).to.exist;
+ expect(infoIcon.closest('a')?.href).to.equal(
+ getIndexHelpLink(type.toUpperCase() as any)
+ );
+ });
+ });
+
+ it('does not render cardinality badge when its single', function () {
+ render(
+
+ );
+ expect(() => {
+ screen.getByTestId('compound-badge');
+ }).to.throw;
+ });
+
+ it('renders cardinality badge when its compound', function () {
+ render(
+
+ );
+ const badge = screen.getByTestId('compound-badge');
+ expect(badge).to.exist;
+ expect(badge.textContent).to.equal('compound');
+ const infoIcon = within(badge).getByRole('img', {
+ name: /info with circle icon/i,
+ });
+ expect(infoIcon).to.exist;
+ expect(infoIcon.closest('a')?.href).to.equal(
+ getIndexHelpLink('COMPOUND')
+ );
+ });
+ });
+
+ describe('getPropertyTooltip', function () {
+ it('returns ttl tooltip', function () {
+ expect(
+ getPropertyTooltip('ttl', {
+ expireAfterSeconds: 200,
+ })
+ ).to.equal('expireAfterSeconds: 200');
+ });
+
+ it('returns partial tooltip', function () {
+ expect(
+ getPropertyTooltip('partial', {
+ partialFilterExpression: { _id: true },
+ })
+ ).to.equal(`partialFilterExpression: ${JSON.stringify({ _id: true })}`);
+ });
+
+ it('returns null for unsupported properties', function () {
+ ['unique', 'sparse', 'collation'].forEach(
+ (prop) => expect(getPropertyTooltip(prop as any, {})).to.be.null
+ );
+ });
+ });
+});
diff --git a/packages/compass-indexes/src/components/indexes-table/property-field.tsx b/packages/compass-indexes/src/components/indexes-table/property-field.tsx
new file mode 100644
index 00000000000..49bf53dc9af
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes-table/property-field.tsx
@@ -0,0 +1,85 @@
+import React from 'react';
+import getIndexHelpLink from '../../utils/index-link-helper';
+
+import { spacing, css, Tooltip, Body } from '@mongodb-js/compass-components';
+import type { IndexModel } from './indexes-table';
+import BadgeWithIconLink from './badge-with-icon-link';
+
+const containerStyles = css({
+ display: 'flex',
+ gap: spacing[1],
+});
+
+const partialTooltip = (partialFilterExpression: JSON) => {
+ return `partialFilterExpression: ${JSON.stringify(partialFilterExpression)}`;
+};
+
+const ttlTooltip = (expireAfterSeconds: number) => {
+ return `expireAfterSeconds: ${expireAfterSeconds}`;
+};
+
+export const getPropertyTooltip = (
+ property: IndexModel['properties'][0],
+ extra: IndexModel['extra']
+): string | null => {
+ return property === 'ttl'
+ ? ttlTooltip(extra.expireAfterSeconds as number)
+ : property === 'partial'
+ ? partialTooltip(extra.partialFilterExpression as JSON)
+ : null;
+};
+
+const PropertyBadgeWithTooltip: React.FunctionComponent<{
+ text: string;
+ link: string;
+ tooltip?: string | null;
+}> = ({ text, link, tooltip }) => {
+ return (
+ (
+
+ {children}
+
+
+ )}
+ >
+ {tooltip}
+
+ );
+};
+
+type PropertyFieldProps = {
+ extra: IndexModel['extra'];
+ properties: IndexModel['properties'];
+ cardinality: IndexModel['cardinality'];
+};
+
+const PropertyField: React.FunctionComponent = ({
+ extra,
+ properties,
+ cardinality,
+}) => {
+ return (
+
+ {properties.map((property) => {
+ return (
+
+ );
+ })}
+ {cardinality === 'compound' && (
+
+ )}
+
+ );
+};
+
+export default PropertyField;
diff --git a/packages/compass-indexes/src/components/indexes-table/size-field.spec.tsx b/packages/compass-indexes/src/components/indexes-table/size-field.spec.tsx
new file mode 100644
index 00000000000..61b993c127c
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes-table/size-field.spec.tsx
@@ -0,0 +1,29 @@
+import React from 'react';
+import { cleanup, render, screen } from '@testing-library/react';
+import { expect } from 'chai';
+
+import SizeField, { formatSize, getSizeTooltip } from './size-field';
+
+describe('SizeField', function () {
+ before(cleanup);
+ afterEach(cleanup);
+ describe('SizeField Component', function () {
+ it('renders size', function () {
+ render();
+ expect(screen.getByText(/20 b/i)).to.exist;
+ });
+ });
+
+ describe('SizeField functions', function () {
+ it('formats size', function () {
+ expect(formatSize(908)).to.equal('908 B');
+ expect(formatSize(2020)).to.equal('2.0 KB');
+ expect(formatSize(202020)).to.equal('202.0 KB');
+ });
+
+ it('returns correct tooltip', function () {
+ expect(getSizeTooltip(20)).to.equal('20.00% compared to largest index');
+ expect(getSizeTooltip(8)).to.equal('8.00% compared to largest index');
+ });
+ });
+});
diff --git a/packages/compass-indexes/src/components/indexes-table/size-field.tsx b/packages/compass-indexes/src/components/indexes-table/size-field.tsx
new file mode 100644
index 00000000000..ab9d39984b1
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes-table/size-field.tsx
@@ -0,0 +1,37 @@
+import numeral from 'numeral';
+import React from 'react';
+import { Body, Tooltip } from '@mongodb-js/compass-components';
+
+type SizeFieldProps = {
+ size: number;
+ relativeSize: number;
+};
+
+export const formatSize = (size: number) => {
+ const precision = size <= 1000 ? '0' : '0.0';
+ return numeral(size).format(precision + ' b');
+};
+
+export const getSizeTooltip = (relativeSize: number): string => {
+ return `${relativeSize.toFixed(2)}% compared to largest index`;
+};
+
+const SizeField: React.FunctionComponent = ({
+ relativeSize,
+ size,
+}) => {
+ return (
+ (
+
+ {children}
+ {formatSize(size)}
+
+ )}
+ >
+ {getSizeTooltip(relativeSize)}
+
+ );
+};
+
+export default SizeField;
diff --git a/packages/compass-indexes/src/components/indexes-table/type-field.spec.tsx b/packages/compass-indexes/src/components/indexes-table/type-field.spec.tsx
new file mode 100644
index 00000000000..53d4267bf77
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes-table/type-field.spec.tsx
@@ -0,0 +1,94 @@
+import React from 'react';
+import { cleanup, render, screen, within } from '@testing-library/react';
+import { expect } from 'chai';
+
+import TypeField, { IndexTypeTooltip, canRenderTooltip } from './type-field';
+import getIndexHelpLink from '../../utils/index-link-helper';
+
+describe('TypeField', function () {
+ before(cleanup);
+ afterEach(cleanup);
+ describe('TypeField Component', function () {
+ it('renders index type', function () {
+ render();
+
+ const badge = screen.getByTestId('text-badge');
+ expect(badge).to.exist;
+
+ expect(badge.textContent).to.equal('text');
+ const infoIcon = within(badge).getByRole('img', {
+ name: /info with circle icon/i,
+ });
+ expect(infoIcon).to.exist;
+ expect(infoIcon.closest('a')?.href).to.equal(getIndexHelpLink('TEXT'));
+ });
+
+ it('renders index type - with extra information', function () {
+ render(
+
+ );
+
+ const badge = screen.getByTestId('hashed-badge');
+ expect(badge).to.exist;
+ });
+ });
+
+ describe('IndexTypeTooltip Component', function () {
+ it('renders allowed props in tooltip', function () {
+ const extras: any = {
+ weights: 20,
+ default_language: 'de',
+ language_override: 'en',
+ wildcardProjection: { _id: true },
+ columnstoreProjection: { name: false },
+ };
+ render();
+ for (const key in extras) {
+ expect(
+ screen.getByText(`${key}: ${JSON.stringify(extras[key])}`),
+ `it renders ${key} prop in tooltip`
+ ).to.exist;
+ }
+ });
+
+ it('does not render disallowed props in tooltip', function () {
+ const extras: any = {
+ expireAfterSeconds: 200,
+ partialFilterExpression: { _id: true },
+ };
+ render();
+ for (const key in extras) {
+ expect(
+ () => screen.getByText(`${key}: ${JSON.stringify(extras[key])}`),
+ `it does not render ${key} prop in tooltip`
+ ).to.throw;
+ }
+ });
+ });
+
+ describe('canRenderTooltip function', function () {
+ it('renders tooltip', function () {
+ ['text', 'wildcard', 'columnstore'].forEach(
+ (x) =>
+ expect(
+ canRenderTooltip(x as any),
+ `it renders tooltip when type is ${x}`
+ ).to.be.true
+ );
+ });
+ it('does not render tooltip', function () {
+ ['geo', 'hashed', 'clustered'].forEach(
+ (x) =>
+ expect(
+ canRenderTooltip(x as any),
+ `it does not render tooltip when type is ${x}`
+ ).to.be.false
+ );
+ });
+ });
+});
diff --git a/packages/compass-indexes/src/components/indexes-table/type-field.tsx b/packages/compass-indexes/src/components/indexes-table/type-field.tsx
new file mode 100644
index 00000000000..d0a6381bc30
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes-table/type-field.tsx
@@ -0,0 +1,56 @@
+import React from 'react';
+import getIndexHelpLink from '../../utils/index-link-helper';
+import { Tooltip, Body } from '@mongodb-js/compass-components';
+
+import type { IndexModel } from './indexes-table';
+import BadgeWithIconLink from './badge-with-icon-link';
+
+export const canRenderTooltip = (type: IndexModel['type']) => {
+ return ['text', 'wildcard', 'columnstore'].indexOf(type) !== -1;
+};
+
+type TypeFieldProps = {
+ type: IndexModel['type'];
+ extra: IndexModel['extra'];
+};
+
+export const IndexTypeTooltip: React.FunctionComponent<{
+ extra: IndexModel['extra'];
+}> = ({ extra }) => {
+ const allowedProps = [
+ 'weights',
+ 'default_language',
+ 'language_override',
+ 'wildcardProjection',
+ 'columnstoreProjection',
+ ];
+ const items: JSX.Element[] = [];
+ for (const k in extra) {
+ if (allowedProps.includes(k)) {
+ items.push({`${k}: ${JSON.stringify(extra[k])}`});
+ }
+ }
+ return <>{items}>;
+};
+
+const TypeField: React.FunctionComponent = ({
+ type,
+ extra,
+}) => {
+ const link = getIndexHelpLink(type.toUpperCase() as any);
+ return (
+ (
+
+ {children}
+
+
+ )}
+ >
+
+
+ );
+};
+
+export default TypeField;
diff --git a/packages/compass-indexes/src/components/indexes-table/usage-field.spec.tsx b/packages/compass-indexes/src/components/indexes-table/usage-field.spec.tsx
new file mode 100644
index 00000000000..76a89f3924f
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes-table/usage-field.spec.tsx
@@ -0,0 +1,46 @@
+import React from 'react';
+import { cleanup, render, screen } from '@testing-library/react';
+import { expect } from 'chai';
+
+import UsageField, { getUsageTooltip } from './usage-field';
+
+describe('UsageField', function () {
+ before(cleanup);
+ afterEach(cleanup);
+
+ describe('UsageField Component', function () {
+ it('renders usage', function () {
+ const since = new Date();
+ render();
+
+ const renderedText = `20 (since ${since.toDateString()})`;
+ expect(screen.getByText(renderedText)).to.exist;
+ });
+
+ it('renders zero when usage is not defined', function () {
+ const since = new Date();
+ render();
+
+ const renderedText = `0 (since ${since.toDateString()})`;
+ expect(screen.getByText(renderedText)).to.exist;
+ });
+
+ it('renders N/A when since is not defined', function () {
+ render();
+ const renderedText = '30 (N/A)';
+ expect(screen.getByText(renderedText)).to.exist;
+ });
+ });
+
+ describe('getUsageTooltip', function () {
+ it('returns correct tooltip', function () {
+ expect(getUsageTooltip()).to.equal(
+ 'Either the server does not support the $indexStats command' +
+ 'or the user is not authorized to execute it.'
+ );
+ expect(getUsageTooltip(30)).to.equal(
+ '30 index hits since index creation or last server restart'
+ );
+ });
+ });
+});
diff --git a/packages/compass-indexes/src/components/indexes-table/usage-field.tsx b/packages/compass-indexes/src/components/indexes-table/usage-field.tsx
new file mode 100644
index 00000000000..86d1253135b
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes-table/usage-field.tsx
@@ -0,0 +1,41 @@
+import React from 'react';
+import { Tooltip, Body } from '@mongodb-js/compass-components';
+
+const NO_USAGE_STATS =
+ 'Either the server does not support the $indexStats command' +
+ 'or the user is not authorized to execute it.';
+
+export const getUsageTooltip = (usage?: number): string => {
+ return !usage
+ ? NO_USAGE_STATS
+ : `${usage} index hits since index creation or last server restart`;
+};
+
+type UsageFieldProps = {
+ usage?: number;
+ since?: Date;
+};
+
+const nbsp = '\u00a0';
+const UsageField: React.FunctionComponent = ({
+ usage,
+ since,
+}) => {
+ return (
+ (
+
+ {children}
+
+ {usage || 0}
+ {nbsp}(<>{since ? `since ${since.toDateString()}` : 'N/A'}>)
+
+
+ )}
+ >
+ {getUsageTooltip(usage)}
+
+ );
+};
+
+export default UsageField;
diff --git a/packages/compass-indexes/src/components/indexes-toolbar.spec.tsx b/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx
similarity index 100%
rename from packages/compass-indexes/src/components/indexes-toolbar.spec.tsx
rename to packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx
diff --git a/packages/compass-indexes/src/components/indexes-toolbar.tsx b/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx
similarity index 51%
rename from packages/compass-indexes/src/components/indexes-toolbar.tsx
rename to packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx
index 86a6eb3f25b..3c10a168f73 100644
--- a/packages/compass-indexes/src/components/indexes-toolbar.tsx
+++ b/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx
@@ -12,7 +12,15 @@ import {
import type AppRegistry from 'hadron-app-registry';
const toolbarStyles = css({
- padding: spacing[3],
+ padding: spacing[2],
+ backgroundColor: 'transparent',
+});
+
+const toolbarButtonsContainer = css({
+ display: 'flex',
+ flexDirection: 'row',
+ gap: spacing[2],
+ justifyContent: 'flex-end',
});
const createIndexButtonContainerStyles = css({
@@ -44,39 +52,39 @@ export const IndexesToolbar: React.FunctionComponent = ({
const showCreateIndexButton = !isReadonly && !isReadonlyView && !errorMessage;
return (
-
- {showCreateIndexButton ? (
- (
-
-
{isReadonlyView ? (
-
-
- );
- }
-
- /**
- * Render the indexes.
- *
- * @returns {React.Component} The indexes.
- */
- render() {
- return (
-
-
- {!this.props.isReadonlyView &&
- !this.props.errorMessage &&
- this.renderComponent()}
-
- );
- }
-}
-
-/**
- * Map the store state to properties to pass to the components.
- *
- * @param {Object} state - The store state.
- *
- * @returns {Object} The mapped properties.
- */
-const mapStateToProps = (state) => ({
- indexes: state.indexes,
- isWritable: state.isWritable,
- isReadonly: state.isReadonly,
- isReadonlyView: state.isReadonlyView,
- writeStateDescription: state.description,
- errorMessage: state.error,
- dataService: state.dataService,
- sortColumn: state.sortColumn,
- sortOrder: state.sortOrder,
- localAppRegistry: state.appRegistry.localAppRegistry,
-});
-
-/**
- * Connect the redux store to the component.
- * (dispatch)
- */
-const MappedIndexes = connect(mapStateToProps, {
- writeStateChanged,
- dataServiceConnected,
- sortIndexes,
- reset,
- nameChanged,
- openLink,
-})(Indexes);
-
-export default MappedIndexes;
-export { Indexes };
diff --git a/packages/compass-indexes/src/components/indexes/indexes.spec.jsx b/packages/compass-indexes/src/components/indexes/indexes.spec.jsx
deleted file mode 100644
index 93214694062..00000000000
--- a/packages/compass-indexes/src/components/indexes/indexes.spec.jsx
+++ /dev/null
@@ -1,189 +0,0 @@
-import React from 'react';
-import { mount } from 'enzyme';
-import { expect } from 'chai';
-import sinon from 'sinon';
-import AppRegistry from 'hadron-app-registry';
-import hadronApp from 'hadron-app';
-import { ErrorSummary, WarningSummary } from '@mongodb-js/compass-components';
-
-import { Indexes } from '../indexes';
-import IndexHeader from '../index-header';
-import IndexList from '../index-list';
-
-describe('indexes [Component]', function () {
- const localAppRegistry = new AppRegistry();
- let component;
- const sortIndexesSpy = sinon.spy();
- const toggleIsVisibleSpy = sinon.spy();
- const resetSpy = sinon.spy();
- const nameChangedSpy = sinon.spy();
- const openLinkSpy = sinon.spy();
-
- before(function () {
- global.hadronApp = hadronApp;
- global.hadronApp.appRegistry = localAppRegistry;
- });
-
- context('when the collection is not a readonly view', function () {
- beforeEach(function () {
- component = mount(
-
- );
- });
-
- afterEach(function () {
- component = null;
- });
-
- it('renders a create-index-button', function () {
- expect(
- component
- .find('button')
- .findWhere((node) => node.text() === 'Create Index')
- ).to.be.present();
- });
-
- it('does not render errors or warnings', function () {
- expect(component.find(ErrorSummary)).to.not.be.present();
- expect(component.find(WarningSummary)).to.not.be.present();
- });
-
- it('renders the list and header', function () {
- expect(component.find(IndexHeader)).to.be.present();
- expect(component.find(IndexList)).to.be.present();
- });
- });
-
- context('when the collection is a readonly view', function () {
- beforeEach(function () {
- component = mount(
-
- );
- });
-
- afterEach(function () {
- component = null;
- });
-
- it('does not render a create-index-button', function () {
- expect(
- component
- .find('button')
- .findWhere((node) => node.text() === 'Create Index')
- ).to.not.be.present();
- });
-
- it('renders a warning summary', function () {
- expect(component.find(WarningSummary)).to.be.present();
- expect(component.find(WarningSummary).text()).to.equal(
- 'Readonly views may not contain indexes.'
- );
- });
-
- it('does not render the list or header', function () {
- expect(component.find(IndexHeader)).to.not.be.present();
- expect(component.find(IndexList)).to.not.be.present();
- });
- });
-
- context('when the distribution is readonly', function () {
- beforeEach(function () {
- component = mount(
-
- );
- });
-
- afterEach(function () {
- component = null;
- });
-
- it('does not render a create-index-button', function () {
- expect(
- component
- .find('button')
- .findWhere((node) => node.text() === 'Create Index')
- ).to.not.be.present();
- });
-
- it('does not render errors or warnings', function () {
- expect(component.find(ErrorSummary)).to.not.be.present();
- expect(component.find(WarningSummary)).to.not.be.present();
- });
-
- it('renders the main column', function () {
- expect(component.find(IndexHeader)).to.be.present();
- expect(component.find(IndexList)).to.be.present();
- });
- });
-
- context('when there is an error', function () {
- beforeEach(function () {
- component = mount(
-
- );
- });
-
- afterEach(function () {
- component = null;
- });
-
- it('renders an error summary', function () {
- expect(component.find(ErrorSummary)).to.be.present();
- expect(component.find(ErrorSummary).text()).to.equal('a test error');
- });
- });
-});
diff --git a/packages/compass-indexes/src/components/indexes/indexes.spec.tsx b/packages/compass-indexes/src/components/indexes/indexes.spec.tsx
new file mode 100644
index 00000000000..5c2f73e6712
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes/indexes.spec.tsx
@@ -0,0 +1,92 @@
+import React from 'react';
+import { cleanup, render, screen, within } from '@testing-library/react';
+import { expect } from 'chai';
+import AppRegistry from 'hadron-app-registry';
+
+import { Indexes } from './indexes';
+
+const renderIndexes = (
+ props: Partial> = {}
+) => {
+ const appRegistry = new AppRegistry();
+ render(
+ {}}
+ {...props}
+ />
+ );
+};
+
+describe('Indexes Component', function () {
+ before(cleanup);
+ afterEach(cleanup);
+
+ it('renders indexes card', function () {
+ renderIndexes();
+ expect(screen.getByTestId('indexes')).to.exist;
+ });
+
+ it('renders indexes toolbar', function () {
+ renderIndexes();
+ expect(screen.getByTestId('indexes-toolbar')).to.exist;
+ });
+
+ it('does not render indexes list when its a readonly view', function () {
+ renderIndexes({
+ indexes: [],
+ isReadonlyView: true,
+ error: undefined,
+ });
+ expect(() => {
+ screen.getByTestId('indexes-list');
+ }).to.throw;
+ });
+ it('does not render indexes list when there is an error', function () {
+ renderIndexes({
+ indexes: [],
+ isReadonlyView: false,
+ error: 'Some random error',
+ });
+ expect(() => {
+ screen.getByTestId('indexes-list');
+ }).to.throw;
+ });
+ it('renders indexes list', function () {
+ renderIndexes({
+ indexes: [
+ {
+ cardinality: 'single',
+ name: '_id_',
+ size: 12,
+ relativeSize: 20,
+ type: 'hashed',
+ extra: {},
+ properties: ['unique'],
+ fields: {
+ serialize() {
+ return [
+ {
+ field: '_id',
+ value: 1,
+ },
+ ];
+ },
+ },
+ },
+ ],
+ isReadonlyView: false,
+ error: undefined,
+ });
+
+ const indexesList = screen.getByTestId('indexes-list');
+ expect(indexesList).to.exist;
+ expect(within(indexesList).getByTestId('index-row-_id_')).to.exist;
+ });
+});
diff --git a/packages/compass-indexes/src/components/indexes/indexes.tsx b/packages/compass-indexes/src/components/indexes/indexes.tsx
new file mode 100644
index 00000000000..56c2e88c653
--- /dev/null
+++ b/packages/compass-indexes/src/components/indexes/indexes.tsx
@@ -0,0 +1,104 @@
+import React from 'react';
+import { css, Card, spacing } from '@mongodb-js/compass-components';
+import { connect } from 'react-redux';
+import type AppRegistry from 'hadron-app-registry';
+
+import { sortIndexes } from '../../modules/indexes';
+
+import { IndexesToolbar } from '../indexes-toolbar/indexes-toolbar';
+import { IndexesTable } from '../indexes-table/indexes-table';
+import type { IndexModel } from '../indexes-table/indexes-table';
+
+const containerStyles = css({
+ margin: spacing[3],
+ padding: spacing[3],
+ display: 'grid',
+ gridTemplateAreas: `
+ 'toolbar'
+ 'indexTable'
+ `,
+ width: '100%',
+ overflow: 'hidden',
+ alignContent: 'start',
+});
+const toolbarStyles = css({
+ gridArea: 'toolbar',
+});
+const indexTableStyles = css({
+ gridArea: 'indexTable',
+ overflow: 'auto',
+});
+
+type IndexesProps = {
+ indexes: IndexModel[];
+ isWritable: boolean;
+ isReadonly: boolean;
+ isReadonlyView: boolean;
+ description?: string;
+ error?: string;
+ localAppRegistry: AppRegistry;
+ onSortTable: (name: string, direction: 'asc' | 'desc') => void;
+};
+
+export const Indexes: React.FunctionComponent = ({
+ indexes,
+ isWritable,
+ isReadonly,
+ isReadonlyView,
+ description,
+ error,
+ localAppRegistry,
+ onSortTable,
+}) => {
+ const onDeleteIndex = (name: string) => {
+ return localAppRegistry.emit('toggle-drop-index-modal', true, name);
+ };
+ return (
+
+
+
+
+ {!isReadonlyView && !error && (
+
+
+
+ )}
+
+ );
+};
+
+const mapState = ({
+ indexes,
+ isWritable,
+ isReadonly,
+ isReadonlyView,
+ description,
+ error,
+ appRegistry: { localAppRegistry },
+}: any) => ({
+ indexes,
+ isWritable,
+ isReadonly,
+ isReadonlyView,
+ description,
+ error,
+ localAppRegistry,
+});
+
+const mapDispatch = {
+ onSortTable: sortIndexes,
+};
+
+export default connect(mapState, mapDispatch)(Indexes as any);
diff --git a/packages/compass-indexes/src/components/name-column/index.js b/packages/compass-indexes/src/components/name-column/index.js
deleted file mode 100644
index 5d2fd30b7cd..00000000000
--- a/packages/compass-indexes/src/components/name-column/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import NameColumn from './name-column';
-export default NameColumn;
diff --git a/packages/compass-indexes/src/components/name-column/name-column.jsx b/packages/compass-indexes/src/components/name-column/name-column.jsx
deleted file mode 100644
index cf9c7d1f7bf..00000000000
--- a/packages/compass-indexes/src/components/name-column/name-column.jsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
-import {
- spacing,
- css,
- IndexIcon,
- BadgeVariant,
- Badge,
- Accordion,
-} from '@mongodb-js/compass-components';
-
-const keyListStyles = css({
- marginTop: spacing[1],
- marginBottom: spacing[1],
-});
-
-const keyItemStyles = css({
- paddingTop: spacing[1],
- paddingLeft: spacing[4],
-});
-
-class NameColumn extends PureComponent {
- static displayName = 'NameColumn';
-
- static propTypes = {
- index: PropTypes.object.isRequired,
- };
-
- render() {
- const indexName = this.props.index.name;
- const indexKeys = this.props.index.fields.serialize();
- return (
-
-
-
- {indexKeys.map(({ field, value }) => (
- -
-
- {field}
-
-
-
-
- ))}
-
-
- |
- );
- }
-}
-
-export default NameColumn;
diff --git a/packages/compass-indexes/src/components/property-column/index.js b/packages/compass-indexes/src/components/property-column/index.js
deleted file mode 100644
index 1e3d67f9e2e..00000000000
--- a/packages/compass-indexes/src/components/property-column/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import PropertyColumn from './property-column';
-export default PropertyColumn;
diff --git a/packages/compass-indexes/src/components/property-column/property-column.jsx b/packages/compass-indexes/src/components/property-column/property-column.jsx
deleted file mode 100644
index a8c507e1fbe..00000000000
--- a/packages/compass-indexes/src/components/property-column/property-column.jsx
+++ /dev/null
@@ -1,122 +0,0 @@
-import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
-import getIndexHelpLink from '../../utils/index-link-helper';
-
-import {
- spacing,
- css,
- Tooltip,
- Body,
- Badge,
- BadgeVariant,
- Icon,
- Link,
- uiColors,
-} from '@mongodb-js/compass-components';
-
-const contentStyles = css({
- display: 'flex',
- gap: spacing[1],
-});
-
-const badgeStyles = css({
- gap: spacing[2],
-});
-
-const iconLinkStyles = css({
- lineHeight: 0,
- color: uiColors.white,
- span: {
- // LG uses backgroundImage instead of textDecoration
- backgroundImage: 'none !important',
- },
-});
-
-class PropertyColumn extends PureComponent {
- static displayName = 'PropertyColumn';
-
- static propTypes = {
- index: PropTypes.object.isRequired,
- openLink: PropTypes.func.isRequired,
- };
-
- _partialTooltip() {
- const { partialFilterExpression } = this.props.index.extra;
- return `partialFilterExpression: ${JSON.stringify(
- partialFilterExpression
- )}`;
- }
-
- _ttlTooltip() {
- const { expireAfterSeconds } = this.props.index.extra;
- return `expireAfterSeconds: ${expireAfterSeconds}`;
- }
-
- renderItemWithTooltip(text, link, tooltip) {
- return (
- (
-
- {children}
-
- {text}
-
-
-
-
-
- )}
- >
- {tooltip}
-
- );
- }
-
- renderCardinality() {
- const { cardinality } = this.props.index;
- if (cardinality !== 'compound') {
- return null;
- }
- return this.renderItemWithTooltip(
- cardinality,
- getIndexHelpLink('COMPOUND')
- );
- }
-
- renderProperty(prop) {
- const tooltip =
- prop === 'ttl'
- ? this._ttlTooltip()
- : prop === 'partial'
- ? this._partialTooltip()
- : null;
-
- return this.renderItemWithTooltip(
- prop,
- getIndexHelpLink(prop.toUpperCase()),
- tooltip
- );
- }
-
- render() {
- const properties = this.props.index.properties.map(
- this.renderProperty.bind(this)
- );
- return (
-
-
- {properties}
- {this.renderCardinality()}
-
- |
- );
- }
-}
-
-export default PropertyColumn;
diff --git a/packages/compass-indexes/src/components/size-column/index.js b/packages/compass-indexes/src/components/size-column/index.js
deleted file mode 100644
index 044cc84c4a4..00000000000
--- a/packages/compass-indexes/src/components/size-column/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import SizeColumn from './size-column';
-export default SizeColumn;
diff --git a/packages/compass-indexes/src/components/size-column/size-column.jsx b/packages/compass-indexes/src/components/size-column/size-column.jsx
deleted file mode 100644
index 9023ca4716f..00000000000
--- a/packages/compass-indexes/src/components/size-column/size-column.jsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import numeral from 'numeral';
-import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
-
-import { Body, Tooltip } from '@mongodb-js/compass-components';
-
-/**
- * Component for the size column.
- */
-class SizeColumn extends PureComponent {
- static displayName = 'SizeColumn';
-
- static propTypes = {
- size: PropTypes.number.isRequired,
- relativeSize: PropTypes.number.isRequired,
- };
-
- _format(size) {
- const precision = size <= 1000 ? '0' : '0.0';
- return numeral(size).format(precision + ' b');
- }
-
- render() {
- const indexSize = this._format(this.props.size);
- const tooltip = `${this.props.relativeSize.toFixed(
- 2
- )}% compared to largest index`;
- return (
-
- (
-
- {children}
- {indexSize}
-
- )}
- >
- {tooltip}
-
- |
- );
- }
-}
-
-export default SizeColumn;
diff --git a/packages/compass-indexes/src/components/type-column/index.js b/packages/compass-indexes/src/components/type-column/index.js
deleted file mode 100644
index 01de3d8b138..00000000000
--- a/packages/compass-indexes/src/components/type-column/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import TypeColumn from './type-column';
-export default TypeColumn;
diff --git a/packages/compass-indexes/src/components/type-column/type-column.jsx b/packages/compass-indexes/src/components/type-column/type-column.jsx
deleted file mode 100644
index 88f13ae276d..00000000000
--- a/packages/compass-indexes/src/components/type-column/type-column.jsx
+++ /dev/null
@@ -1,93 +0,0 @@
-import map from 'lodash.map';
-import pick from 'lodash.pick';
-import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
-import getIndexHelpLink from '../../utils/index-link-helper';
-import {
- spacing,
- css,
- Tooltip,
- Body,
- Badge,
- BadgeVariant,
- Icon,
- Link,
- uiColors,
-} from '@mongodb-js/compass-components';
-
-const badgeStyles = css({
- gap: spacing[2],
-});
-
-const iconLinkStyles = css({
- lineHeight: 0,
- color: uiColors.white,
- span: {
- // LG uses backgroundImage instead of textDecoration
- backgroundImage: 'none !important',
- },
-});
-
-/**
- * Component for the type column.
- */
-class TypeColumn extends PureComponent {
- static displayName = 'TypeColumn';
-
- static propTypes = {
- index: PropTypes.object.isRequired,
- openLink: PropTypes.func.isRequired,
- };
-
- canRenderTooltip() {
- return (
- ['text', 'wildcard', 'columnstore'].indexOf(this.props.index.type) !== -1
- );
- }
-
- renderTooltip() {
- const info = pick(this.props.index.extra, [
- 'weights',
- 'default_language',
- 'language_override',
- 'wildcardProjection',
- 'columnstoreProjection',
- ]);
- const items = map(info, (v, k) => {
- return {`${k}: ${JSON.stringify(v)}`};
- });
- return <>{items}>;
- }
-
- render() {
- const helpLink = getIndexHelpLink(this.props.index.type.toUpperCase());
- return (
-
- (
-
- {children}
-
- {this.props.index.type}
-
-
-
-
-
- )}
- >
- {this.renderTooltip()}
-
- |
- );
- }
-}
-
-export default TypeColumn;
diff --git a/packages/compass-indexes/src/components/usage-column/index.js b/packages/compass-indexes/src/components/usage-column/index.js
deleted file mode 100644
index f9017806f5f..00000000000
--- a/packages/compass-indexes/src/components/usage-column/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import UsageColumn from './usage-column';
-export default UsageColumn;
diff --git a/packages/compass-indexes/src/components/usage-column/usage-column.jsx b/packages/compass-indexes/src/components/usage-column/usage-column.jsx
deleted file mode 100644
index cafab04e2d6..00000000000
--- a/packages/compass-indexes/src/components/usage-column/usage-column.jsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import isUndefined from 'lodash.isundefined';
-import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
-
-import { Tooltip, Body } from '@mongodb-js/compass-components';
-
-const NO_USAGE_STATS =
- 'Either the server does not support the $indexStats command' +
- 'or the user is not authorized to execute it.';
-
-class UsageColumn extends PureComponent {
- static displayName = 'UsageColumn';
-
- static propTypes = {
- usage: PropTypes.any,
- since: PropTypes.any,
- };
-
- tooltip() {
- if (isUndefined(this.props.usage)) {
- return NO_USAGE_STATS;
- }
- return `${this.props.usage} index hits since index creation or last\n server restart`;
- }
-
- renderSince() {
- if (isUndefined(this.props.since)) {
- return null;
- }
- return (
-
- (since
- {this.props.since ? this.props.since.toDateString() : 'N/A'})
-
- );
- }
-
- render() {
- const usage = isUndefined(this.props.usage) ? '0' : this.props.usage;
- const tooltip = this.tooltip();
- return (
-
- (
-
- {children}
-
- {usage} {this.renderSince()}
-
-
- )}
- >
- {tooltip}
-
- |
- );
- }
-}
-
-export default UsageColumn;
diff --git a/packages/compass-indexes/src/modules/indexes.js b/packages/compass-indexes/src/modules/indexes.js
index 62cd86139ec..53c6e9c5619 100644
--- a/packages/compass-indexes/src/modules/indexes.js
+++ b/packages/compass-indexes/src/modules/indexes.js
@@ -25,8 +25,8 @@ export const SORT_INDEXES = `${PREFIX}/indexes/SORT_INDEXES`;
* Default sortOrder
*/
export const DEFAULT = 'Name and Definition';
-export const ASC = 'fa-sort-asc';
-export const DESC = 'fa-sort-desc';
+export const ASC = 'asc';
+export const DESC = 'desc';
export const USAGE = 'Usage';
/**
@@ -171,21 +171,17 @@ export const loadIndexes = (indexes) => ({
indexes: indexes,
});
-/**
- * Action creator for sort indexes events.
- *
- * @param {Array} indexes - The raw indexes list.
- * @param {String} column - The column.
- * @param {String} order - The order.
- *
- * @returns {Object} The load indexes action.
- */
-export const sortIndexes = (indexes, column, order) => ({
- type: SORT_INDEXES,
- indexes: indexes,
- column: column,
- order: order,
-});
+export const sortIndexes = (column, order) => {
+ return (dispatch, getState) => {
+ const { indexes } = getState();
+ return dispatch({
+ type: SORT_INDEXES,
+ indexes,
+ column,
+ order,
+ });
+ };
+};
/**
* Load indexes from DB.
diff --git a/packages/compass-indexes/src/modules/indexes.spec.js b/packages/compass-indexes/src/modules/indexes.spec.js
index 8775ee56d8b..60d6fd1baf9 100644
--- a/packages/compass-indexes/src/modules/indexes.spec.js
+++ b/packages/compass-indexes/src/modules/indexes.spec.js
@@ -31,17 +31,33 @@ describe('indexes module', function () {
context('when the column is Usage', function () {
context('when sorting asc', function () {
it('returns the sorted indexes list', function () {
- expect(
- reducer(undefined, sortIndexes(defaultSort, USAGE, ASC))
- ).to.deep.equal(usageSort);
+ const dispatch = (args) => args;
+ const getState = () => {
+ return {
+ indexes: defaultSort,
+ };
+ };
+ const result = reducer(
+ undefined,
+ sortIndexes(USAGE, ASC)(dispatch, getState)
+ );
+ expect(result).to.deep.equal(usageSort);
});
});
context('when sorting desc', function () {
it('returns the sorted indexes list', function () {
- expect(
- reducer(undefined, sortIndexes(defaultSort, USAGE, DESC))
- ).to.deep.equal(usageSortDesc);
+ const dispatch = (args) => args;
+ const getState = () => {
+ return {
+ indexes: defaultSort,
+ };
+ };
+ const result = reducer(
+ undefined,
+ sortIndexes(USAGE, DESC)(dispatch, getState)
+ );
+ expect(result).to.deep.equal(usageSortDesc);
});
});
});
@@ -49,17 +65,33 @@ describe('indexes module', function () {
context('when the column is Name and Definition', function () {
context('when sorting asc', function () {
it('returns the sorted indexes list', function () {
- expect(
- reducer(undefined, sortIndexes(usageSort, DEFAULT, ASC))
- ).to.deep.equal(defaultSort);
+ const dispatch = (args) => args;
+ const getState = () => {
+ return {
+ indexes: usageSort,
+ };
+ };
+ const result = reducer(
+ undefined,
+ sortIndexes(DEFAULT, ASC)(dispatch, getState)
+ );
+ expect(result).to.deep.equal(defaultSort);
});
});
context('when sorting desc', function () {
it('returns the sorted indexes list', function () {
- expect(
- reducer(undefined, sortIndexes(usageSort, DEFAULT, DESC))
- ).to.deep.equal(defaultSortDesc);
+ const dispatch = (args) => args;
+ const getState = () => {
+ return {
+ indexes: usageSort,
+ };
+ };
+ const result = reducer(
+ undefined,
+ sortIndexes(DEFAULT, DESC)(dispatch, getState)
+ );
+ expect(result).to.deep.equal(defaultSortDesc);
});
});
});
@@ -84,7 +116,11 @@ describe('indexes module', function () {
describe('#sortIndexes', function () {
it('returns the action', function () {
- expect(sortIndexes([], 'Database Name', DESC)).to.deep.equal({
+ const dispatch = (x) => x;
+ const getState = () => ({ indexes: [] });
+ expect(
+ sortIndexes('Database Name', DESC)(dispatch, getState)
+ ).to.deep.equal({
type: SORT_INDEXES,
indexes: [],
column: 'Database Name',
diff --git a/packages/compass-indexes/src/modules/sort-column.spec.js b/packages/compass-indexes/src/modules/sort-column.spec.js
index b51d1390b31..6c5ad7e5541 100644
--- a/packages/compass-indexes/src/modules/sort-column.spec.js
+++ b/packages/compass-indexes/src/modules/sort-column.spec.js
@@ -7,9 +7,11 @@ describe('sort column module', function () {
describe('#reducer', function () {
context('when an action is provided', function () {
it('returns the new column', function () {
- expect(reducer(undefined, sortIndexes(null, 'Size', ''))).to.equal(
- 'Size'
- );
+ const dispatch = (x) => x;
+ const getState = () => ({});
+ expect(
+ reducer(undefined, sortIndexes('Size', '')(dispatch, getState))
+ ).to.equal('Size');
});
});
diff --git a/packages/compass-indexes/src/modules/sort-order.spec.js b/packages/compass-indexes/src/modules/sort-order.spec.js
index e0b4e9e6c4c..32dcec67430 100644
--- a/packages/compass-indexes/src/modules/sort-order.spec.js
+++ b/packages/compass-indexes/src/modules/sort-order.spec.js
@@ -7,9 +7,11 @@ describe('sort order module', function () {
describe('#reducer', function () {
context('when an action is provided', function () {
it('returns the new order', function () {
- expect(reducer(undefined, sortIndexes(null, '', 'desc'))).to.equal(
- 'desc'
- );
+ const dispatch = (x) => x;
+ const getState = () => ({});
+ expect(
+ reducer(undefined, sortIndexes('', 'desc')(dispatch, getState))
+ ).to.equal('desc');
});
});
diff --git a/packages/compass-indexes/src/plugin.jsx b/packages/compass-indexes/src/plugin.jsx
index 7759ba3b4b7..e3ecf97b314 100644
--- a/packages/compass-indexes/src/plugin.jsx
+++ b/packages/compass-indexes/src/plugin.jsx
@@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import PropTypes from 'prop-types';
-import Indexes from './components/indexes';
+import Indexes from './components/indexes/indexes';
class Plugin extends Component {
static displayName = 'IndexesPlugin';
diff --git a/packages/compass-indexes/src/utils/index-link-helper.ts b/packages/compass-indexes/src/utils/index-link-helper.ts
index 352833d6a05..f89aa579d2b 100644
--- a/packages/compass-indexes/src/utils/index-link-helper.ts
+++ b/packages/compass-indexes/src/utils/index-link-helper.ts
@@ -31,7 +31,6 @@ const HELP_URLS = {
* The function looks up index help links.
*
* @param {String} section - The name of the section to open.
- * @returns {String} - the link.
*/
export default function getIndexHelpLink(section: keyof typeof HELP_URLS) {
return HELP_URLS[section] || null;