From eca3f5015a38b711700ac25a990f594f8f59e0db Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 3 Aug 2017 09:24:38 -0400 Subject: [PATCH 1/9] COMPASS-1101: Removing CRUD plugin --- src/internal-packages/crud/README.md | 85 --- src/internal-packages/crud/index.js | 105 ---- src/internal-packages/crud/lib/actions.js | 40 -- .../crud/lib/component/document-actions.jsx | 110 ---- .../crud/lib/component/document-footer.jsx | 246 --------- .../crud/lib/component/document-list.jsx | 296 ----------- .../crud/lib/component/document.jsx | 36 -- .../crud/lib/component/editable-document.jsx | 484 ------------------ .../crud/lib/component/editable-element.jsx | 432 ---------------- .../crud/lib/component/editable-key.jsx | 234 --------- .../crud/lib/component/editable-value.jsx | 267 ---------- .../crud/lib/component/editor/date.js | 102 ---- .../crud/lib/component/editor/double.js | 30 -- .../crud/lib/component/editor/index.js | 31 -- .../crud/lib/component/editor/int32.js | 30 -- .../crud/lib/component/editor/null.js | 43 -- .../crud/lib/component/editor/objectid.js | 73 --- .../crud/lib/component/editor/standard.js | 79 --- .../crud/lib/component/editor/string.js | 30 -- .../crud/lib/component/editor/undefined.js | 43 -- .../crud/lib/component/element-action.jsx | 33 -- .../crud/lib/component/element.jsx | 201 -------- .../lib/component/insert-document-dialog.jsx | 167 ------ .../lib/component/insert-document-footer.jsx | 124 ----- .../crud/lib/component/insert-document.jsx | 94 ---- .../crud/lib/component/line-number.jsx | 323 ------------ .../crud/lib/component/no-action.jsx | 27 - .../crud/lib/component/readonly-document.jsx | 147 ------ .../crud/lib/component/remove-action.jsx | 51 -- .../lib/component/remove-document-footer.jsx | 163 ------ .../crud/lib/component/revert-action.jsx | 51 -- .../crud/lib/component/types.jsx | 174 ------- .../crud/lib/component/utils.js | 13 - .../crud/lib/store/insert-document-store.js | 59 --- .../lib/store/load-more-documents-store.js | 77 --- .../open-insert-document-dialog-store.js | 46 -- .../crud/lib/store/remove-document-store.js | 26 - .../lib/store/reset-document-list-store.js | 86 ---- src/internal-packages/crud/package.json | 9 - .../crud/styles/document-actions.less | 29 -- .../crud/styles/document-elements.less | 53 -- .../crud/styles/document-footer.less | 107 ---- .../crud/styles/document-list.less | 19 - .../crud/styles/document.less | 31 -- .../crud/styles/editable-element-field.less | 23 - .../crud/styles/editable-element-value.less | 94 ---- .../crud/styles/editable-element.less | 299 ----------- .../styles/editable-expandable-element.less | 225 -------- .../crud/styles/element.less | 66 --- .../crud/styles/expandable-element.less | 52 -- src/internal-packages/crud/styles/index.less | 42 -- .../crud/styles/insert-document-dialog.less | 116 ----- .../crud/styles/line-number.less | 65 --- .../crud/styles/loading-indicator.less | 15 - 54 files changed, 5903 deletions(-) delete mode 100644 src/internal-packages/crud/README.md delete mode 100644 src/internal-packages/crud/index.js delete mode 100644 src/internal-packages/crud/lib/actions.js delete mode 100644 src/internal-packages/crud/lib/component/document-actions.jsx delete mode 100644 src/internal-packages/crud/lib/component/document-footer.jsx delete mode 100644 src/internal-packages/crud/lib/component/document-list.jsx delete mode 100644 src/internal-packages/crud/lib/component/document.jsx delete mode 100644 src/internal-packages/crud/lib/component/editable-document.jsx delete mode 100644 src/internal-packages/crud/lib/component/editable-element.jsx delete mode 100644 src/internal-packages/crud/lib/component/editable-key.jsx delete mode 100644 src/internal-packages/crud/lib/component/editable-value.jsx delete mode 100644 src/internal-packages/crud/lib/component/editor/date.js delete mode 100644 src/internal-packages/crud/lib/component/editor/double.js delete mode 100644 src/internal-packages/crud/lib/component/editor/index.js delete mode 100644 src/internal-packages/crud/lib/component/editor/int32.js delete mode 100644 src/internal-packages/crud/lib/component/editor/null.js delete mode 100644 src/internal-packages/crud/lib/component/editor/objectid.js delete mode 100644 src/internal-packages/crud/lib/component/editor/standard.js delete mode 100644 src/internal-packages/crud/lib/component/editor/string.js delete mode 100644 src/internal-packages/crud/lib/component/editor/undefined.js delete mode 100644 src/internal-packages/crud/lib/component/element-action.jsx delete mode 100644 src/internal-packages/crud/lib/component/element.jsx delete mode 100644 src/internal-packages/crud/lib/component/insert-document-dialog.jsx delete mode 100644 src/internal-packages/crud/lib/component/insert-document-footer.jsx delete mode 100644 src/internal-packages/crud/lib/component/insert-document.jsx delete mode 100644 src/internal-packages/crud/lib/component/line-number.jsx delete mode 100644 src/internal-packages/crud/lib/component/no-action.jsx delete mode 100644 src/internal-packages/crud/lib/component/readonly-document.jsx delete mode 100644 src/internal-packages/crud/lib/component/remove-action.jsx delete mode 100644 src/internal-packages/crud/lib/component/remove-document-footer.jsx delete mode 100644 src/internal-packages/crud/lib/component/revert-action.jsx delete mode 100644 src/internal-packages/crud/lib/component/types.jsx delete mode 100644 src/internal-packages/crud/lib/component/utils.js delete mode 100644 src/internal-packages/crud/lib/store/insert-document-store.js delete mode 100644 src/internal-packages/crud/lib/store/load-more-documents-store.js delete mode 100644 src/internal-packages/crud/lib/store/open-insert-document-dialog-store.js delete mode 100644 src/internal-packages/crud/lib/store/remove-document-store.js delete mode 100644 src/internal-packages/crud/lib/store/reset-document-list-store.js delete mode 100644 src/internal-packages/crud/package.json delete mode 100644 src/internal-packages/crud/styles/document-actions.less delete mode 100644 src/internal-packages/crud/styles/document-elements.less delete mode 100644 src/internal-packages/crud/styles/document-footer.less delete mode 100644 src/internal-packages/crud/styles/document-list.less delete mode 100644 src/internal-packages/crud/styles/document.less delete mode 100644 src/internal-packages/crud/styles/editable-element-field.less delete mode 100644 src/internal-packages/crud/styles/editable-element-value.less delete mode 100644 src/internal-packages/crud/styles/editable-element.less delete mode 100644 src/internal-packages/crud/styles/editable-expandable-element.less delete mode 100644 src/internal-packages/crud/styles/element.less delete mode 100644 src/internal-packages/crud/styles/expandable-element.less delete mode 100644 src/internal-packages/crud/styles/index.less delete mode 100644 src/internal-packages/crud/styles/insert-document-dialog.less delete mode 100644 src/internal-packages/crud/styles/line-number.less delete mode 100644 src/internal-packages/crud/styles/loading-indicator.less diff --git a/src/internal-packages/crud/README.md b/src/internal-packages/crud/README.md deleted file mode 100644 index 3a1c06ed00d..00000000000 --- a/src/internal-packages/crud/README.md +++ /dev/null @@ -1,85 +0,0 @@ -# Compass CRUD Package - -Provide functionality shown in the "Documents" tab in the collection view. - -## Available Resources in the App Registry - -### Components - -#### Definitions - -| Key | Description | -|---------------------|------------------------------| -| `CRUD.Document` | Renders a single document. | -| `CRUD.DocumentList` | Renders a list of documents. | - -### Actions - -| Key | Description | -|----------------|-------------------------------| -| `CRUD.Actions` | All the CRUD related actions. | - -### Stores - -| Key | Description -|-------------------------------|---------------------------------------------------------| -| `CRUD.InsertDocumentStore` | Triggers when a document is inserted. | -| `CRUD.ResetDocumentListStore` | Triggers when the query filter is reset. | -| `CRUD.LoadMoreDocumentsStore` | Triggers when more documents are fetched via scrolling. | - -## Usage - -Render an editable document in a React component. - -```jsx -const app = require('hadron-app'); -const React = require('react'); - -class MyComponent extends React.Component { - constructor(props) { - super(props); - this.Document = app.appRegistry.getRole('CRUD.Document')[0].component; - } - render() { - return (); - } -} -``` - -Render a non-editable pre-expanded document in a React component. - -```jsx -const app = require('hadron-app'); -const React = require('react'); - -class MyComponent extends React.Component { - constructor(props) { - super(props); - this.Document = app.appRegistry.getRole('CRUD.Document')[0].component; - } - render() { - return (); - } -} -``` - -Listen to the various CRUD actions. - -```javascript -const app = require('hadron-app'); -const CrudActions = app.appRegistry.getAction('CRUD.Actions'); - -CrudActions.documentRemoved.listen((id) => { - console.log(`Document with _id ${id} removed.`); -}); - -CrudActions.openInsertDocumentDialog((doc, clone) => { - if (clone) { - console.log('Opening insert dialog with cloned document'); - } -}); - -CrudActions.insertDocument((doc) => { - console.log('Inserting document into db'); -}); -``` diff --git a/src/internal-packages/crud/index.js b/src/internal-packages/crud/index.js deleted file mode 100644 index 5faac8060c8..00000000000 --- a/src/internal-packages/crud/index.js +++ /dev/null @@ -1,105 +0,0 @@ -const app = require('hadron-app'); -const Document = require('./lib/component/document'); -const DocumentList = require('./lib/component/document-list'); -const Actions = require('./lib/actions'); -const InsertDocumentStore = require('./lib/store/insert-document-store'); -const ResetDocumentListStore = require('./lib/store/reset-document-list-store'); -const LoadMoreDocumentsStore = require('./lib/store/load-more-documents-store'); -const { - StandardEditor, - DateEditor, - StringEditor, - Int32Editor, - DoubleEditor, - NullEditor, - UndefinedEditor, - ObjectIDEditor -} = require('./lib/component/editor'); - -const COLLECTION_TAB_ROLE = { - component: DocumentList, - name: 'DOCUMENTS', - hasQueryHistory: true, - order: 1 -}; - -const DOCUMENT_ROLE = { - component: Document, - name: 'STANDARD', - order: 1 -}; - -const STANDARD_EDITOR_ROLE = { - component: StandardEditor -}; - -const DATE_EDITOR_ROLE = { - component: DateEditor -}; - -const DOUBLE_EDITOR_ROLE = { - component: DoubleEditor -}; - -const STRING_EDITOR_ROLE = { - component: StringEditor -}; - -const INT32_EDITOR_ROLE = { - component: Int32Editor -}; - -const NULL_EDITOR_ROLE = { - component: NullEditor -}; - -const UNDEFINED_EDITOR_ROLE = { - component: UndefinedEditor -}; - -const OBJECT_ID_EDITOR_ROLE = { - component: ObjectIDEditor -}; - -/** - * Activate all the components in the CRUD package. - */ -function activate(appRegistry) { - appRegistry.registerRole('Collection.Tab', COLLECTION_TAB_ROLE); - appRegistry.registerRole('CRUD.Document', DOCUMENT_ROLE); - appRegistry.registerRole('CRUD.Editor.Standard', STANDARD_EDITOR_ROLE); - appRegistry.registerRole('CRUD.Editor.Date', DATE_EDITOR_ROLE); - appRegistry.registerRole('CRUD.Editor.Double', DOUBLE_EDITOR_ROLE); - appRegistry.registerRole('CRUD.Editor.String', STRING_EDITOR_ROLE); - appRegistry.registerRole('CRUD.Editor.Int32', INT32_EDITOR_ROLE); - appRegistry.registerRole('CRUD.Editor.Null', NULL_EDITOR_ROLE); - appRegistry.registerRole('CRUD.Editor.Undefined', UNDEFINED_EDITOR_ROLE); - appRegistry.registerRole('CRUD.Editor.ObjectID', OBJECT_ID_EDITOR_ROLE); - appRegistry.registerAction('CRUD.Actions', Actions); - appRegistry.registerStore('CRUD.InsertDocumentStore', InsertDocumentStore); - appRegistry.registerStore('CRUD.ResetDocumentListStore', ResetDocumentListStore); - appRegistry.registerStore('CRUD.LoadMoreDocumentsStore', LoadMoreDocumentsStore); -} - -/** - * Deactivate all the components in the CRUD package. - */ -function deactivate() { - app.appRegistry.deregisterRole('Collection.Tab', COLLECTION_TAB_ROLE); - app.appRegistry.deregisterRole('CRUD.Document', DOCUMENT_ROLE); - app.appRegistry.deregisterRole('CRUD.Editor.Standard', STANDARD_EDITOR_ROLE); - app.appRegistry.deregisterRole('CRUD.Editor.Date', DATE_EDITOR_ROLE); - app.appRegistry.deregisterRole('CRUD.Editor.Double', DOUBLE_EDITOR_ROLE); - app.appRegistry.deregisterRole('CRUD.Editor.String', STRING_EDITOR_ROLE); - app.appRegistry.deregisterRole('CRUD.Editor.Int32', INT32_EDITOR_ROLE); - app.appRegistry.deregisterRole('CRUD.Editor.Null', NULL_EDITOR_ROLE); - app.appRegistry.deregisterRole('CRUD.Editor.Undefined', UNDEFINED_EDITOR_ROLE); - app.appRegistry.deregisterRole('CRUD.Editor.ObjectID', OBJECT_ID_EDITOR_ROLE); - app.appRegistry.deregisterAction('CRUD.Actions'); - app.appRegistry.deregisterStore('CRUD.InsertDocumentStore'); - app.appRegistry.deregisterStore('CRUD.ResetDocumentListStore'); - app.appRegistry.deregisterStore('CRUD.LoadMoreDocumentsStore'); -} - -module.exports.activate = activate; -module.exports.deactivate = deactivate; diff --git a/src/internal-packages/crud/lib/actions.js b/src/internal-packages/crud/lib/actions.js deleted file mode 100644 index c2e80b5aede..00000000000 --- a/src/internal-packages/crud/lib/actions.js +++ /dev/null @@ -1,40 +0,0 @@ -const fs = require('fs'); -const Reflux = require('reflux'); -const debug = require('debug')('mongodb-compass:crud:actions'); - -const Actions = Reflux.createActions([ - 'documentRemoved', - 'openInsertDocumentDialog', - 'closeInsertDocumentDialog', - 'insertDocument', - 'fileDropped', - 'refreshDocuments', - 'closeAllMenus', - 'fetchNextDocuments', - 'elementInvalid', - 'elementValid' -]); - -document.ondragover = document.ondrop = (ev) => { - ev.preventDefault(); -}; - -document.body.ondrop = (ev) => { - ev.preventDefault(); - const file = ev.dataTransfer.files[0]; - if (file) { - const path = file.path; - fs.readFile(path, 'utf-8', (error, data) => { - if (error) { - debug(`Error opening file '${path}': ${error.message}`); - } - try { - Actions.openInsertDocumentDialog(JSON.parse(data), false); - } catch (e) { - debug(`File ${path} is not a single parseable JSON document: ${e.message}`); - } - }); - } -}; - -module.exports = Actions; diff --git a/src/internal-packages/crud/lib/component/document-actions.jsx b/src/internal-packages/crud/lib/component/document-actions.jsx deleted file mode 100644 index f3a1432db53..00000000000 --- a/src/internal-packages/crud/lib/component/document-actions.jsx +++ /dev/null @@ -1,110 +0,0 @@ -const React = require('react'); -const PropTypes = require('prop-types'); -const { IconButton } = require('hadron-react-buttons'); - -/** - * Override once the button to be able to update. - */ -class UpdatableIconButton extends IconButton { - - /** - * By default should always need to to re-render itself. - * - * @returns {Boolean} Always true. - */ - shouldComponentUpdate() { - return true; - } -} - -/** - * Component for actions on the document. - */ -class DocumentActions extends React.Component { - - /** - * Instantiate the actions. - * - * @param {Object} props - The properties. - */ - constructor(props) { - super(props); - this.state = { allExpanded: props.allExpanded }; - } - - /** - * Set the state when new props are received. - * - * @param {Object} nextProps - The new props. - */ - componentWillReceiveProps(nextProps) { - if (nextProps.allExpanded !== this.state.allExpanded) { - this.setState({ allExpanded: nextProps.allExpanded }); - } - } - - /** - * Render the expand all button. - * - * @returns {React.Component} The expand all button. - */ - renderExpandAll() { - const title = this.state.allExpanded ? 'Collapse All' : 'Expand All'; - const iconClass = this.state.allExpanded ? 'fa-angle-down' : 'fa-angle-right'; - return ( - - ); - } - - /** - * Render the actions. - * - * @returns {Component} The actions component. - */ - render() { - return ( -
-
- {this.renderExpandAll()} -
-
- - - -
-
- ); - } -} - -DocumentActions.displayName = 'DocumentActions'; - -DocumentActions.propTypes = { - edit: PropTypes.func.isRequired, - remove: PropTypes.func.isRequired, - clone: PropTypes.func.isRequired, - allExpanded: PropTypes.bool.isRequired, - expandAll: PropTypes.func.isRequired -}; - -module.exports = DocumentActions; diff --git a/src/internal-packages/crud/lib/component/document-footer.jsx b/src/internal-packages/crud/lib/component/document-footer.jsx deleted file mode 100644 index 716959eb5dc..00000000000 --- a/src/internal-packages/crud/lib/component/document-footer.jsx +++ /dev/null @@ -1,246 +0,0 @@ -const _ = require('lodash'); -const React = require('react'); -const PropTypes = require('prop-types'); -const { Element } = require('hadron-document'); -const { TextButton } = require('hadron-react-buttons'); - -/** - * The progress mode. - */ -const PROGRESS = 'Progress'; - -/** - * The success mode. - */ -const SUCCESS = 'Success'; - -/** - * The error mode. - */ -const ERROR = 'Error'; - -/** - * The editing mode. - */ -const EDITING = 'Editing'; - -/** - * The viewing mode. - */ -const VIEWING = 'Viewing'; - -/** - * The invalid message. - */ -const INVALID_MESSAGE = 'Update not permitted while document contains errors.'; - -/** - * Map of modes to styles. - */ -const MODES = { - 'Progress': 'is-in-progress', - 'Success': 'is-success', - 'Error': 'is-error', - 'Editing': 'is-modified', - 'Viewing': 'is-viewing' -}; - -/** - * The empty message. - */ -const EMPTY = ''; - -/** - * The modified message. - */ -const MODIFIED = 'Document Modified.'; - -/** - * The updating message. - */ -const UPDATING = 'Updating Document.'; - -/** - * The updated message. - */ -const UPDATED = 'Document Updated.'; - -/** - * Component for a the edit document footer. - */ -class DocumentFooter extends React.Component { - - /** - * The component constructor. - * - * @param {Object} props - The properties. - */ - constructor(props) { - super(props); - this.doc = props.doc; - this.updateStore = props.updateStore; - this.actions = props.actions; - this.state = { mode: VIEWING, message: EMPTY }; - this.invalidElements = []; - } - - /** - * Subscribe to the update store on mount. - */ - componentDidMount() { - this.unsubscribeUpdate = this.updateStore.listen(this.handleStoreUpdate.bind(this)); - - this.unsubscribeAdded = this.handleModification.bind(this); - this.unsubscribeEdited = this.handleModification.bind(this); - this.unsubscribeRemoved = this.handleModification.bind(this); - this.unsubscribeReverted = this.handleModification.bind(this); - this.unsubscribeInvalid = this.handleInvalid.bind(this); - this.unsubscribeValid = this.handleValid.bind(this); - - this.doc.on(Element.Events.Added, this.unsubscribeAdded); - this.doc.on(Element.Events.Edited, this.unsubscribeEdited); - this.doc.on(Element.Events.Removed, this.unsubscribeRemoved); - this.doc.on(Element.Events.Reverted, this.unsubscribeReverted); - this.doc.on(Element.Events.Invalid, this.unsubscribeInvalid); - this.doc.on(Element.Events.Valid, this.unsubscribeValid); - } - - /** - * Unsubscribe from the udpate store on unmount. - */ - componentWillUnmount() { - this.unsubscribeUpdate(); - this.doc.removeListener(Element.Events.Added, this.unsubscribeAdded); - this.doc.removeListener(Element.Events.Edited, this.unsubscribeEdited); - this.doc.removeListener(Element.Events.Removed, this.unsubscribeRemoved); - this.doc.removeListener(Element.Events.Reverted, this.unsubscribeReverted); - this.doc.removeListener(Element.Events.Invalid, this.unsubscribeInvalid); - this.doc.removeListener(Element.Events.Valid, this.unsubscribeValid); - } - - /** - * Handle the user clicking the cancel button. - */ - handleCancel() { - this.doc.cancel(); - this.setState({ mode: VIEWING, message: EMPTY }); - } - - /** - * Handle an error with the document update. - * - * @param {Error} error - The error. - */ - handleError(error) { - this.setState({ mode: ERROR, message: error.message }); - } - - handleValid(uuid) { - _.pull(this.invalidElements, uuid); - } - - handleInvalid(uuid) { - if (!_.includes(this.invalidElements, uuid)) { - this.invalidElements.push(uuid); - this.handleModification(); - } - } - - /** - * Handle modification to the document. - */ - handleModification() { - const isModified = this.doc.isModified(); - if (this.hasErrors()) { - this.setState({ mode: ERROR, message: INVALID_MESSAGE }); - } else { - this.setState({ - mode: isModified ? EDITING : VIEWING, - message: isModified ? MODIFIED : EMPTY - }); - } - } - - /** - * Handle the user clicking the update button. - */ - handleUpdate() { - const object = this.props.doc.generateObject(); - this.setState({ mode: PROGRESS, message: UPDATING }); - this.actions.update(object); - } - - /** - * Handle a successful document update. - */ - handleSuccess() { - this.setState({ mode: SUCCESS, message: UPDATED }); - } - - /** - * Handles a trigger from the store. - * - * @param {Boolean} success - If the update succeeded. - * @param {Object} object - The error or document. - */ - handleStoreUpdate(success, object) { - if (success) { - this.handleSuccess(); - } else { - this.handleError(object); - } - } - - hasErrors() { - return this.invalidElements.length > 0; - } - - /** - * Get the style of the footer based on the current mode. - * - * @returns {String} The style. - */ - style() { - return `document-footer document-footer-${MODES[this.state.mode]}`; - } - - /** - * Render the footer. - * - * @returns {Component} The footer component. - */ - render() { - return ( -
-
- {this.state.message} -
-
- - -
-
- ); - } -} - -DocumentFooter.displayName = 'DocumentFooter'; - -DocumentFooter.propTypes = { - doc: PropTypes.object.isRequired, - actions: PropTypes.object.isRequired, - updateStore: PropTypes.object.isRequired -}; - -module.exports = DocumentFooter; diff --git a/src/internal-packages/crud/lib/component/document-list.jsx b/src/internal-packages/crud/lib/component/document-list.jsx deleted file mode 100644 index 3422aebac45..00000000000 --- a/src/internal-packages/crud/lib/component/document-list.jsx +++ /dev/null @@ -1,296 +0,0 @@ -const _ = require('lodash'); -const React = require('react'); -const uuid = require('uuid'); -const ObjectID = require('bson').ObjectID; -const Action = require('../actions'); -const { StatusRow } = require('hadron-react-components'); -const ResetDocumentListStore = require('../store/reset-document-list-store'); -const LoadMoreDocumentsStore = require('../store/load-more-documents-store'); -const RemoveDocumentStore = require('../store/remove-document-store'); -const InsertDocumentStore = require('../store/insert-document-store'); -const InsertDocumentDialog = require('./insert-document-dialog'); -const Actions = require('../actions'); - -const debug = require('debug')('mongodb-compass:crud:component'); - -/* eslint no-return-assign:0 */ - -/** - * The full document list container class. - */ -const LIST_CLASS = 'document-list'; - -/** - * The scroll event name. - */ -const SCROLL_EVENT = 'scroll'; - -/** - * The loading more class. - */ -const LOADING = 'loading-indicator'; - -/** - * Loading indicator is loading. - */ -const IS_LOADING = `${LOADING}-is-loading`; - -/** - * The list item test id. - */ -const LIST_ITEM_TEST_ID = 'document-list-item'; - -/** - * Component for the entire document list. - */ -class DocumentList extends React.Component { - - /** - * The component constructor. - * - * @param {Object} props - The properties. - */ - constructor(props) { - super(props); - const appRegistry = global.hadronApp.appRegistry; - this.samplingMessage = appRegistry.getComponent('Query.SamplingMessage'); - this.CollectionStore = appRegistry.getStore('App.CollectionStore'); - this.NamespaceStore = appRegistry.getStore('App.NamespaceStore'); - this.projection = false; - this.queryBar = appRegistry.getComponent('Query.QueryBar'); - this.QueryChangedStore = appRegistry.getStore('Query.ChangedStore'); - this.Document = appRegistry.getRole('CRUD.Document')[0].component; - this.state = { - docs: [], - nextSkip: 0, - namespace: this.NamespaceStore.ns, - loading: false - }; - } - - /** - * Fetch the state when the component mounts. - */ - componentDidMount() { - this.attachScrollEvent(); - this.unsubscribeReset = ResetDocumentListStore.listen(this.handleReset.bind(this)); - this.unsubscribeLoadMore = LoadMoreDocumentsStore.listen(this.handleLoadMore.bind(this)); - this.unsubscribeRemove = RemoveDocumentStore.listen(this.handleRemove.bind(this)); - this.unsubscribeInsert = InsertDocumentStore.listen(this.handleInsert.bind(this)); - this.unsubscribeQueryStore = this.QueryChangedStore.listen(this.handleQueryChanged.bind(this)); - } - - /** - * Unsibscribe from the document list store when unmounting. - */ - componentWillUnmount() { - this.unsubscribeReset(); - this.unsubscribeLoadMore(); - this.unsubscribeRemove(); - this.unsubscribeInsert(); - } - - /** - * Attach the scroll event to the parent container. - */ - attachScrollEvent() { - this._node.parentNode.parentNode.addEventListener( - SCROLL_EVENT, - this.handleScroll.bind(this) - ); - } - - /** - * Handle the loading of more documents. - * - * @param {Object} error - Error when trying to load more documents. - * @param {Array} documents - The next batch of documents. - */ - handleLoadMore(error, documents) { - // If not resetting we append the documents to the existing - // list and increment the page. The loaded count is incremented - // by the number of new documents. - this.setState({ - docs: this.state.docs.concat(this.renderDocuments(documents)), - nextSkip: (this.state.nextSkip + documents.length), - loadedCount: (this.state.loadedCount + documents.length), - error: error, - loading: false - }); - } - - /** - * Handle the reset of the document list. - * - * @param {Object} error - Error when trying to reset the document list. - * @param {Array} documents - The documents. - * @param {Integer} count - The count. - */ - handleReset(error, documents, count) { - if (error) { - this.setState({ error: error }); - } else { - // If resetting, then we need to go back to page one with - // the documents as the filter changed. The loaded count and - // total count are reset here as well. - this.setState({ - docs: this.renderDocuments(documents), - nextSkip: documents.length, - count: count, - loadedCount: documents.length, - namespace: this.NamespaceStore.ns, - error: error - }); - } - } - - /** - * Handles removal of a document from the document list. - * - * @param {Object} id - The id of the removed document. - */ - handleRemove(id) { - const index = _.findIndex(this.state.docs, (component) => { - const _id = component.props.children.props.doc._id; - if (id instanceof ObjectID) { - return id.equals(_id); - } - return _id === id; - }); - this.state.docs.splice(index, 1); - this.setState({ - docs: this.state.docs, - loadedCount: (this.state.loadedCount - 1), - nextSkip: (this.state.nextSkip - 1) - }); - } - - /** - * Handle the scroll event of the parent container. - * - * @param {Event} evt - The scroll event. - */ - handleScroll(evt) { - const container = evt.srcElement; - if (container.scrollTop === (container.scrollHeight - container.offsetHeight)) { - this.loadMore(); - } - } - - /** - * Handle opening of the insert dialog. - */ - handleOpenInsert() { - Actions.openInsertDocumentDialog({ _id: new ObjectID(), '': '' }, false); - } - - /** - * Handle insert of a new document. - * - * @param {Boolean} success - If the insert was successful. - * @param {Object} doc - The raw document that was inserted. - */ - handleInsert(success, doc) { - if (success) { - this.setState({ - docs: this.state.docs.concat(this.renderDocuments([doc])), - nextSkip: (this.state.nextSkip + 1), - loadedCount: (this.state.loadedCount + 1), - count: this.state.count + 1 - }); - } - } - - handleQueryChanged(state) { - debug('state', state); - this.projection = state.project !== null; - } - - /** - * Get the next batch of documents. Will only fire if there are more documents - * in the collection to load. - */ - loadMore() { - if (!this.state.loading && (this.state.loadedCount < this.state.count)) { - this.setState({ loading: true }); - Action.fetchNextDocuments(this.state.nextSkip); - } - } - - /** - * Get the key for a doc. - * - * @returns {String} The unique key. - */ - _key() { - return uuid.v4(); - } - - /** - * Get the document list item components. - * - * @param {Array} docs - The raw documents. - * - * @return {Array} The document list item components. - */ - renderDocuments(docs) { - return _.map(docs, (doc) => { - const editable = !this.CollectionStore.isReadonly() && !this.projection; - return ( -
  • - -
  • - ); - }); - } - - /** - * Render the list of documents. - * - * @returns {React.Component} The list. - */ - renderContent() { - if (this.state.error) { - return ( - - {this.state.error.message} - - ); - } - return ( -
    -
    -
      this._node = c}> - {this.state.docs} - -
    -
    - -
    -
    -
    - ); - } - - /** - * Render the document list. - * - * @returns {React.Component} The document list. - */ - render() { - return ( -
    -
    - - -
    - {this.renderContent()} -
    - ); - } -} - -DocumentList.displayName = 'DocumentList'; -DocumentList.Document = Document; - -module.exports = DocumentList; diff --git a/src/internal-packages/crud/lib/component/document.jsx b/src/internal-packages/crud/lib/component/document.jsx deleted file mode 100644 index cd294eaa883..00000000000 --- a/src/internal-packages/crud/lib/component/document.jsx +++ /dev/null @@ -1,36 +0,0 @@ -const React = require('react'); -const PropTypes = require('prop-types'); -const EditableDocument = require('./editable-document'); -const ReadonlyDocument = require('./readonly-document'); - -/** - * Component for a single document in a list of documents. - */ -class Document extends React.Component { - - /** - * Render a single document list item. - * - * @returns {React.Component} The component. - */ - render() { - if (this.props.editable) { - return (); - } - return ( - - ); - } -} - -Document.displayName = 'Document'; - -Document.propTypes = { - doc: PropTypes.object.isRequired, - editable: PropTypes.bool, - expandAll: PropTypes.bool -}; - -module.exports = Document; diff --git a/src/internal-packages/crud/lib/component/editable-document.jsx b/src/internal-packages/crud/lib/component/editable-document.jsx deleted file mode 100644 index 8714add8760..00000000000 --- a/src/internal-packages/crud/lib/component/editable-document.jsx +++ /dev/null @@ -1,484 +0,0 @@ -const app = require('hadron-app'); -const React = require('react'); -const PropTypes = require('prop-types'); -const Reflux = require('reflux'); -const HadronDocument = require('hadron-document'); -const Element = require('hadron-document').Element; -const Actions = require('../actions'); -const EditableElement = require('./editable-element'); -const DocumentActions = require('./document-actions'); -const DocumentFooter = require('./document-footer'); -const RemoveDocumentFooter = require('./remove-document-footer'); - -/** - * The arrow up class. - */ -const ARROW_UP = 'fa fa-arrow-up'; - -/** - * The arrow down class. - */ -const ARROW_DOWN = 'fa fa-arrow-down'; - -/** - * The base class. - */ -const BASE = 'document'; - -/** - * The elements class. - */ -const ELEMENTS = `${BASE}-elements`; - -/** - * The field limit. - */ -const FIELD_LIMIT = 30; - -/** - * The expander class. - */ -const EXPANDER = 'btn btn-default btn-xs'; - -/** - * The test id. - */ -const TEST_ID = 'editable-document'; - -/** - * The delete error message. - */ -const DELETE_ERROR = new Error('Cannot delete documents that do not have an _id field.'); - -/** - * Component for a single editable document in a list of documents. - */ -class EditableDocument extends React.Component { - - /** - * The component constructor. - * - * @param {Object} props - The properties. - */ - constructor(props) { - super(props); - this.doc = EditableDocument.loadDocument(props.doc); - this.state = { - editing: false, - deleting: false, - deleteFinished: false, - expandAll: false, - expanded: false - }; - - // Actions need to be scoped to the single document component and not - // global singletons. - this.actions = Reflux.createActions([ 'update', 'remove', 'cancelRemove' ]); - - // The update store needs to be scoped to a document and not a global - // singleton. - this.updateStore = this.createUpdateStore(this.actions); - this.removeStore = this.createRemoveStore(this.actions); - } - - /** - * Subscribe to the update store on mount. - */ - componentDidMount() { - this.unsubscribeUpdate = this.updateStore.listen(this.handleStoreUpdate.bind(this)); - this.unsubscribeRemove = this.removeStore.listen(this.handleStoreRemove.bind(this)); - this.subscribeToDocumentEvents(); - } - - /** - * Unsubscribe from the udpate store on unmount. - */ - componentWillUnmount() { - this.unsubscribeUpdate(); - this.unsubscribeRemove(); - this.unsubscribeFromDocumentEvents(); - } - - /** - * Load the hadron document for the provided document. - * - * @param {Object} doc - The document to load. - * - * @returns {HadronDocument} The hadron document. - */ - static loadDocument(doc) { - return new HadronDocument(doc); - } - - subscribeToDocumentEvents() { - this.unsubscribeFromDocumentEvents(); - - if (!this.unsubscribeAdded) { - this.unsubscribeAdded = this.handleModify.bind(this); - this.unsubscribeRemoved = this.handleModify.bind(this); - this.unsubscribeCancel = this.handleCancel.bind(this); - } - - this.doc.on(Element.Events.Added, this.unsubscribeAdded); - this.doc.on(Element.Events.Removed, this.unsubscribeRemoved); - this.doc.on(HadronDocument.Events.Cancel, this.unsubscribeCancel); - } - - unsubscribeFromDocumentEvents() { - if (this.unsubscribeAdded) { - this.doc.removeListener(Element.Events.Added, this.unsubscribeAdded); - this.doc.removeListener(Element.Events.Removed, this.unsubscribeRemoved); - this.doc.removeListener(HadronDocument.Events.Cancel, this.unsubscribeCancel); - } - } - - /** - * Create the scoped update store. - * - * @param {Action} actions - The component reflux actions. - * - * @returns {Store} The scoped store. - */ - createUpdateStore(actions) { - return Reflux.createStore({ - - /** - * Initialize the store. - */ - init: function() { - this.ns = app.appRegistry.getStore('App.NamespaceStore').ns; - this.listenTo(actions.update, this.update); - }, - - /** - * Update the document in the database. - * - * @param {Object} object - The replacement document. - * - * @todo: Durran: Determine shard key. - */ - update: function(object) { - // TODO (@thomasr) this does not work for projections - app.dataService.findOneAndReplace( - this.ns, - { _id: object._id }, - object, - { returnOriginal: false, promoteValues: false }, - this.handleResult - ); - }, - - /** - * Handle the result from the driver. - * - * @param {Error} error - The error. - * @param {Object} doc - The document. - * - * @returns {Object} The trigger event. - */ - handleResult: function(error, doc) { - return (error) ? this.trigger(false, error) : this.trigger(true, doc); - } - }); - } - - /** - * Create the scoped remove store. - * - * @param {Action} actions - The component reflux actions. - * - * @returns {Store} The scoped store. - */ - createRemoveStore(actions) { - return Reflux.createStore({ - - /** - * Initialize the store. - */ - init: function() { - this.ns = app.appRegistry.getStore('App.NamespaceStore').ns; - this.listenTo(actions.remove, this.remove); - }, - - /** - * Remove the document from the collection. - * - * @param {Object} object - The object to delete. - */ - remove: function(object) { - const id = object.getId(); - if (id) { - app.dataService.deleteOne(this.ns, { _id: id }, {}, this.handleResult); - } else { - this.handleResult(DELETE_ERROR); - } - }, - - /** - * Handle the result from the driver. - * - * @param {Error} error - The error. - * @param {Object} result - The document. - * - * @returns {Object} The trigger event. - */ - handleResult: function(error, result) { - return (error) ? this.trigger(false, error) : this.trigger(true, result); - } - }); - } - - /** - * Handle clicking the expand button. - */ - handleExpandClick() { - this.setState({ expanded: !this.state.expanded }); - } - - /** - * Handles a trigger from the store. - * - * @param {Boolean} success - If the update succeeded. - * @param {Object} object - The error or document. - */ - handleStoreUpdate(success, object) { - if (this.state.editing) { - if (success) { - this.handleUpdateSuccess(object); - } - } - } - - /** - * Handles a trigger from the store. - * - * @param {Boolean} success - If the update succeeded. - * @param {Object} object - The error or document. - */ - handleStoreRemove(success) { - if (success) { - this.handleRemoveSuccess(); - } - } - - /** - * Handle a sucessful update. - * - * @param {Object} doc - The updated document. - */ - handleUpdateSuccess(doc) { - this.doc = EditableDocument.loadDocument(doc); - this.subscribeToDocumentEvents(); - setTimeout(() => { - this.setState({ editing: false }); - }, 500); - } - - /** - * Handle a sucessful update. - * - * @param {Object} doc - The updated document. - */ - handleRemoveSuccess() { - this.setState({ deleting: false, deleteFinished: true }); - Actions.documentRemoved(this.props.doc._id); - } - - /** - * Handles canceling edits to the document. - */ - handleCancel() { - this.setState({ editing: false }); - } - - /** - * Handle cloning of the document. - */ - handleClone() { - Actions.openInsertDocumentDialog(this.doc.generateObject(), true); - } - - /** - * Handles document deletion. - */ - handleDelete() { - this.setState({ editing: false, deleting: true, expanded: true }); - } - - /** - * Handles canceling a delete. - */ - handleCancelDelete() { - this.setState({ deleting: false }); - } - - /** - * Handle the edit click. - */ - handleEdit() { - this.setState({ editing: true, expanded: true }); - } - - /** - * Handles modification to the document. - */ - handleModify() { - this.setState({}); - } - - /** - * Handle clicking the expand all button. - */ - handleExpandAll() { - this.setState({ expandAll: !this.state.expandAll }); - } - - /** - * Get the current style of the document div. - * - * @returns {String} The document class name. - */ - style() { - let style = BASE; - if (this.state.editing) { - style = style.concat(' document-is-editing'); - } - if (this.state.deleting && !this.state.deleteFinished) { - style = style.concat(' document-is-deleting'); - } - return style; - } - - /** - * Render the actions component. - * - * @returns {Component} The actions component. - */ - renderActions() { - if (!this.state.editing && !this.state.deleting) { - return ( - - ); - } - } - - /** - * Get the elements for the document. If we are editing, we get editable elements, - * otherwise the readonly elements are returned. - * - * @returns {Array} The elements. - */ - renderElements() { - const components = []; - let index = 0; - for (const element of this.doc.elements) { - components.push(( - - )); - index++; - } - return components; - } - - /** - * Render the expander bar. - * - * @returns {React.Component} The expander bar. - */ - renderExpansion() { - if (this.doc.elements.size > FIELD_LIMIT && !this.state.editing && !this.state.deleting) { - return ( - - ); - } - } - - /** - * Render the expansion text. - * - * @returns {String} The text. - */ - renderExpansionText() { - const extraFields = this.doc.elements.size - FIELD_LIMIT; - if (this.state.expanded) { - return `Hide ${extraFields} fields`; - } - return `Show ${extraFields} more fields`; - } - - /** - * Render the footer component. - * - * @returns {Component} The footer component. - */ - renderFooter() { - if (this.state.editing) { - return ( - - ); - } else if (this.state.deleting) { - return ( - - ); - } - } - - /** - * Render the style for the expansion icon. - * - * @returns {String} The style. - */ - renderIconStyle() { - return this.state.expanded ? ARROW_UP : ARROW_DOWN; - } - - /** - * Render a single document list item. - * - * @returns {React.Component} The component. - */ - render() { - return ( -
    -
      - {this.renderElements()} - {this.renderExpansion()} -
    - {this.renderActions()} - {this.renderFooter()} -
    - ); - } -} - -EditableDocument.displayName = 'EditableDocument'; - -EditableDocument.propTypes = { - doc: PropTypes.object.isRequired, - editable: PropTypes.bool, - expandAll: PropTypes.bool -}; - -module.exports = EditableDocument; diff --git a/src/internal-packages/crud/lib/component/editable-element.jsx b/src/internal-packages/crud/lib/component/editable-element.jsx deleted file mode 100644 index 00ba3ae3c3f..00000000000 --- a/src/internal-packages/crud/lib/component/editable-element.jsx +++ /dev/null @@ -1,432 +0,0 @@ -const React = require('react'); -const PropTypes = require('prop-types'); -const getComponent = require('hadron-react-bson'); -const { Element } = require('hadron-document'); -const EditableKey = require('./editable-key'); -const EditableValue = require('./editable-value'); -const ElementAction = require('./element-action'); -const LineNumber = require('./line-number'); -const Types = require('./types'); - -/** - * The BEM base style name for the element. - */ -const BEM_BASE = 'editable-element'; - -/** - * The BEM base style name for the expandable element. - */ -const BEM_EXP_BASE = 'editable-expandable-element'; - -/** - * The added constant. - */ -const ADDED = 'is-added'; - -/** - * The edited constant. - */ -const EDITED = 'is-edited'; - -/** - * The editing constant. - */ -const EDITING = 'is-editing'; - -/** - * The field limit. - */ -const FIELD_LIMIT = 30; - -/** - * The removed constant. - */ -const REMOVED = 'is-removed'; - -/** - * The expanded class name. - */ -const EXPANDED = 'is-expanded'; - -/** - * The class for the document itself. - */ -const CHILDREN = `${BEM_EXP_BASE}-children`; - -/** - * The header class for expandable elements. - */ -const HEADER = `${BEM_EXP_BASE}-header`; - -/** - * The expandable label class. - */ -const HEADER_LABEL = `${HEADER}-label`; - -/** - * The separator style. - */ -const SEPARATOR = 'element-separator'; - -/** - * The field class. - */ -const FIELD_CLASS = 'editable-element-field'; - -/** - * Wrapper class. - */ -const WRAPPER = 'editable-element-value-wrapper'; - -/** - * General editable element component. - */ -class EditableElement extends React.Component { - - /** - * The component constructor. - * - * @param {Object} props - The properties. - */ - constructor(props) { - super(props); - this.element = props.element; - this.state = { - expanded: this.props.expandAll, - expandAll: this.props.expandAll, - focusKey: false, - focusValue: false - }; - } - - /** - * Subscribe to the events. - */ - componentDidMount() { - this.unsubscribeAdded = this.handleExpand.bind(this); - this.unsubscribeConverted = this.handleExpand.bind(this); - this.unsubscribeEdited = this.handleChange.bind(this); - this.unsubscribeRemoved = this.handleChange.bind(this); - this.unsubscribeReverted = this.handleChange.bind(this); - this.unsubscribeInvalid = this.handleChange.bind(this); - - this.element.on(Element.Events.Added, this.unsubscribeAdded); - this.element.on(Element.Events.Converted, this.unsubscribeConverted); - this.element.on(Element.Events.Edited, this.unsubscribeEdited); - this.element.on(Element.Events.Removed, this.unsubscribeRemoved); - this.element.on(Element.Events.Reverted, this.unsubscribeReverted); - this.element.on(Element.Events.Invalid, this.unsubscribeInvalid); - } - - /** - * Set the state if the expand all prop changes. - * - * @param {Object} nextProps - The next properties. - */ - componentWillReceiveProps(nextProps) { - const state = {}; - if (!nextProps.editing) { - state.focusKey = false; - state.focusValue = false; - } - - if (nextProps.expandAll !== this.state.expandAll) { - state.expanded = nextProps.expandAll; - state.expandAll = nextProps.expandAll; - } - - this.setState(state); - } - - /** - * Unsubscribe from the events. - */ - componentWillUnmount() { - this.element.removeListener(Element.Events.Added, this.unsubscribeAdded); - this.element.removeListener(Element.Events.Converted, this.unsubscribeConverted); - this.element.removeListener(Element.Events.Edited, this.unsubscribeEdited); - this.element.removeListener(Element.Events.Removed, this.unsubscribeRemoved); - this.element.removeListener(Element.Events.Reverted, this.unsubscribeReverted); - this.element.removeListener(Element.Events.Invalid, this.unsubscribeInvalid); - } - - /** - * Expand the element. - */ - handleExpand() { - this.setState({ expanded: true }); - } - - /** - * Here to re-render the component when a change is made. - */ - handleChange() { - this.setState({}); - } - - /** - * Toggles the expandable aspect of the element. - */ - toggleExpandable() { - this.setState({ expanded: !this.state.expanded }); - } - - /** - * Get the inline style for the element. - * - * @returns {Object} The inline style object. - */ - inlineStyle() { - return { paddingLeft: `${this.props.indent}px` }; - } - - /** - * Get the inline style for the toggle. - * - * @returns {Object} The inline style for the toggle. - */ - inlineToggleStyle() { - return { left: `${this.props.indent + 48}px` }; - } - - /** - * Get the style for the element component. - * - * @param {String} base - The base style. - * - * @returns {String} The element style. - */ - style(base = BEM_BASE) { - let style = base; - if (this.props.editing) { - style = style.concat(` ${base}-${EDITING}`); - if (this.element.isAdded()) { - style = style.concat(` ${base}-${ADDED}`); - } else if (this.element.isEdited()) { - style = style.concat(` ${base}-${EDITED}`); - } else if (this.element.isRemoved()) { - style = style.concat(` ${base}-${REMOVED}`); - } - } else if (this.props.rootFieldIndex >= FIELD_LIMIT) { - style = `${style} hidden`; - } - if (this.state.expanded) { - style = style.concat(` ${base}-${EXPANDED}`); - } - return style; - } - - focusEditKey() { - this.props.edit(); - this.setState({focusKey: true}); - } - - focusEditValue() { - this.props.edit(); - this.setState({focusValue: true}); - } - - /** - * Get the components for the elements. - * - * @returns {Array} The components. - */ - renderChildren() { - const components = []; - if (!this.state.expanded && !this.props.expandAll) { - // COMPASS-1312 Lazily render children when user clicks on expand - return components; - } - let index = 0; - for (const element of this.element.elements) { - components.push(( - - )); - index++; - } - return components; - } - - /** - * Render the action column. - * - * @returns {React.Component} The component. - */ - renderAction() { - if (this.props.editing && this.element.isValueEditable()) { - return (); - } - } - - /** - * Render the line number column. - * - * @returns {React.Component} The component. - */ - renderLineNumber() { - if (this.props.editing) { - return (); - } - } - - /** - * Render the separator column. - * - * @returns {React.Component} The component. - */ - renderSeparator() { - return (:); - } - - /** - * Render the types column. - * - * @returns {React.Component} The component. - */ - renderTypes() { - if (this.props.editing) { - return (); - } - } - - /** - * Render the key column. - * - * @returns {React.Component} The component. - */ - renderKey() { - if (this.props.editing && this.element.isKeyEditable()) { - return (); - } - const onDoubleClick = this.element.isKeyEditable() ? null : this.focusEditKey.bind(this); - return ( -
    - {this.element.parent.currentType === 'Array' ? this.props.index : this.element.currentKey} -
    - ); - } - - /** - * Render the toggle column. - * - * @returns {Component} The component. - */ - renderToggle() { - const HEADER_TOGGLE = this.state.expanded ? 'fa fa-angle-down' : 'fa fa-angle-right'; - - return ( -
    -
    - ); - } - - /** - * Render the expandable label column. - * - * @returns {Component} The component. - */ - renderLabel() { - return ( -
    - {this.element.currentType} -
    - ); - } - - /** - * Render the value column. - * - * @todo: Durran: Editing or not? - * - * @returns {Component} The component. - */ - renderValue() { - if (this.props.editing && this.element.isValueEditable()) { - return (); - } - const component = getComponent(this.element.currentType); - const reactComponent = React.createElement( - component, - { type: this.element.currentType, value: this.element.currentValue } - ); - - return {reactComponent}; - } - - /** - * Render a non-expandable element. - * - * @returns {Component} The component. - */ - renderNonExpandable() { - return ( -
  • - {this.renderAction()} - {this.renderLineNumber()} - {this.renderKey()} - {this.renderSeparator()} - {this.renderValue()} - {this.renderTypes()} -
  • - ); - } - - /** - * Render an expandable element. - * - * @returns {React.Component} The component. - */ - renderExpandable() { - return ( -
  • -
    - {this.renderAction()} - {this.renderLineNumber()} - {this.renderToggle()} - {this.renderKey()} - {this.renderSeparator()} - {this.renderLabel()} - {this.renderTypes()} -
    -
      - {this.renderChildren()} -
    -
  • - ); - } - - /** - * Render a single editable element. - * - * @returns {React.Component} The element component. - */ - render() { - return this.element.elements ? this.renderExpandable() : this.renderNonExpandable(); - } -} - -EditableElement.displayName = 'EditableElement'; - -EditableElement.propTypes = { - editing: PropTypes.bool, - edit: PropTypes.func, - element: PropTypes.object.isRequired, - index: PropTypes.number, - indent: PropTypes.number, - expandAll: PropTypes.bool, - rootFieldIndex: PropTypes.number -}; - -module.exports = EditableElement; diff --git a/src/internal-packages/crud/lib/component/editable-key.jsx b/src/internal-packages/crud/lib/component/editable-key.jsx deleted file mode 100644 index fb09f5c3702..00000000000 --- a/src/internal-packages/crud/lib/component/editable-key.jsx +++ /dev/null @@ -1,234 +0,0 @@ -const React = require('react'); -const PropTypes = require('prop-types'); -const chars = require('./utils'); - -/* eslint no-return-assign:0 */ - -/** - * The editing class constant. - */ -const EDITING = 'editable-element-field-is-editing'; - -/** - * The duplicate key value. - */ -const DUPLICATE = 'editable-element-field-is-duplicate'; - -/** - * The document key class. - */ -const KEY_CLASS = 'editable-element-field'; - -/** - * Escape key code. - */ -const ESC = 27; - -/** - * Colon key code. - */ -const COLON = 186; - -/** - * General editable key component. - */ -class EditableKey extends React.Component { - - /** - * The component constructor. - * - * @param {Object} props - The properties. - */ - constructor(props) { - super(props); - this.element = props.element; - this.state = { duplcate: false, editing: false }; - } - - /** - * Focus on this field on mount, so the tab can do it's job and move - * to the value field. - */ - componentDidMount() { - if (this.isAutoFocusable() || this.props.isFocused) { - this._node.focus(); - } - } - - /** - * When hitting a key on the last element some special things may happen. - * - * @param {Event} evt - The event. - */ - handleKeyDown(evt) { - const value = evt.target.value; - if (evt.keyCode === ESC) { - if (value.length === 0) { - this.element.remove(); - } else { - this._node.blur(); - } - } else if (evt.keyCode === 13) { - // Simulate a tab if the user presses enter. - try { - this._node.nextSibling.nextSibling.firstChild.focus(); - } catch (e) { - return; - } - } - } - - /** - * If they key is a colon, tab to the next input. - * - * @param {Object} evt - The event. - */ - handleKeyUp(evt) { - if (evt.keyCode === COLON) { - const value = evt.target.value; - if (value !== ':') { - this.element.rename(value.replace(':', '')); - evt.target.value = ''; - // focus is not always available, this is now guarded - try { - this._node.nextSibling.nextSibling.firstChild.focus(); - } catch (e) { - return; - } - } - } - } - - /** - * Handles changes to the element key. - * - * @param {Event} evt - The event. - */ - handleChange(evt) { - const value = evt.target.value; - this._node.size = chars(value); - if (this.isEditable()) { - if (this.element.isDuplicateKey(value)) { - this.setState({ duplicate: true }); - } else if (this.state.duplicate) { - this.setState({ duplicate: false }); - } - this.element.rename(value); - } - } - - /** - * Handle focus on the key. - */ - handleFocus() { - if (this.isEditable()) { - this.setState({ editing: true }); - } - } - - /** - * Handle blur from the key. - */ - handleBlur() { - if (this.isEditable()) { - this.setState({ editing: false }); - } - } - - /** - * Is this component auto focusable? - * - * This is true if: - * - When a new element has been added and is a normal element. - * - When not being tabbed into. - * - * Is false if: - * - When a new array value has been added. - * - When the key is _id - * - * @returns {Boolean} If the component is editable. - */ - isAutoFocusable() { - return this.element.isAdded() && this.isEditable(); - } - - /** - * Is the key able to be edited? - * - * @returns {Boolean} If the key can be edited. - */ - isEditable() { - return this.element.isKeyEditable() && this.element.parent.currentType !== 'Array'; - } - - /** - * Get the style for the key of the element. - * - * @returns {String} The key style. - */ - style() { - let style = KEY_CLASS; - if (this.state.editing) { - style = style.concat(` ${EDITING}`); - } - if (this.state.duplicate) { - style = style.concat(` ${DUPLICATE}`); - } - return style; - } - - /** - * Render the value of the key. - * - * @returns {String} The value for the key. - */ - renderValue() { - return this.element.parent.currentType === 'Array' ? this.props.index : this.element.currentKey; - } - - /** - * Render the title. - * - * @returns {String} The title. - */ - renderTitle() { - if (this.state.duplicate) { - return `Duplicate key: '${this.element.currentKey}' - this will overwrite previous values.`; - } - return this.element.currentKey; - } - - /** - * Render a single editable key. - * - * @returns {React.Component} The element component. - */ - render() { - const length = (chars(this.renderValue()) * 6.625) + 6.625; - return ( - this._node = c} - type="text" - style={{ width: `${length}px` }} - tabIndex={this.isEditable() ? 0 : -1} - onBlur={this.handleBlur.bind(this)} - onFocus={this.handleFocus.bind(this)} - onChange={this.handleChange.bind(this)} - onKeyDown={this.handleKeyDown.bind(this)} - onKeyUp={this.handleKeyUp.bind(this)} - value={this.renderValue()} - title={this.renderTitle()} /> - ); - } -} - -EditableKey.displayName = 'EditableKey'; - -EditableKey.propTypes = { - element: PropTypes.object.isRequired, - index: PropTypes.number, - isFocused: PropTypes.bool.isRequired -}; - -module.exports = EditableKey; diff --git a/src/internal-packages/crud/lib/component/editable-value.jsx b/src/internal-packages/crud/lib/component/editable-value.jsx deleted file mode 100644 index df4f8631043..00000000000 --- a/src/internal-packages/crud/lib/component/editable-value.jsx +++ /dev/null @@ -1,267 +0,0 @@ -const app = require('hadron-app'); -const React = require('react'); -const PropTypes = require('prop-types'); -const { Tooltip } = require('hadron-react-components'); -const initEditors = require('./editor/'); - -/* eslint no-return-assign:0 */ - -/** - * Escape key code. - */ -const ESC = 27; - -/** - * The editing class constant. - */ -const EDITING = 'is-editing'; - -/** - * The document value class. - */ -const VALUE_CLASS = 'editable-element-value'; - -/** - * The version at which high precision values are available. - */ -const HP_VERSION = '3.4.0'; - -/** - * Invalid type class. - */ -const INVALID = `${VALUE_CLASS}-is-invalid-type`; - -/** - * General editable value component. - */ -class EditableValue extends React.Component { - - /** - * The component constructor. - * - * @param {Object} props - The properties. - */ - constructor(props) { - super(props); - this.element = props.element; - this.state = { editing: false }; - this._pasting = false; - this._version = app.instance.build.version; - this._editors = initEditors(this.element); - } - - /** - * Focus on this field on mount, so the tab can do it's job and move - * to the value field. - */ - componentDidMount() { - if (this.isAutoFocusable() || this.props.isFocused) { - this._node.focus(); - } - } - - /** - * Get the editor for the current type. - * - * @returns {Editor} The editor. - */ - editor() { - return this._editors[this.element.currentType] || this._editors.Standard; - } - - /** - * Is the element auto-focusable? - * - * @returns {Boolean} If the element can be focused automatically. - */ - isAutoFocusable() { - return !this.element.isKeyEditable(); - } - - /** - * Are high precision values available? - * - * @returns {boolean} if high precision values are available. - */ - isHighPrecision() { - return this._version >= HP_VERSION; - } - - /** - * When hitting a key on the last element some special things may happen. - * - * @param {Event} evt - The event. - */ - handleKeyDown(evt) { - if (evt.keyCode === 9 && !evt.shiftKey) { - this.simulateTab(evt); - } else if (evt.keyCode === ESC) { - const value = evt.target.value; - if (value.length === 0 && this.element.currentKey.length === 0) { - this.element.remove(); - } else { - this._node.blur(); - } - } else if (evt.keyCode === 13) { - if (this.element.nextElement) { - // need to force the focus. - this._node.parentNode.parentNode.nextSibling.childNodes[2].focus(); - } - this.simulateTab(evt); - } - } - - /** - * Simulates a tab event. - * - * @param {Event} evt - The event. - */ - simulateTab(evt) { - if (this.isTabable()) { - if (!this.element.nextElement || - this.element.currentValue === '{' || - this.element.currentValue === '[') { - this.element.next(); - evt.preventDefault(); - evt.stopPropagation(); - } - } else { - // We don't want to create another element when the current one is blank. - evt.preventDefault(); - evt.stopPropagation(); - } - } - - /** - * Sets the flag that the user pasted the value. The reason for this is that - * the onpaste event that is passed does not have consistent behaviour with - * respect to event.target.value - it is sometimes undefined. The onchange - * event always gets fired after this to we can set the flag and then check - * for it in that event. - */ - handlePaste() { - this._pasting = true; - } - - /** - * Is the element tabbable. - * - * @returns {Boolean} If the element is tabbable. - */ - isTabable() { - if (this.element.parent.currentType === 'Array') { - return this.element.currentValue !== ''; - } - return this.element.currentKey.length !== 0; - } - - /** - * Handles changes to the element value. - * - * @param {Event} evt - The event. - */ - handleChange(evt) { - const value = evt.target.value; - if (this._pasting) { - this._pasteEdit(value); - } else { - this.editor().edit(value); - } - } - - /** - * Edit the field value when using copy/paste. - * - * @param {String} value - The value to paste in. - */ - _pasteEdit(value) { - try { - this.editor().paste(value); - } catch (e) { - this.editor().edit(value); - } finally { - this._pasting = false; - } - } - - /** - * Handle focus on the value. - */ - handleFocus() { - this.editor().start(); - this.setState({ editing: true }); - } - - /** - * Handle blur from the value. Calls complete on the editor and sets the state. - */ - handleBlur() { - this.editor().complete(); - this.setState({ editing: false }); - } - - /** - * Get the style for the value of the element. - * - * @returns {String} The value style. - */ - style() { - let typeClass = `${VALUE_CLASS}-is-${this.element.currentType.toLowerCase()}`; - if (!this.element.isCurrentTypeValid()) { - typeClass = `${typeClass} ${INVALID}`; - } - if (this.state.editing) { - return `${VALUE_CLASS} ${VALUE_CLASS}-${EDITING} ${typeClass}`; - } - return `${VALUE_CLASS} ${typeClass}`; - } - - /** - * Get the style for the input wrapper. - * - * @returns {String} The class name. - */ - wrapperStyle() { - return `${VALUE_CLASS}-wrapper ${VALUE_CLASS}-wrapper-is-${this.element.currentType.toLowerCase()}`; - } - - /** - * Render a single editable value. - * - * @returns {React.Component} The element component. - */ - render() { - const length = (this.editor().size(this.state.editing) * 6.625) + 6.625; - return ( - - { return this.element.invalidTypeMessage; }}/> - this._node = c} - type="text" - style={{ width: `${length}px` }} - className={this.style()} - onBlur={this.handleBlur.bind(this)} - onFocus={this.handleFocus.bind(this)} - onChange={this.handleChange.bind(this)} - onKeyDown={this.handleKeyDown.bind(this)} - onPaste={this.handlePaste.bind(this)} - value={this.editor().value(this.state.editing)} /> - - ); - } -} - -EditableValue.displayName = 'EditableValue'; - -EditableValue.propTypes = { - element: PropTypes.object.isRequired, - isFocused: PropTypes.bool.isRequired -}; - -module.exports = EditableValue; diff --git a/src/internal-packages/crud/lib/component/editor/date.js b/src/internal-packages/crud/lib/component/editor/date.js deleted file mode 100644 index 5ee09c0d102..00000000000 --- a/src/internal-packages/crud/lib/component/editor/date.js +++ /dev/null @@ -1,102 +0,0 @@ -const moment = require('moment'); -const TypeChecker = require('hadron-type-checker'); -const { Element } = require('hadron-document'); -const chars = require('../utils'); -const StandardEditor = require('./standard'); - -/** - * The date format. - */ -const FORMAT = 'YYYY-MM-DD HH:mm:ss.SSS'; - -/** - * CRUD editor for date values. - */ -class DateEditor extends StandardEditor { - - /** - * Create the editor with the element. - * - * @param {Element} element - The hadron document element. - */ - constructor(element) { - super(element); - } - - /** - * Complete the date edit by converting the valid string to a date - * object or leaving as invalid. - */ - complete() { - if (this.element.isCurrentTypeValid()) { - this.element.edit(TypeChecker.cast(this.element.currentValue, 'Date')); - } - } - - /** - * Edit the element with the provided value. - * - * @param {Object} value - The new value. - */ - edit(value) { - try { - const date = TypeChecker.cast(value, 'Date'); - if (date.toString() === 'Invalid Date') { - this.element.setInvalid(value, 'Date', `${value} is not in a valid date format`); - } else { - this.element.currentValue = value; - this.element.setValid(); - this.element._bubbleUp(Element.Events.Edited); - } - } catch (e) { - this.element.setInvalid(value, this.element.currentType, e.message); - } - } - - /** - * Get the number of characters the value should display. - * - * @param {Boolean} editMode - If the element is being edited. - * - * @returns {Number} The number of characters. - */ - size(editMode) { - const value = this.element.currentValue; - if (editMode) { - return chars(value); - } - return this.element.isCurrentTypeValid() ? chars(this._formattedValue()) : chars(value); - } - - /** - * Start the date edit. - * - * @param {Object} value - The value in the field. - */ - start() { - if (this.element.isCurrentTypeValid()) { - this.edit(this._formattedValue()); - } - } - - /** - * Get the value being edited. - * - * @param {Boolean} editMode - If the UI is in edit mode. - * - * @returns {String} The value. - */ - value(editMode) { - const value = this.element.currentValue; - if (!editMode && this.element.isCurrentTypeValid()) { - return this._formattedValue(); - } - return value; - } - - _formattedValue() { - return moment(this.element.currentValue).format(FORMAT); - } -} - -module.exports = DateEditor; diff --git a/src/internal-packages/crud/lib/component/editor/double.js b/src/internal-packages/crud/lib/component/editor/double.js deleted file mode 100644 index 09eef600434..00000000000 --- a/src/internal-packages/crud/lib/component/editor/double.js +++ /dev/null @@ -1,30 +0,0 @@ -const chars = require('../utils'); -const StandardEditor = require('./standard'); - -/** - * CRUD editor for double values. - */ -class DoubleEditor extends StandardEditor { - - /** - * Create the editor with the element. - * - * @param {Element} element - The hadron document element. - */ - constructor(element) { - super(element); - } - - /** - * Get the number of characters the value should display. - * - * @param {Boolean} editMode - If the element is being edited. - * - * @returns {Number} The number of characters. - */ - size() { - return chars(this.element.currentValue.value); - } -} - -module.exports = DoubleEditor; diff --git a/src/internal-packages/crud/lib/component/editor/index.js b/src/internal-packages/crud/lib/component/editor/index.js deleted file mode 100644 index 2e77a74bd0c..00000000000 --- a/src/internal-packages/crud/lib/component/editor/index.js +++ /dev/null @@ -1,31 +0,0 @@ -const StandardEditor = require('./standard'); -const StringEditor = require('./string'); -const Int32Editor = require('./int32'); -const DoubleEditor = require('./double'); -const DateEditor = require('./date'); -const NullEditor = require('./null'); -const UndefinedEditor = require('./undefined'); -const ObjectIDEditor = require('./objectid'); - -const init = (element) => { - return { - 'Standard': new StandardEditor(element), - 'String': new StringEditor(element), - 'Date': new DateEditor(element), - 'Double': new DoubleEditor(element), - 'Int32': new Int32Editor(element), - 'Null': new NullEditor(element), - 'Undefined': new UndefinedEditor(element), - 'ObjectID': new ObjectIDEditor(element) - }; -}; - -module.exports = init; -module.exports.DateEditor = DateEditor; -module.exports.StandardEditor = StandardEditor; -module.exports.StringEditor = StringEditor; -module.exports.DoubleEditor = DoubleEditor; -module.exports.Int32Editor = Int32Editor; -module.exports.NullEditor = NullEditor; -module.exports.UndefinedEditor = UndefinedEditor; -module.exports.ObjectIDEditor = ObjectIDEditor; diff --git a/src/internal-packages/crud/lib/component/editor/int32.js b/src/internal-packages/crud/lib/component/editor/int32.js deleted file mode 100644 index e764b5be147..00000000000 --- a/src/internal-packages/crud/lib/component/editor/int32.js +++ /dev/null @@ -1,30 +0,0 @@ -const chars = require('../utils'); -const StandardEditor = require('./standard'); - -/** - * CRUD editor for int32 values. - */ -class Int32Editor extends StandardEditor { - - /** - * Create the editor with the element. - * - * @param {Element} element - The hadron document element. - */ - constructor(element) { - super(element); - } - - /** - * Get the number of characters the value should display. - * - * @param {Boolean} editMode - If the element is being edited. - * - * @returns {Number} The number of characters. - */ - size() { - return chars(this.element.currentValue.valueOf()); - } -} - -module.exports = Int32Editor; diff --git a/src/internal-packages/crud/lib/component/editor/null.js b/src/internal-packages/crud/lib/component/editor/null.js deleted file mode 100644 index f58c7c23385..00000000000 --- a/src/internal-packages/crud/lib/component/editor/null.js +++ /dev/null @@ -1,43 +0,0 @@ -const StandardEditor = require('./standard'); - -/** - * Null is always 'null' - */ -const VALUE = 'null'; - -/** - * CRUD editor for null values. - */ -class NullEditor extends StandardEditor { - - /** - * Create the editor with the element. - * - * @param {Element} element - The hadron document element. - */ - constructor(element) { - super(element); - } - - /** - * Get the number of characters the value should display. - * - * @param {Boolean} editMode - If the element is being edited. - * - * @returns {Number} The number of characters. - */ - size() { - return 4; - } - - /** - * Get the value being edited. - * - * @returns {Object} The value. - */ - value() { - return VALUE; - } -} - -module.exports = NullEditor; diff --git a/src/internal-packages/crud/lib/component/editor/objectid.js b/src/internal-packages/crud/lib/component/editor/objectid.js deleted file mode 100644 index 0e55a0ac23e..00000000000 --- a/src/internal-packages/crud/lib/component/editor/objectid.js +++ /dev/null @@ -1,73 +0,0 @@ -const TypeChecker = require('hadron-type-checker'); -const { Element } = require('hadron-document'); -const chars = require('../utils'); - -/** - * CRUD editor for object id values. - */ -class ObjectIDEditor { - - /** - * Create the editor with the element. - * - * @param {Element} element - The hadron document element. - */ - constructor(element) { - this.element = element; - } - - /** - * Complete the object id edit by converting the valid string to an object id - * object or leaving as invalid. - */ - complete() { - if (this.element.isCurrentTypeValid()) { - this.element.edit(TypeChecker.cast(this.element.currentValue, 'ObjectID')); - } - } - - /** - * Edit the element with the provided value. - * - * @param {Object} value - The new value. - */ - edit(value) { - try { - TypeChecker.cast(value, 'ObjectID'); - this.element.currentValue = value; - this.element.setValid(); - this.element._bubbleUp(Element.Events.Edited); - } catch (e) { - this.element.setInvalid(value, this.element.currentType, e.message); - } - } - - /** - * Get the number of characters the value should display. - * - * @returns {Number} The number of characters. - */ - size() { - return chars(this.element.currentValue); - } - - /** - * Start the object id edit. - */ - start() { - if (this.element.isCurrentTypeValid()) { - this.edit(String(this.element.currentValue)); - } - } - - /** - * Get the value being edited. - * - * @returns {String} The value. - */ - value() { - return this.element.currentValue; - } -} - -module.exports = ObjectIDEditor; diff --git a/src/internal-packages/crud/lib/component/editor/standard.js b/src/internal-packages/crud/lib/component/editor/standard.js deleted file mode 100644 index 6a67aa6141a..00000000000 --- a/src/internal-packages/crud/lib/component/editor/standard.js +++ /dev/null @@ -1,79 +0,0 @@ -const TypeChecker = require('hadron-type-checker'); -const { Element } = require('hadron-document'); -const chars = require('../utils'); - -/** - * Regex to match an array or object string. - */ -const ARRAY_OR_OBJECT = /^(\[|\{)(.+)(\]|\})$/; - -/** - * CRUD editor for standard values. - */ -class StandardEditor { - - /** - * Create the editor with the element. - * - * @param {Element} element - The hadron document element. - */ - constructor(element) { - this.element = element; - } - - /** - * Edit the element with the provided value. - * - * @param {Object} value - The new value. - */ - edit(value) { - const currentType = this.element.currentType; - try { - const newValue = TypeChecker.cast(value, currentType); - this.element.edit(newValue); - } catch (e) { - this.element.setInvalid(value, currentType, e.message); - } - } - - /** - * Edit the element via a paste. - * - * @param {String} value - The balue. - */ - paste(value) { - if (value.match(ARRAY_OR_OBJECT)) { - this.edit(JSON.parse(value)); - this.element._bubbleUp(Element.Events.Converted); - } else { - this.edit(value); - } - } - - - /** - * Get the number of characters the value should display. - * - * @param {Boolean} editMode - If the element is being edited. - * - * @returns {Number} The number of characters. - */ - size() { - return chars(this.element.currentValue); - } - - /** - * Get the value being edited. - * - * @returns {Object} The value. - */ - value() { - return this.element.currentValue; - } - - // Standard editing requires no special start/complete behaviour. - start() {} - complete() {} -} - -module.exports = StandardEditor; diff --git a/src/internal-packages/crud/lib/component/editor/string.js b/src/internal-packages/crud/lib/component/editor/string.js deleted file mode 100644 index 7f96ab50ff4..00000000000 --- a/src/internal-packages/crud/lib/component/editor/string.js +++ /dev/null @@ -1,30 +0,0 @@ -const chars = require('../utils'); -const StandardEditor = require('./standard'); - -/** - * CRUD editor for string values. - */ -class StringEditor extends StandardEditor { - - /** - * Create the editor with the element. - * - * @param {Element} element - The hadron document element. - */ - constructor(element) { - super(element); - } - - /** - * Get the number of characters the value should display. - * - * @param {Boolean} editMode - If the element is being edited. - * - * @returns {Number} The number of characters. - */ - size() { - return chars(this.element.currentValue); - } -} - -module.exports = StringEditor; diff --git a/src/internal-packages/crud/lib/component/editor/undefined.js b/src/internal-packages/crud/lib/component/editor/undefined.js deleted file mode 100644 index cfc6a11cd7a..00000000000 --- a/src/internal-packages/crud/lib/component/editor/undefined.js +++ /dev/null @@ -1,43 +0,0 @@ -const StandardEditor = require('./standard'); - -/** - * Undefined is always 'undefined' - */ -const VALUE = 'undefined'; - -/** - * CRUD editor for undefined values. - */ -class UndefinedEditor extends StandardEditor { - - /** - * Create the editor with the element. - * - * @param {Element} element - The hadron document element. - */ - constructor(element) { - super(element); - } - - /** - * Get the number of characters the value should display. - * - * @param {Boolean} editMode - If the element is being edited. - * - * @returns {Number} The number of characters. - */ - size() { - return 9; - } - - /** - * Get the value being edited. - * - * @returns {Object} The value. - */ - value() { - return VALUE; - } -} - -module.exports = UndefinedEditor; diff --git a/src/internal-packages/crud/lib/component/element-action.jsx b/src/internal-packages/crud/lib/component/element-action.jsx deleted file mode 100644 index 2e49af8a948..00000000000 --- a/src/internal-packages/crud/lib/component/element-action.jsx +++ /dev/null @@ -1,33 +0,0 @@ -const React = require('react'); -const PropTypes = require('prop-types'); -const RevertAction = require('./revert-action'); -const RemoveAction = require('./remove-action'); -const NoAction = require('./no-action'); - -/** - * Component to render the available action for an element. - */ -class ElementAction extends React.Component { - - /** - * Render the action available for the element. - * - * @returns {React.Component} The component. - */ - render() { - if (this.props.element.isRevertable()) { - return (); - } else if (this.props.element.isNotActionable()) { - return (); - } - return (); - } -} - -ElementAction.displayName = 'ElementAction'; - -ElementAction.propTypes = { - element: PropTypes.object.isRequired -}; - -module.exports = ElementAction; diff --git a/src/internal-packages/crud/lib/component/element.jsx b/src/internal-packages/crud/lib/component/element.jsx deleted file mode 100644 index c18430dc98f..00000000000 --- a/src/internal-packages/crud/lib/component/element.jsx +++ /dev/null @@ -1,201 +0,0 @@ -const React = require('react'); -const PropTypes = require('prop-types'); -const getComponent = require('hadron-react-bson'); - -/** - * The base class. - */ -const CLASS = 'element'; - -/** - * The field class. - */ -const FIELD = `${CLASS}-field`; - -/** - * The field limit. - */ -const FIELD_LIMIT = 30; - -/** - * The separator class. - */ -const SEPARATOR = `${CLASS}-separator`; - -/** - * The expandable element class. - */ -const EXP_CLASS = 'expandable-element'; - -/** - * The expandable header class. - */ -const EXP_HEADER = `${EXP_CLASS}-header`; - -/** - * The carat toggle class. - */ -const EXP_TOGGLE = `${EXP_HEADER}-toggle`; - -/** - * The expandable field class. - */ -const EXP_FIELD = `${EXP_HEADER}-field`; - -/** - * The expandable label class. - */ -const EXP_LABEL = `${EXP_HEADER}-label`; - -/** - * The expandable children class. - */ -const EXP_CHILDREN = `${EXP_CLASS}-children`; - -/** - * The expandable element separator class. - */ -const EXP_SEPARATOR = `${EXP_HEADER}-separator`; - -/** - * General element component. - */ -class Element extends React.Component { - - /** - * Instantiate the element. - * - * @param {Object} props - The properties. - */ - constructor(props) { - super(props); - this.state = { expanded: props.expandAll }; - } - - /** - * Get the full class name for the base style. - * - * @param {String} base - The base class. - * - * @returns {String} The full class name. - */ - getClassName(base) { - if (this.state.expanded) { - return `${base} ${base}-is-expanded`; - } - return base; - } - - /** - * Toggles the expandable aspect of the element. - */ - toggleExpandable() { - this.setState({ expanded: !this.state.expanded }); - } - - /** - * Render the children. - * - * @returns {Array} The children. - */ - renderChildren() { - const components = []; - for (const element of this.props.element.elements) { - components.push( - () - ); - } - return components; - } - - /** - * Render a single element. - * - * @returns {React.Component} The single element. - */ - renderElement() { - return ( -
  • -
    - {this.props.element.currentKey} -
    - : - {this.renderValue()} -
  • - ); - } - - /** - * Render a single expandable element. - * - * @returns {React.Component} The expandable element. - */ - renderExpandableElement() { - return ( -
  • -
    -
    -
    {this.props.element.currentKey}
    - : -
    - {this.props.element.currentType} -
    -
    -
      - {this.renderChildren()} -
    -
  • - ); - } - - /** - * Render the style with the provided base style. - * - * @param {String} base - The base style. - * - * @returns {String} The style. - */ - renderStyle(base) { - let style = base; - if (this.props.rootFieldIndex >= FIELD_LIMIT) { - style = `${style} hidden`; - } - return style; - } - - /** - * Render the value of the element. - * - * @returns {React.Component} The value component. - */ - renderValue() { - const component = getComponent(this.props.element.currentType); - return React.createElement( - component, - { type: this.props.element.currentType, value: this.props.element.currentValue } - ); - } - - /** - * Render a single element in a document. - * - * @returns {React.Component} The element component. - */ - render() { - return this.props.element.elements ? this.renderExpandableElement() : this.renderElement(); - } -} - -Element.displayName = 'Element'; - -Element.propTypes = { - element: PropTypes.any.isRequired, - expandAll: PropTypes.bool, - rootFieldIndex: PropTypes.number -}; - -module.exports = Element; diff --git a/src/internal-packages/crud/lib/component/insert-document-dialog.jsx b/src/internal-packages/crud/lib/component/insert-document-dialog.jsx deleted file mode 100644 index a7343995451..00000000000 --- a/src/internal-packages/crud/lib/component/insert-document-dialog.jsx +++ /dev/null @@ -1,167 +0,0 @@ -const _ = require('lodash'); -const React = require('react'); -const Modal = require('react-bootstrap').Modal; -const OpenInsertDocumentDialogStore = require('../store/open-insert-document-dialog-store'); -const InsertDocumentStore = require('../store/insert-document-store'); -const InsertDocument = require('./insert-document'); -const InsertDocumentFooter = require('./insert-document-footer'); -const { TextButton } = require('hadron-react-buttons'); -const { Element } = require('hadron-document'); -const Actions = require('../actions'); - -/** - * Component for the insert document dialog. - */ -class InsertDocumentDialog extends React.Component { - - /** - * The component constructor. - * - * @param {Object} props - The properties. - */ - constructor(props) { - super(props); - this.state = { open: false, canHide: false }; - } - - /** - * Subscribe to the open dialog store. - */ - componentWillMount() { - this.invalidElements = []; - this.unsubscribeOpen = OpenInsertDocumentDialogStore.listen(this.handleStoreOpen.bind(this)); - this.unsubscribeInsert = InsertDocumentStore.listen(this.handleDocumentInsert.bind(this)); - this.unsubscribeClose = Actions.closeInsertDocumentDialog.listen(this.closeDialog.bind(this)); - } - - /** - * Unsubscribe from the store. - */ - componentWillUnmount() { - this.unsubscribeOpen(); - this.unsubscribeInsert(); - this.unsubscribeClose(); - } - - /** - * Close the dialog. - */ - closeDialog() { - this.invalidElements = []; - this.state.doc.removeListener(Element.Events.Invalid, this.unsubscribeInvalid); - this.state.doc.removeListener(Element.Events.Valid, this.unsubscribeValid); - this.setState({ open: false }); - } - - /** - * Handle opening the dialog with the new document. - * - * @param {Object} doc - The document. - */ - handleStoreOpen(doc) { - this.setState({ doc: doc, open: true }); - this.unsubscribeInvalid = this.handleInvalid.bind(this); - this.unsubscribeValid = this.handleValid.bind(this); - this.state.doc.on(Element.Events.Invalid, this.unsubscribeInvalid); - this.state.doc.on(Element.Events.Valid, this.unsubscribeValid); - } - - /** - * Handle canceling the insert. - */ - handleCancel() { - this.closeDialog(); - } - - /** - * handle losing focus from element - */ - handleBlur() { - this.setState({canHide: false}); - } - - /** - * handle hide event rather than cancel - */ - handleHide() { - if (this.state.canHide) { - this.closeDialog(); - } else { - this.setState({ canHide: true }); - } - } - - /** - * Handles completion of the document insert. - * - * @param {Boolean} success - If the operation succeeded. - */ - handleDocumentInsert(success) { - if (success) { - this.closeDialog(); - } - } - - handleValid(uuid) { - _.pull(this.invalidElements, uuid); - this.setState({}); - Actions.elementValid(uuid); - } - - handleInvalid(uuid) { - if (!_.includes(this.invalidElements, uuid)) { - this.invalidElements.push(uuid); - this.setState({}); - Actions.elementInvalid(uuid); - } - } - - /** - * Handle the insert. - */ - handleInsert() { - Actions.insertDocument(this.state.doc.generateObject()); - } - - hasErrors() { - return this.invalidElements.length > 0; - } - - /** - * Render the modal dialog. - * - * @returns {React.Component} The react component. - */ - render() { - return ( - - - Insert Document - - - - - - - - - - - - - ); - } -} - -InsertDocumentDialog.displayName = 'InsertDocumentDialog'; - -module.exports = InsertDocumentDialog; diff --git a/src/internal-packages/crud/lib/component/insert-document-footer.jsx b/src/internal-packages/crud/lib/component/insert-document-footer.jsx deleted file mode 100644 index b2792cbe3d9..00000000000 --- a/src/internal-packages/crud/lib/component/insert-document-footer.jsx +++ /dev/null @@ -1,124 +0,0 @@ -const _ = require('lodash'); -const React = require('react'); -const InsertDocumentStore = require('../store/insert-document-store'); -const Actions = require('../actions'); - -const INSERTING = 'Inserting Document'; - -/** - * The invalid message. - */ -const INVALID_MESSAGE = 'Insert not permitted while document contains errors.'; - -/** - * Map of modes to styles. - */ -const MODES = { - 'Progress': 'is-in-progress', - 'Error': 'is-error', - 'Viewing': 'is-viewing', - 'Modifying': 'is-modifying' -}; - -/** - * Component for the insert document footer. - */ -class InsertDocumentFooter extends React.Component { - - /** - * The component constructor. - * - * @param {Object} props - The properties. - */ - constructor(props) { - super(props); - this.state = { message: '', mode: 'Modifying' }; - } - - /** - * Subscribe to the insert document store. - */ - componentWillMount() { - this.invalidElements = []; - this.unsubscribeInsert = InsertDocumentStore.listen(this.handleDocumentInsert.bind(this)); - this.unsubscribeStart = Actions.insertDocument.listen(this.handleInsertStart.bind(this)); - this.unsubscribeInvalid = Actions.elementInvalid.listen(this.handleInvalid.bind(this)); - this.unsubscribeValid = Actions.elementValid.listen(this.handleValid.bind(this)); - } - - /** - * Unsubscribe from the store. - */ - componentWillUnmount() { - this.invalidElements = []; - this.unsubscribeInsert(); - this.unsubscribeStart(); - this.unsubscribeInvalid(); - this.unsubscribeValid(); - } - - /** - * Handles completion of document insert. - * - * @param {Boolean} success - If the operation succeeded. - * @param {Object} doc - The document or error. - */ - handleDocumentInsert(success, doc) { - if (!success) { - this.setState({ message: doc.message, mode: 'Error' }); - } - } - - /** - * Handles the start of a document insert. - */ - handleInsertStart() { - this.setState({ message: INSERTING, mode: 'Progess' }); - } - - handleValid(uuid) { - _.pull(this.invalidElements, uuid); - if (!this.hasErrors()) { - this.handleInsertStart(); - } - } - - handleInvalid(uuid) { - if (!_.includes(this.invalidElements, uuid)) { - this.invalidElements.push(uuid); - this.setState({ message: INVALID_MESSAGE, mode: 'Error' }); - } - } - - hasErrors() { - return this.invalidElements.length > 0; - } - - /** - * Get the style of the footer based on the current mode. - * - * @returns {String} The style. - */ - style() { - return `document-footer document-footer-${MODES[this.state.mode]}`; - } - - /** - * Render the footer. - * - * @returns {Component} The footer component. - */ - render() { - return ( -
    -
    - {this.state.message} -
    -
    - ); - } -} - -InsertDocumentFooter.displayName = 'InsertDocumentFooter'; - -module.exports = InsertDocumentFooter; diff --git a/src/internal-packages/crud/lib/component/insert-document.jsx b/src/internal-packages/crud/lib/component/insert-document.jsx deleted file mode 100644 index 4259d3b54b0..00000000000 --- a/src/internal-packages/crud/lib/component/insert-document.jsx +++ /dev/null @@ -1,94 +0,0 @@ -const React = require('react'); -const PropTypes = require('prop-types'); -const Element = require('hadron-document').Element; -const EditableElement = require('./editable-element'); - -/** - * The class for the document itself. - */ -const DOCUMENT = 'document'; - -/** - * The elements wrapper class. - */ -const DOCUMENT_ELEMENTS = 'document-elements'; - -/** - * Component for a single document in a list of documents. - */ -class InsertDocument extends React.Component { - - /** - * The component constructor. - * - * @param {Object} props - The properties. - */ - constructor(props) { - super(props); - this.doc = props.doc; - this.doc.on(Element.Events.Added, this.handleModify.bind(this)); - this.doc.on(Element.Events.Removed, this.handleModify.bind(this)); - } - - /** - * Subscribe to the events. - */ - componentDidMount() { - this.unsubscribeAdded = this.handleModify.bind(this); - this.unsubscribeRemoved = this.handleModify.bind(this); - - this.doc.on(Element.Events.Added, this.unsubscribeAdded); - this.doc.on(Element.Events.Removed, this.unsubscribeRemoved); - } - - /** - * Unsubscribe from the events. - */ - componentWillUnmount() { - this.doc.removeListener(Element.Events.Added, this.unsubscribeAdded); - this.doc.removeListener(Element.Events.Removed, this.unsubscribeRemoved); - } - - /** - * Handle modifications to the document. - */ - handleModify() { - this.setState({}); - } - - /** - * Get the editable elements. - * - * @returns {Array} The editable elements. - */ - renderElements() { - const components = []; - for (const element of this.doc.elements) { - components.push(); - } - return components; - } - - /** - * Render a single document list item. - * - * @returns {React.Component} The component. - */ - render() { - return ( -
    -
      - {this.renderElements(this.doc)} -
    -
    - ); - } -} - -InsertDocument.displayName = 'InsertDocument'; - -InsertDocument.propTypes = { - doc: PropTypes.object -}; - -module.exports = InsertDocument; diff --git a/src/internal-packages/crud/lib/component/line-number.jsx b/src/internal-packages/crud/lib/component/line-number.jsx deleted file mode 100644 index 3e742602827..00000000000 --- a/src/internal-packages/crud/lib/component/line-number.jsx +++ /dev/null @@ -1,323 +0,0 @@ -const React = require('react'); -const PropTypes = require('prop-types'); -const outsideClickable = require('react-click-outside'); -const getComponent = require('hadron-react-bson'); -const Actions = require('../actions'); -// const debug = require('debug')('mongodb-compass:crud:line-number'); - -/** - * The BEM base style name for the element. - */ -const BEM_BASE = 'line-number'; - -/** - * The menu class. - */ -const MENU_CLASS = `${BEM_BASE}-menu`; - -/** - * The field name class. - */ -const FIELD_NAME_CLASS = `${MENU_CLASS}-field`; - -/** - * The default text. - */ -const DEFAULT_TEXT = 'Add Field After '; - -/** - * Object text. - */ -const OBJECT_TEXT = 'Add Field To '; - -/** - * Array text. - */ -const ARRAY_TEXT = 'Add Array Element To '; - -/** - * Array element text. - */ -const ARRAY_ELEMENT_TEXT = 'Add Array Element After '; - -/** - * Add child icon. - */ -const ADD_CHILD_ICON = 'fa fa-level-down fa-rotate-90'; - -/** - * Add field icon. - */ -const ADD_FIELD_ICON = 'fa fa-plus-square-o'; - -/** - * Line number component. - */ -class LineNumber extends React.Component { - - /** - * Instantiate the line number component. - * - * @param {Object} props - The props. - */ - constructor(props) { - super(props); - this.state = { menu: false }; - } - - /** - * Subscribe to the close menu action. - */ - componentDidMount() { - this.unsubscribeClose = Actions.closeAllMenus.listen(this.handleCloseAllMenus.bind(this)); - } - - /** - * Unsubscribe from the close menu action. - */ - componentWillUnmount() { - this.unsubscribeClose(); - } - - /** - * Class name for line number div. - * - * @returns {String} The class name. - */ - divClassName() { - return this.state.menu ? `${BEM_BASE} ${BEM_BASE}-is-selected` : BEM_BASE; - } - - /** - * Handle click on the line number. - */ - handleClick() { - // Provide menu for _id because it's top-level, but not for any potential children. - if (this.props.element.isParentEditable()) { - Actions.closeAllMenus(this); - this.setState({menu: !this.state.menu}); - } - } - - /** - * Handle key press for enter on the line number. - * - * @param {Object} event The DOM event - */ - handleKeyPress(event) { - if (event.key === 'Enter' && this.props.element.isParentEditable()) { - Actions.closeAllMenus(this); - this.setState({menu: !this.state.menu}); - } - } - - /** - * Handle clicking outside the element. - */ - handleClickOutside() { - this.setState({ menu: false }); - } - - /** - * When clicking on a hotspot we append or remove on the parent. - */ - handleAddFieldClick() { - this.props.element.next(); - this.setState({ menu: false }); - } - - /** - * When clicking on an expandable element to append a child. - */ - handleAddChildClick() { - this.props.element.insertPlaceholder(); - this.setState({ menu: false }); - } - - /** - * Handle the close all menus action. - * - * @param {React.Component} component - The component that called the action. - */ - handleCloseAllMenus(component) { - if (component !== this) { - this.setState({ menu: false }); - } - } - - - /** - * Is the current element an object? - * - * @returns {Boolean} If the element is an object. - */ - isElementObject() { - return this.props.element.currentType === 'Object'; - } - - /** - * Is the current element an array? - * - * @returns {Boolean} If the element is an array. - */ - isElementArray() { - return this.props.element.currentType === 'Array'; - } - - /** - * Is the parent of this element an array? - * - * @returns {Boolean} If the parent element is an array. - */ - isParentArray() { - return !this.props.element.parent.isRoot() && - this.props.element.parent.currentType === 'Array'; - } - - /** - * Class name for the menu. - * - * @returns {String} The class name. - */ - menuClassName() { - return this.state.menu ? - `${MENU_CLASS} ${MENU_CLASS}-is-visible dropdown-menu` : `${MENU_CLASS} dropdown-menu`; - } - - /** - * Render an array menu item. - * - * @returns {React.Component} The component. - */ - renderArrayItem() { - if (this.isElementArray() && this.props.element.isValueEditable()) { - return this.renderMenuItem( - ADD_CHILD_ICON, - ARRAY_TEXT, - this.handleAddChildClick.bind(this), - 'add-element-to-array' - ); - } - } - - /** - * Render the default menu item. - * - * @returns {React.Component} The component. - */ - renderDefaultItem() { - const text = this.isParentArray() ? ARRAY_ELEMENT_TEXT : DEFAULT_TEXT; - return this.renderMenuItem( - ADD_FIELD_ICON, - text, - this.handleAddFieldClick.bind(this), - 'add-field-after' - ); - } - - /** - * Render the value of the element. - * - * @returns {React.Component} The value component. - */ - renderValue() { - const component = getComponent(this.props.element.currentType); - return React.createElement( - component, - { type: this.props.element.currentType, value: this.props.element.currentValue } - ); - } - - /** - * Render the identifier in the menu. For objects and arrays in an array, - * this is the type, because the type is already part of the message. For other - * values inside arrays, it's the actual value. Otherwise it's the key. - * - * @returns {String} The field name or value if an array element. - */ - renderIdentifier() { - // this case is already handled in renderDefaultItem() - if (this.isParentArray() && (this.isElementObject() || this.isElementArray())) { - return this.props.element.currentType; - } - return this.props.element.currentKey || this.renderValue(); - } - - /** - * Render a single menu item. - * - * @param {String} iconClassName - The icon class name. - * @param {String} text - The text. - * @param {Function} handler - The click handler. - * @param {String} testId - The test id. - * - * @returns {Component} the menu item component - */ - renderMenuItem(iconClassName, text, handler, testId) { - return ( -
  • - - - {text} - {this.renderIdentifier()} - -
  • - ); - } - - /** - * Render an object menu item. - * - * @returns {React.Component} The component. - */ - renderObjectItem() { - if (this.isElementObject() && this.props.element.isValueEditable()) { - return this.renderMenuItem( - ADD_CHILD_ICON, - OBJECT_TEXT, - this.handleAddChildClick.bind(this), - 'add-child-to-object' - ); - } - } - - /** - * Render the menu. - * - * @returns {React.Component} The menu. - */ - renderMenu() { - return ( -
      - {this.renderObjectItem()} - {this.renderArrayItem()} - {this.renderDefaultItem()} -
    - ); - } - - /** - * Render the line number. - * - * @returns {React.Component} The component. - */ - render() { - return ( -
    - {this.renderMenu()} -
    - ); - } -} - -LineNumber.displayName = 'LineNumber'; - -LineNumber.propTypes = { - element: PropTypes.object.isRequired -}; - -module.exports = outsideClickable(LineNumber); diff --git a/src/internal-packages/crud/lib/component/no-action.jsx b/src/internal-packages/crud/lib/component/no-action.jsx deleted file mode 100644 index 377a31a7207..00000000000 --- a/src/internal-packages/crud/lib/component/no-action.jsx +++ /dev/null @@ -1,27 +0,0 @@ -const React = require('react'); - -/** - * The actions class. - */ -const ACTIONS = 'editable-element-actions'; - -/** - * General element action component. - */ -class NoAction extends React.Component { - - /** - * Render a single editable key. - * - * @returns {React.Component} The element component. - */ - render() { - return ( -
    - ); - } -} - -NoAction.displayName = 'NoAction'; - -module.exports = NoAction; diff --git a/src/internal-packages/crud/lib/component/readonly-document.jsx b/src/internal-packages/crud/lib/component/readonly-document.jsx deleted file mode 100644 index dc0e57620c6..00000000000 --- a/src/internal-packages/crud/lib/component/readonly-document.jsx +++ /dev/null @@ -1,147 +0,0 @@ -const React = require('react'); -const PropTypes = require('prop-types'); -const HadronDocument = require('hadron-document'); -const Element = require('./element'); - -/** - * The arrow up class. - */ -const ARROW_UP = 'fa fa-arrow-up'; - -/** - * The arrow down class. - */ -const ARROW_DOWN = 'fa fa-arrow-down'; - -/** - * The base class. - */ -const BASE = 'document'; - -/** - * The elements class. - */ -const ELEMENTS = `${BASE}-elements`; - -/** - * The field limit. - */ -const FIELD_LIMIT = 30; - -/** - * The expander class. - */ -const EXPANDER = 'btn btn-default btn-xs'; - -/** - * The test id. - */ -const TEST_ID = 'readonly-document'; - -/** - * Component for a single readonly document in a list of documents. - */ -class ReadonlyDocument extends React.Component { - - /** - * Initialize the readonly document. - * - * @param {Object} props - The properties. - */ - constructor(props) { - super(props); - this.doc = new HadronDocument(props.doc); - this.state = { expanded: false }; - } - - /** - * Handle clicking the expand button. - */ - handleExpandClick() { - this.setState({ expanded: !this.state.expanded }); - } - - /** - * Get the elements for the document. - * - * @returns {Array} The elements. - */ - renderElements() { - const components = []; - let index = 0; - for (const element of this.doc.elements) { - components.push(( - - )); - index++; - } - return components; - } - - /** - * Render the expander bar. - * - * @returns {React.Component} The expander bar. - */ - renderExpansion() { - if (this.doc.elements.size >= FIELD_LIMIT) { - return ( - - ); - } - } - - /** - * Render the expansion text. - * - * @returns {String} The text. - */ - renderExpansionText() { - const extraFields = this.doc.elements.size - FIELD_LIMIT; - if (this.state.expanded) { - return `Hide ${extraFields} fields`; - } - return `Show ${extraFields} more fields`; - } - - /** - * Render the style for the expansion icon. - * - * @returns {String} The style. - */ - renderIconStyle() { - return this.state.expanded ? ARROW_UP : ARROW_DOWN; - } - - /** - * Render a single document list item. - * - * @returns {React.Component} The component. - */ - render() { - return ( -
    -
      - {this.renderElements()} - {this.renderExpansion()} -
    -
    - ); - } -} - -ReadonlyDocument.displayName = 'ReadonlyDocument'; - -ReadonlyDocument.propTypes = { - doc: PropTypes.object.isRequired, - expandAll: PropTypes.bool -}; - -module.exports = ReadonlyDocument; diff --git a/src/internal-packages/crud/lib/component/remove-action.jsx b/src/internal-packages/crud/lib/component/remove-action.jsx deleted file mode 100644 index 3798eb0d9d4..00000000000 --- a/src/internal-packages/crud/lib/component/remove-action.jsx +++ /dev/null @@ -1,51 +0,0 @@ -const React = require('react'); -const PropTypes = require('prop-types'); - -/** - * The actions class. - */ -const ACTIONS = 'editable-element-actions'; - -/** - * General element action component. - */ -class RemoveAction extends React.Component { - - /** - * The component constructor. - * - * @param {Object} props - The properties. - */ - constructor(props) { - super(props); - this.element = props.element; - } - - /** - * Remove the change. - */ - handleClick() { - this.element.remove(); - } - - /** - * Render a single editable key. - * - * @returns {React.Component} The element component. - */ - render() { - return ( -
    - -
    - ); - } -} - -RemoveAction.displayName = 'RemoveAction'; - -RemoveAction.propTypes = { - element: PropTypes.object.isRequired -}; - -module.exports = RemoveAction; diff --git a/src/internal-packages/crud/lib/component/remove-document-footer.jsx b/src/internal-packages/crud/lib/component/remove-document-footer.jsx deleted file mode 100644 index 5e195f7ecb1..00000000000 --- a/src/internal-packages/crud/lib/component/remove-document-footer.jsx +++ /dev/null @@ -1,163 +0,0 @@ -const React = require('react'); -const PropTypes = require('prop-types'); -const { TextButton } = require('hadron-react-buttons'); - -/** - * The progress mode. - */ -const PROGRESS = 'Progress'; - -/** - * The error mode. - */ -const ERROR = 'Error'; - -/** - * The editing mode. - */ -const DELETING = 'Deleting'; - -/** - * Map of modes to styles. - */ -const MODES = { - 'Progress': 'is-in-progress', - 'Success': 'is-success', - 'Error': 'is-error', - 'Deleting': 'is-error' -}; - -/** - * The modified message. - */ -const MODIFIED = 'Document Flagged For Deletion.'; - -/** - * The updating message. - */ -const UPDATING = 'Deleting Document.'; - -/** - * The updated message. - */ -const UPDATED = 'Document Deleted.'; - -/** - * Component for a the remove document footer. - */ -class RemoveDocumentFooter extends React.Component { - - /** - * The component constructor. - * - * @param {Object} props - The properties. - */ - constructor(props) { - super(props); - this.doc = props.doc; - this.actions = props.actions; - this.removeStore = props.removeStore; - this.state = { mode: DELETING, message: MODIFIED }; - } - - /** - * Subscribe to the remove store on mount. - */ - componentDidMount() { - this.unsubscribeRemove = this.removeStore.listen(this.handleStoreRemove.bind(this)); - } - - /** - * Unsubscribe from the remove store on unmount. - */ - componentWillUnmount() { - this.unsubscribeRemove(); - } - - /** - * Handle an error with the document update. - * - * @param {Error} error - The error. - */ - handleError(error) { - this.setState({ mode: ERROR, message: error.message }); - } - - /** - * Handle the user clicking the update button. - */ - handleRemove() { - this.setState({ mode: PROGRESS, message: UPDATING }); - this.actions.remove(this.doc); - } - - /** - * Handle a successful document update. - */ - handleSuccess() { - this.setState({ mode: DELETING, message: UPDATED }); - } - - /** - * Handles a trigger from the store. - * - * @param {Boolean} success - If the delete succeeded. - * @param {Object} object - The error or document. - */ - handleStoreRemove(success, object) { - if (success) { - this.handleSuccess(); - } else { - this.handleError(object); - } - } - - /** - * Get the style of the footer based on the current mode. - * - * @returns {String} The style. - */ - style() { - return `document-footer document-footer-${MODES[this.state.mode]}`; - } - - /** - * Render the footer. - * - * @returns {Component} The footer component. - */ - render() { - return ( -
    -
    - {this.state.message} -
    -
    - - -
    -
    - ); - } -} - -RemoveDocumentFooter.displayName = 'RemoveDocumentFooter'; - -RemoveDocumentFooter.propTypes = { - doc: PropTypes.object.isRequired, - actions: PropTypes.object.isRequired, - removeStore: PropTypes.object.isRequired, - cancelHandler: PropTypes.func.isRequired -}; - -module.exports = RemoveDocumentFooter; diff --git a/src/internal-packages/crud/lib/component/revert-action.jsx b/src/internal-packages/crud/lib/component/revert-action.jsx deleted file mode 100644 index 83300d9bf84..00000000000 --- a/src/internal-packages/crud/lib/component/revert-action.jsx +++ /dev/null @@ -1,51 +0,0 @@ -const React = require('react'); -const PropTypes = require('prop-types'); - -/** - * The actions class. - */ -const ACTIONS = 'editable-element-actions'; - -/** - * General element action component. - */ -class RevertAction extends React.Component { - - /** - * The component constructor. - * - * @param {Object} props - The properties. - */ - constructor(props) { - super(props); - this.element = props.element; - } - - /** - * Revert the change. - */ - handleClick() { - this.element.revert(); - } - - /** - * Render a single editable key. - * - * @returns {React.Component} The element component. - */ - render() { - return ( -
    - -
    - ); - } -} - -RevertAction.displayName = 'RevertAction'; - -RevertAction.propTypes = { - element: PropTypes.object.isRequired -}; - -module.exports = RevertAction; diff --git a/src/internal-packages/crud/lib/component/types.jsx b/src/internal-packages/crud/lib/component/types.jsx deleted file mode 100644 index 2c72c215e53..00000000000 --- a/src/internal-packages/crud/lib/component/types.jsx +++ /dev/null @@ -1,174 +0,0 @@ -const _ = require('lodash'); -const app = require('hadron-app'); -const React = require('react'); -const PropTypes = require('prop-types'); -const TypeChecker = require('hadron-type-checker'); -const { DateEditor } = require('./editor'); - -require('bootstrap/js/dropdown'); - -/** - * Object constant. - */ -const OBJECT = 'Object'; - -/** - * Array constant. - */ -const ARRAY = 'Array'; - -/** - * The version at which high precision values are available. - */ -const HP_VERSION = '3.4.0'; - -/** - * General types component. - */ -class Types extends React.Component { - - /** - * The component constructor. - * - * @param {Object} props - The properties. - */ - constructor(props) { - super(props); - this.state = { isOpen: false }; - this.element = props.element; - this._version = app.instance.build.version; - } - - /** - * Get the class name for the dropdown. - * - * @returns {String} The class name. - */ - getClassName() { - let className = 'editable-element-types dropdown'; - if (this.element.currentType !== this.element.type) { - className = `${className} editable-element-types-is-edited`; - } - return this.state.isOpen ? `${className}` : `${className} closed`; - } - - /** - * Get the castable value for this value. - * - * @returns {Object} The cast value. - */ - castableValue() { - return this.element.generateObject(); - } - - /** - * Handles a change in the type. - * - * @param {Event} evt - The event. - */ - handleTypeChange(evt) { - const newType = evt.target.innerText; - if (newType === OBJECT) { - this.element.edit('{'); - this.element.next(); - } else if (newType === ARRAY) { - this.element.edit('['); - this.element.next(); - } else { - try { - if (newType === 'Date') { - const editor = new DateEditor(this.element); - editor.edit(this.castableValue()); - editor.complete(); - } else { - const value = TypeChecker.cast(this.castableValue(), newType); - this.element.edit(value); - } - } catch (e) { - this.element.setInvalid(this.element.currentValue, newType, e.message); - } - } - } - - /** - * Are high precision values available? - * - * @returns {boolean} if high precision values are available. - */ - isHighPrecision() { - return this._version >= HP_VERSION; - } - - /** - * Is the type changeable? - * - * @returns {Boolean} If the type is changeable. - */ - isTypeChangeable() { - return this.element.isValueEditable() || this.element.isAdded(); - } - - removeOpenClass() { - this.setState({ isOpen: !this.state.isOpen }); - } - - /** - * Render the type list dropdown. - * - * @returns {Component} The react component. - */ - renderDropdown() { - return ( -
    - -
      - {this.renderTypes()} -
    -
    - ); - } - - /** - * Render the types - * - * @returns {Component} The react component. - */ - renderTypes() { - return _.map(TypeChecker.castableTypes(this.isHighPrecision()), (type) => { - return ( -
  • - {type} -
  • - ); - }); - } - - /** - * Render a type list. - * - * @returns {React.Component} The element component. - */ - render() { - return this.renderDropdown(); - } -} - -Types.displayName = 'Types'; - -Types.propTypes = { - element: PropTypes.object.isRequired -}; - -module.exports = Types; diff --git a/src/internal-packages/crud/lib/component/utils.js b/src/internal-packages/crud/lib/component/utils.js deleted file mode 100644 index b3eed393d4e..00000000000 --- a/src/internal-packages/crud/lib/component/utils.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Get the size for the string value. - * - * @param {Object} value - The value. - * - * @return {Number} The size. - */ -const size = (value) => { - const length = String(value).length; - return length === 0 ? 1 : length; -}; - -module.exports = size; diff --git a/src/internal-packages/crud/lib/store/insert-document-store.js b/src/internal-packages/crud/lib/store/insert-document-store.js deleted file mode 100644 index 9c63bbfaf65..00000000000 --- a/src/internal-packages/crud/lib/store/insert-document-store.js +++ /dev/null @@ -1,59 +0,0 @@ -const Reflux = require('reflux'); -const app = require('hadron-app'); -const toNS = require('mongodb-ns'); -const Actions = require('../actions'); - -/** - * The reflux store for inserting documents. - */ -const InsertDocumentStore = Reflux.createStore({ - - /** - * Initialize the insert document list store. - */ - init: function() { - this.filter = {}; - this.listenToExternalStore('Query.ChangedStore', this.onQueryChanged.bind(this)); - this.listenTo(Actions.insertDocument, this.insertDocument); - this.NamespaceStore = app.appRegistry.getStore('App.NamespaceStore'); - }, - - /** - * Insert the document. - * - * @param {Document} doc - The document to insert. - */ - insertDocument: function(doc) { - app.dataService.insertOne(this.NamespaceStore.ns, doc, {}, (error) => { - if (error) { - return this.trigger(false, error); - } - // check if the newly inserted document matches the current filter, by - // running the same filter but targeted only to the doc's _id. - const filter = Object.assign({}, this.filter, { _id: doc._id }); - app.dataService.count(this.NamespaceStore.ns, filter, {}, (err, count) => { - if (err) { - return this.trigger(false, err); - } - // count is either 0 or 1, if 1 then the new doc matches the filter - if (count > 0) { - return this.trigger(true, doc); - } - Actions.closeInsertDocumentDialog(); - }); - }); - }, - - /** - * Fires when the query is changed. - * - * @param {Object} state - The query state. - */ - onQueryChanged: function(state) { - if (state.ns && toNS(state.ns).collection && state.filter) { - this.filter = state.filter; - } - } -}); - -module.exports = InsertDocumentStore; diff --git a/src/internal-packages/crud/lib/store/load-more-documents-store.js b/src/internal-packages/crud/lib/store/load-more-documents-store.js deleted file mode 100644 index 12791959d3f..00000000000 --- a/src/internal-packages/crud/lib/store/load-more-documents-store.js +++ /dev/null @@ -1,77 +0,0 @@ -const Reflux = require('reflux'); -const app = require('hadron-app'); -const toNS = require('mongodb-ns'); -const Actions = require('../actions'); -const _ = require('lodash'); - -// const debug = require('debug')('mongodb-compass:crud:load-more-store'); - -const NUM_PAGE_DOCS = 20; -/** - * The reflux store for loading more documents. - */ -const LoadMoreDocumentsStore = Reflux.createStore({ - - /** - * Initialize the reset document list store. - */ - init: function() { - this.filter = {}; - this.sort = [[ '_id', 1 ]]; - this.limit = 0; - this.skip = 0; - this.project = null; - this.counter = 0; - - this.NamespaceStore = app.appRegistry.getStore('App.NamespaceStore'); - this.listenToExternalStore('Query.ChangedStore', this.onQueryChanged.bind(this)); - this.listenTo(Actions.fetchNextDocuments, this.fetchNextDocuments.bind(this)); - }, - - /** - * Fires when the query is changed. Need to copy the latest query details - * and reset the counter. - * - * @param {Object} state - The query state. - */ - onQueryChanged: function(state) { - if (state.ns && toNS(state.ns).collection) { - this.filter = state.filter || {}; - this.sort = _.pairs(state.sort); - this.limit = state.limit; - this.skip = state.skip; - this.project = state.project; - this.counter = 0; - } - }, - - /** - * Fetch the next page of documents. Increase the counter by the page size - * (20 documents) until we reach the user-specified limit. Also take into - * account user-specified skip. - * - * @param {Integer} skip - The number of documents to skip. - */ - fetchNextDocuments: function(skip) { - this.counter += NUM_PAGE_DOCS; - let nextPageCount = 20; - if (this.limit > 0) { - nextPageCount = Math.min(Math.max(0, this.limit - this.counter), NUM_PAGE_DOCS); - if (nextPageCount === 0) { - return; - } - } - const options = { - skip: skip + this.skip, - limit: nextPageCount, - sort: this.sort, - fields: this.project, - promoteValues: false - }; - app.dataService.find(this.NamespaceStore.ns, this.filter, options, (error, documents) => { - this.trigger(error, documents); - }); - } -}); - -module.exports = LoadMoreDocumentsStore; diff --git a/src/internal-packages/crud/lib/store/open-insert-document-dialog-store.js b/src/internal-packages/crud/lib/store/open-insert-document-dialog-store.js deleted file mode 100644 index ca7618357bd..00000000000 --- a/src/internal-packages/crud/lib/store/open-insert-document-dialog-store.js +++ /dev/null @@ -1,46 +0,0 @@ -const ipc = require('hadron-ipc'); -const Reflux = require('reflux'); -const ObjectId = require('bson').ObjectId; -const Actions = require('../actions'); -const HadronDocument = require('hadron-document'); - -// const debug = require('debug')('mongodb-compass:crud:store:open-insert-doc'); - -/** - * The reflux store for opening the insert document dialog. - */ -const OpenInsertDocumentDialogStore = Reflux.createStore({ - - /** - * Initialize the reset document list store. - */ - init: function() { - this.listenTo(Actions.openInsertDocumentDialog, this.openInsertDocumentDialog.bind(this)); - ipc.on('window:menu-open-insert-document-dialog', () => { - this.openInsertDocumentDialog({ _id: new ObjectId(), '': '' }, false); - }); - }, - - /** - * Open the insert document dialog. - * - * @param {Object} doc - The document to open the dialog with. - * @param {Boolean} clone - If the operation is a clone. - */ - openInsertDocumentDialog: function(doc, clone) { - const hadronDoc = new HadronDocument(doc, true); - if (clone) { - // We need to remove the _id or we will get an duplicate key error on - // insert, and we currently do not allow editing of the _id field. - for (const element of hadronDoc.elements) { - if (element.currentKey === '_id') { - hadronDoc.elements.remove(element); - break; - } - } - } - this.trigger(hadronDoc); - } -}); - -module.exports = OpenInsertDocumentDialogStore; diff --git a/src/internal-packages/crud/lib/store/remove-document-store.js b/src/internal-packages/crud/lib/store/remove-document-store.js deleted file mode 100644 index 7e6ac453351..00000000000 --- a/src/internal-packages/crud/lib/store/remove-document-store.js +++ /dev/null @@ -1,26 +0,0 @@ -const Reflux = require('reflux'); -const Actions = require('../actions'); - -/** - * The reflux store for removing a document from the list. - */ -const RemoveDocumentStore = Reflux.createStore({ - - /** - * Initialize the reset document list store. - */ - init: function() { - this.listenTo(Actions.documentRemoved, this.remove); - }, - - /** - * This function is called when when a document is deleted. - * - * @param {Object} id - The document id. - */ - remove: function(id) { - this.trigger(id); - } -}); - -module.exports = RemoveDocumentStore; diff --git a/src/internal-packages/crud/lib/store/reset-document-list-store.js b/src/internal-packages/crud/lib/store/reset-document-list-store.js deleted file mode 100644 index f4d7d911c84..00000000000 --- a/src/internal-packages/crud/lib/store/reset-document-list-store.js +++ /dev/null @@ -1,86 +0,0 @@ -const Reflux = require('reflux'); -const app = require('hadron-app'); -const toNS = require('mongodb-ns'); -const Actions = require('../actions'); -const _ = require('lodash'); - -// const debug = require('debug')('mongodb-compass:crud:reset-store'); - -/** - * The reflux store for resetting the document list. - */ -const ResetDocumentListStore = Reflux.createStore({ - - /** - * Initialize the reset document list store. - */ - init: function() { - this.filter = {}; - this.sort = [[ '_id', 1 ]]; - this.limit = 0; - this.skip = 0; - this.project = null; - this.ns = ''; - - // listen for query changes - this.listenToExternalStore('Query.ChangedStore', this.onQueryChanged.bind(this)); - - Actions.refreshDocuments.listen(this.reset.bind(this)); - }, - - /** - * Fires when the query is changed. - * - * @param {Object} state - The query state. - */ - onQueryChanged: function(state) { - if (state.ns && toNS(state.ns).collection) { - this.filter = state.filter || {}; - this.sort = _.pairs(state.sort); - this.limit = state.limit; - this.skip = state.skip; - this.project = state.project; - this.ns = state.ns; - this.reset(); - } - }, - - /** - * This function is called when the collection filter changes. - * - * @param {Object} filter - The query filter. - */ - reset: function() { - const countOptions = { - skip: this.skip - }; - - const findOptions = { - sort: this.sort, - fields: this.project, - skip: this.skip, - limit: 20, - promoteValues: false - }; - - // only set limit if it's > 0, read-only views cannot handle 0 limit. - if (this.limit > 0) { - countOptions.limit = this.limit; - findOptions.limit = Math.min(20, this.limit); - } - - app.dataService.count(this.ns, this.filter, countOptions, (err, count) => { - if (!err) { - app.dataService.find(this.ns, this.filter, findOptions, (error, documents) => { - this.trigger(error, documents, count); - }); - } else { - // If the count gets an error we need to display this to the user since - // they have the wrong privs. - this.trigger(err); - } - }); - } -}); - -module.exports = ResetDocumentListStore; diff --git a/src/internal-packages/crud/package.json b/src/internal-packages/crud/package.json deleted file mode 100644 index 36bfd4d93ff..00000000000 --- a/src/internal-packages/crud/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "crud", - "productName": "Compass CRUD Support", - "description": "CRUD support for Compass as an internal package.", - "version": "0.0.1", - "authors": "MongoDB Inc.", - "private": true, - "main": "./index.js" -} diff --git a/src/internal-packages/crud/styles/document-actions.less b/src/internal-packages/crud/styles/document-actions.less deleted file mode 100644 index 28c994ca052..00000000000 --- a/src/internal-packages/crud/styles/document-actions.less +++ /dev/null @@ -1,29 +0,0 @@ -.document-actions { - position: absolute; - width: 100%; - top: 15px; - padding: 0px 20px 0px 20px; - - &-button { - visibility: hidden; - transition: none; - z-index: 200; - position: relative; - } - - &-expand-button { - padding: 0px 5px 0px 5px; - - .fa { - height: 12px; - width: 12px; - } - } - - &-right { - float: right; - } - &-left { - float: left; - } -} diff --git a/src/internal-packages/crud/styles/document-elements.less b/src/internal-packages/crud/styles/document-elements.less deleted file mode 100644 index f15b7e9a50c..00000000000 --- a/src/internal-packages/crud/styles/document-elements.less +++ /dev/null @@ -1,53 +0,0 @@ -.document-elements { - - counter-reset: document-line-number; - display: block; - list-style: none; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 100%; - - .line-number { - position: absolute; - left: 21px; - } - - .editable-element-types-label { - position: absolute; - right: 0px; - display: inline-block; - width: 120px; - color: @gray3; - height: 17px; - padding-left: 12px; - } - - editable-expandable-element-header:hover { - background-color: #ebebed !important; - - input { - background-color: #ebebed !important; - } - - .editable-element-actions { - opacity: 1; - } - - .editable-element-types { - .btn { - border-color: #8c8c8c; - } - .caret { - visibility: visible; - } - - &-is-edited { - .btn { - color: #000000; - font-weight: bold; - } - } - } - } -} - diff --git a/src/internal-packages/crud/styles/document-footer.less b/src/internal-packages/crud/styles/document-footer.less deleted file mode 100644 index ed4607881aa..00000000000 --- a/src/internal-packages/crud/styles/document-footer.less +++ /dev/null @@ -1,107 +0,0 @@ -.document-footer { - font-family: "Akzidenz", "Helvetica Neue", Helvetica, Arial, sans-serif; - height: 28px; - vertical-align: middle; - top: 20px; - position: relative; - - &-message { - position: absolute; - right: 150px; - left: 10px; - font-size: 14px; - font-style: italic; - padding-left: 10px; - padding-top: 4px; - color: @pw; - overflow: hidden; - height: 28px; - } - - &-is-modified { - background-color: @alertOrange; - - .document-footer-actions { - - .cancel { - color: @alertOrangeBorder; - } - - .btn-default { - color: @alertOrangeBorder; - border-color: @alertOrangeBorder; - background: none; - box-shadow: inset 0 -1px 0 0 @alertOrangeBorder; - transition: none; - } - } - } - - &-is-in-progress { - background-color: @chart1; - } - - &-is-success { - background-color: @green2; - - .document-footer-actions { - - .cancel { - color: white; - } - - .btn-default { - color: white; - border-color: white; - background: none; - box-shadow: inset 0 -1px 0 0 white; - transition: none; - } - } - - } - - &-is-error { - background-color: @alertRed; - - .document-footer-actions { - - .cancel { - color: @alertRedBg; - } - - .error { - color: @pw; - background: rgba(255,255,255,0.2); - border: 1px solid rgba(255,255,255,0.4); - - &:hover { - background: rgba(255,255,255,0.4); - border: 1px solid rgba(255,255,255,0.6); - } - } - } - } - - &-is-viewing { - background-color: #F8F8F8; - - .document-footer-actions { - - .cancel { - color: #B8B8B8; - } - } - } - - &-actions { - position: absolute; - right: 10px; - top: 2px; - padding-top: 1px; - - .cancel { - font-weight: bold; - } - } -} diff --git a/src/internal-packages/crud/styles/document-list.less b/src/internal-packages/crud/styles/document-list.less deleted file mode 100644 index f012438f5a9..00000000000 --- a/src/internal-packages/crud/styles/document-list.less +++ /dev/null @@ -1,19 +0,0 @@ -.document-list { - .list-unstyled; - padding-top: 0; - padding-bottom: 10px; - position: relative; - - &-item { - position: relative; - font-family: @font-family-monospace; - font-size: 11px; - margin-bottom: 5px; - background: @pw; - - &:last-child { - margin-bottom: 0; - border-bottom: 0 solid transparent; - } - } -} diff --git a/src/internal-packages/crud/styles/document.less b/src/internal-packages/crud/styles/document.less deleted file mode 100644 index 0aada2aff0a..00000000000 --- a/src/internal-packages/crud/styles/document.less +++ /dev/null @@ -1,31 +0,0 @@ -.document { - font-family: @font-family-monospace; - font-size: 11px; - list-style: none; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 100%; - padding-top: 20px; - padding-bottom: 20px; - - &-elements { - position: relative; - - .btn { - margin: 10px 0px 0px 22px; - - i { - margin-right: 5px; - } - } - } - - &-is-deleting { - box-shadow: 2px 5px 8px rgba(0, 0, 0, 0.2); - border: 1px solid @alertRed; - } - - &-is-editing { - box-shadow: 2px 5px 8px rgba(0, 0, 0, 0.2); - } -} diff --git a/src/internal-packages/crud/styles/editable-element-field.less b/src/internal-packages/crud/styles/editable-element-field.less deleted file mode 100644 index 331cf845cd8..00000000000 --- a/src/internal-packages/crud/styles/editable-element-field.less +++ /dev/null @@ -1,23 +0,0 @@ -.editable-element-field { - font-weight: bold; - border: none; - padding-left: 1px; - -webkit-user-select: text; - max-width: 15vw; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - - &-is-editing { - border: 1px solid @gray3; - box-shadow: 0px 2px 4px 0px rgba(0,0,0,0.2); - z-index: 10; - margin-top: -1px; - margin-bottom: -1px; - } - - &-is-duplicate { - border: 1px solid red; - color: red; - } -} diff --git a/src/internal-packages/crud/styles/editable-element-value.less b/src/internal-packages/crud/styles/editable-element-value.less deleted file mode 100644 index b14766d21ce..00000000000 --- a/src/internal-packages/crud/styles/editable-element-value.less +++ /dev/null @@ -1,94 +0,0 @@ -.editable-element-value { - display: inline-block; - border: none; - padding-left: 1px; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - max-width: ~"calc(85vw - 260px);"; - - &-tooltip { - height: 20px !important; - font-size: 11px !important; - opacity: 1 !important; - padding: 3px 6px !important; - } - - &-is-string { - white-space: pre-line; - word-break: break-all; - padding-right: 0px; - } - - &-is-int32 { - color: #145a32; - } - - &-is-int64 { - color: #196f3d; - } - - &-is-double { - color: #1e8449; - } - - &-is-decimal128 { - color: #229954; - } - - &-is-date { - color: firebrick; - } - - &-is-boolean { - color: purple; - } - - &-is-objectid { - color: orangered; - &::before { - content: "ObjectID('"; - } - &::after { - content: "')"; - } - } - - &-is-editing { - border: 1px solid @gray3; - box-shadow: 0px 2px 4px 0px rgba(0,0,0,0.2); - z-index: 10; - margin-top: -1px; - margin-bottom: -1px; - } - - &-is-invalid-type { - background-color: #F88379 !important; - color: #FFFFFF !important; - } - - &-wrapper { - flex-grow: 3; - z-index: 100; - - &-is-string { - color: steelblue; - &::before { - content: "\""; - } - &::after { - content: "\""; - } - } - - &-is-objectid { - color: orangered; - &::before { - content: "ObjectID('"; - } - &::after { - content: "')"; - } - } - } -} diff --git a/src/internal-packages/crud/styles/editable-element.less b/src/internal-packages/crud/styles/editable-element.less deleted file mode 100644 index b2253d1d028..00000000000 --- a/src/internal-packages/crud/styles/editable-element.less +++ /dev/null @@ -1,299 +0,0 @@ -.editable-element { - width: 100%; - display: flex; - flex-direction: row; - flex-wrap: nowrap; - align-items: center; - - &-field { - display: inline-block; - vertical-align: top; - margin-left: 22px; - } - - &-expand-button { - position: absolute; - padding: 2px 6px 0px 5px; - } - - &-separator { - padding-right: 2px; - vertical-align: top; - } - - &-is-editing { - - .line-number:focus { - outline: 0; - color: #494747; - font-weight: bold; - border: 1px solid #494747; - border-radius: 2px; - - &:before { - content: '+'; - } - - &-is-selected { - &:before { - content: '-'; - } - } - } - - &:hover { - background-color: @gray8; - - &::before { - background-color: @gray8; - } - - input { - background-color: @gray8; - - &:focus { - background-color: @pw; - } - } - - .line-number { - color: #494747; - font-weight: bold; - border: 1px solid #494747; - border-radius: 2px; - - &:before { - content: '+'; - } - - &-is-selected { - &:before { - content: '-'; - } - } - } - - .editable-element-actions { - opacity: 1; - } - - .editable-element-types { - .btn { - background-color: @gray6; - color: @gray0; - } - .caret { - visibility: visible; - } - - &-is-edited { - .btn { - font-weight: bold; - color: #000000; - } - } - } - } - } - - &-is-edited { - background-color: @alertOrangeBg; - - &:hover { - .line-number { - color: @pw; - border: 1px solid @pw; - - &-is-selected { - border: 1px solid @pw; - } - } - } - - .line-number { - background-color: @alertOrange; - color: @pw; - border: 1px solid @pw; - - &-is-selected { - border: 1px solid @pw; - } - } - - input { - background-color: @alertOrangeBg; - } - - .editable-element-field { - background-color: @alertOrangeBg; - - &:focus { - background-color: @pw; - } - - &::before { - background-color: @alertOrangeBg; - } - } - } - - &-is-added { - background-color: @greenBg; - - .editable-element-field { - background-color: @greenBg; - - &:focus { - background-color: @pw; - } - } - - &:hover { - .line-number { - color: @pw; - border: 1px solid @pw; - - &-is-selected { - border: 1px solid @pw; - } - } - } - - .line-number { - background-color: @green2; - color: @pw; - border: 1px solid @pw; - - &-is-selected { - border: 1px solid @pw; - } - } - - input { - background-color: @greenBg; - } - } - - &-is-removed { - background-color: @alertRedBg; - - .editable-element-field { - background-color: @alertRedBg; - - &:focus { - background-color: @pw; - } - } - - &:hover { - .line-number { - color: @pw; - border: 1px solid @pw; - - &-is-selected { - border: 1px solid @pw; - } - } - } - - .line-number { - background-color: @alertRed; - color: @pw; - border: 1px solid @pw; - - &-is-selected { - border: 1px solid @pw; - } - } - - input { - background-color: @alertRedBg; - } - } - - &-actions { - display: inline-block; - opacity: 0; - width: 18px; - font-size: 13px; - line-height: 11px; - margin-top: 1px; - text-align: center; - color: @gray3; - position: absolute; - left: 3px; - - i:hover { - cursor: pointer; - } - } - - &-types { - display: inline-block; - width: 120px; - color: @gray3; - height: 17px; - flex-shrink: 0; - - .closed > .dropdown-menu { - display: none; - } - - .type-label { - padding: 1px 12px; - } - - .btn { - background-color: transparent; - color: @gray4; - border: none; - text-transform: none; - font-size: 11px; - border-radius: 8px; - padding: 1px 12px; - } - - .caret { - visibility: hidden; - } - - &-is-edited { - .btn { - color: #000000; - font-weight: bold; - } - } - - .dropdown-menu { - min-width: 120px; - - li { - span { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - font-size: 11px; - color: #333333; - white-space: nowrap; - } - span:hover { - color: #313030; - text-decoration: none; - background: #e6e6e6; - } - } - } - } - - &-value { - position: relative; - vertical-align: top; - z-index: 1; - } - - &-field { - position: relative; - z-index: 1; - } -} diff --git a/src/internal-packages/crud/styles/editable-expandable-element.less b/src/internal-packages/crud/styles/editable-expandable-element.less deleted file mode 100644 index 0f4dc630906..00000000000 --- a/src/internal-packages/crud/styles/editable-expandable-element.less +++ /dev/null @@ -1,225 +0,0 @@ -.editable-expandable-element { - width: 100%; - display: block; - font-size: 11px; - - &-is-edited { - background-color: @alertOrangeBg; - - .line-number { - background-color: @alertOrange; - color: @pw; - - &-is-selected { - border: 1px solid @pw; - border-radius: 2px; - } - } - - input { - background-color: @alertOrangeBg; - } - - .editable-element-field { - background-color: @alertOrangeBg; - - &:focus { - background-color: @pw; - } - - &::before { - background-color: @alertOrangeBg; - } - } - } - - &-is-added { - background-color: @greenBg; - - .editable-element-field { - background-color: @greenBg; - - &:focus { - background-color: @pw; - } - } - - .line-number { - background-color: @green2; - color: @pw; - - &-is-selected { - border: 1px solid @pw; - border-radius: 2px; - } - } - - input { - background-color: @greenBg; - } - } - - &-is-removed { - background-color: @alertRedBg; - - .editable-element-field { - background-color: @alertRedBg; - - &:focus { - background-color: @pw; - } - } - - .line-number { - background-color: @alertRed; - color: @pw; - - &-is-selected { - border: 1px solid @pw; - border-radius: 2px; - } - } - - input { - background-color: @alertRedBg; - } - } - - &-header { - display: flex; - flex-direction: row; - flex-wrap: nowrap; - align-items: center; - cursor: pointer; - - &-label { - display: inline-block; - flex-grow: 3; - } - - &-is-editing { - - .line-number:focus { - outline: 0; - color: #494747; - font-weight: bold; - border: 1px solid #494747; - border-radius: 2px; - - &:before { - content: '+'; - } - - &-is-selected { - &:before { - content: '-'; - } - } - } - - &:hover { - background-color: @gray8; - - &::before { - background-color: @gray8; - } - - .line-number { - color: #494747; - font-weight: bold; - border: 1px solid #494747; - border-radius: 2px; - - &:before { - content: '+'; - } - - &-is-selected { - &:before { - content: '-'; - } - } - } - - input { - background-color: @gray8; - - &:focus { - background-color: @pw; - } - } - - .editable-element-actions { - opacity: 1; - } - - .editable-element-types { - .btn { - background-color: @gray6; - color: @gray0; - } - .caret { - visibility: visible; - } - &-is-edited { - .btn { - color: #000000; - font-weight: bold; - } - } - } - } - } - - &-is-edited { - - &:hover { - .line-number { - color: @pw; - border: 1px solid @pw; - - &-is-selected { - border: 1px solid @pw; - } - } - } - } - - &-is-added { - - &:hover { - .line-number { - color: @pw; - border: 1px solid @pw; - - &-is-selected { - border: 1px solid @pw; - } - } - } - } - - &-is-removed { - - &:hover { - .line-number { - color: @pw; - border: 1px solid @pw; - - &-is-selected { - border: 1px solid @pw; - } - } - } - } - } - - &-children { - display: none; - padding-left: 0px; - - &-is-expanded { - display: block; - } - } -} diff --git a/src/internal-packages/crud/styles/element.less b/src/internal-packages/crud/styles/element.less deleted file mode 100644 index 0df383230bf..00000000000 --- a/src/internal-packages/crud/styles/element.less +++ /dev/null @@ -1,66 +0,0 @@ -.element { - - width: 100%; - display: block; - font-size: 11px; - - &-field { - display: inline-block; - font-weight: bold; - vertical-align: top; - -webkit-user-select: text; - } - - &-separator { - padding-right: 2px; - vertical-align: top; - -webkit-user-select: text; - } - - &-value { - display: inline-block; - vertical-align: top; - -webkit-user-select: text; - flex-grow: 3; - - &-is-string { - color: steelblue; - white-space: pre-line; - word-break: break-all; - } - - &-is-int32 { - color: #145a32; - } - - &-is-int64 { - color: #196f3d; - } - - &-is-double { - color: #1e8449; - } - - &-is-decimal128 { - color: #229954; - } - - &-is-date { - color: firebrick; - } - - &-is-boolean { - color: purple; - } - - &-is-objectid { - color: orangered; - &::before { - content: "ObjectID('"; - } - &::after { - content: "')"; - } - } - } -} diff --git a/src/internal-packages/crud/styles/expandable-element.less b/src/internal-packages/crud/styles/expandable-element.less deleted file mode 100644 index 6ec6c0b8d66..00000000000 --- a/src/internal-packages/crud/styles/expandable-element.less +++ /dev/null @@ -1,52 +0,0 @@ -.expandable-element { - - width: 100%; - display: block; - font-size: 11px; - - &-header { - - cursor: pointer; - - &-field { - display: inline-block; - font-weight: bold; - } - - &-separator { - padding-right: 2px; - } - - &-label { - display: inline-block; - } - - &-toggle { - display: inline-block; - width: 0; - height: 0; - vertical-align: middle; - border-left: @caret-width-base solid; - border-right: 0; - border-top: @caret-width-base solid transparent; - border-bottom: @caret-width-base solid transparent; - } - - &-is-expanded { - .expandable-element-header-toggle { - border-top: @caret-width-base solid; - border-right: @caret-width-base solid transparent; - border-left: @caret-width-base solid transparent; - } - } - } - - &-children { - display: none; - padding-left: 15px; - - &-is-expanded { - display: block; - } - } -} diff --git a/src/internal-packages/crud/styles/index.less b/src/internal-packages/crud/styles/index.less deleted file mode 100644 index 58602eec04b..00000000000 --- a/src/internal-packages/crud/styles/index.less +++ /dev/null @@ -1,42 +0,0 @@ -@import "./document-list.less"; -@import "./element.less"; -@import "./expandable-element.less"; -@import "./document-actions.less"; -@import "./document.less"; -@import "./document-elements.less"; -@import "./editable-expandable-element.less"; -@import "./editable-element.less"; -@import "./editable-element-field.less"; -@import "./editable-element-value.less"; -@import "./line-number.less"; -@import "./loading-indicator.less"; -@import "./document-footer.less"; -@import "./insert-document-dialog.less"; - -.expandable-element-header { - - &-toggle { - margin-left: -10px; - margin-right: 6px; - } - - &-is-expanded { - .expandable-element-header-toggle { - margin-left: -14px; - } - } -} - -.document { - - &:hover { - .document-actions-button { - visibility: visible; - } - } -} - -.document-actions-button { - visibility: hidden; - margin-left: 3px; -} diff --git a/src/internal-packages/crud/styles/insert-document-dialog.less b/src/internal-packages/crud/styles/insert-document-dialog.less deleted file mode 100644 index 5e4746c01fa..00000000000 --- a/src/internal-packages/crud/styles/insert-document-dialog.less +++ /dev/null @@ -1,116 +0,0 @@ -.modal-body { - .document { - .document-elements { - width: 100%; - - .editable-element-actions { - display: inline-block; - opacity: 0; - width: 18px; - line-height: 11px; - margin-right: 35px; - text-align: center; - cursor: pointer; - color: @gray3; - } - - .editable-element-field { - max-width: 10vw; - } - - .editable-element-value { - max-width: 20vw; - } - - .editable-element-is-added, .editable-expandable-element-header-is-added { - background-color: @pw; - - input { - background-color: @pw; - - &:focus { - background-color: @pw; - } - } - - .line-number { - background-color: @pw; - color: @gray3; - } - } - - .editable-element { - - &:hover { - background-color: @gray8; - - &::before { - background-color: @gray8; - } - - input { - background-color: @gray8; - - &:focus { - background-color: @pw; - } - } - - .editable-element-actions { - opacity: 1; - } - - .editable-element-types { - .btn { - background-color: @gray6; - color: @gray0; - } - .caret { - visibility: visible; - } - - &-is-edited { - .btn { - color: #000000; - font-weight: bold; - } - } - } - } - } - - .editable-expandable-element-header { - - &:hover { - background-color: @gray8; - - &::before { - background-color: @gray8; - } - - input { - background-color: @gray8; - - &:focus { - background-color: @pw; - } - } - - .editable-element-actions { - opacity: 1; - } - - .editable-element-types { - .btn { - background-color: @gray6; - color: @gray0; - } - .caret { - visibility: visible; - } - } - } - } - } - } -} diff --git a/src/internal-packages/crud/styles/line-number.less b/src/internal-packages/crud/styles/line-number.less deleted file mode 100644 index 5b9b0dd4dda..00000000000 --- a/src/internal-packages/crud/styles/line-number.less +++ /dev/null @@ -1,65 +0,0 @@ -.line-number { - - display: inline-block; - color: @gray4; - width: 18px; - height: 17px; - text-align: center; - cursor: pointer; - - &::before { - counter-increment: document-line-number; - content: counter(document-line-number); - } - - &-is-selected { - color: #494747; - font-weight: bold; - border: 1px solid #494747; - border-radius: 2px; - - &:before { - content: '-'; - } - } - - &-menu { - display: none; - color: @gray3; - - li { - span { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - font-size: 11px; - color: #333333; - white-space: nowrap; - - .fa-level-down { - margin-right: 12px; - } - - .fa-plus-square-o { - margin-right: 10px; - } - } - span:hover { - color: #313030; - text-decoration: none; - background: #e6e6e6; - } - } - - &-field { - display: inline-block !important; - padding: 0px !important; - font-weight: bold !important; - } - - &-is-visible { - display: block; - } - } -} diff --git a/src/internal-packages/crud/styles/loading-indicator.less b/src/internal-packages/crud/styles/loading-indicator.less deleted file mode 100644 index 92cce6554d1..00000000000 --- a/src/internal-packages/crud/styles/loading-indicator.less +++ /dev/null @@ -1,15 +0,0 @@ -.loading-indicator { - display: none; - width: 100%; - text-align: center; - vertical-align: middle; - - &-is-loading { - display: block; - } - - i { - font-size: 25px; - color: #CCCCCC; - } -} From 56fb18b700e3acdb2ff3b55e02be02da7fb3cb31 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 3 Aug 2017 10:44:24 -0400 Subject: [PATCH 2/9] Add compass-crud external package --- package.json | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index f3833958604..cdf1ee4496f 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "node_modules/@mongodb-js/compass-document-validation", "node_modules/@mongodb-js/compass-deployment-awareness", "node_modules/@mongodb-js/compass-charts", + "node_modules/@mongodb-js/compass-crud", "node_modules/@mongodb-js/compass-query-history" ], "styles": [ @@ -54,21 +55,9 @@ "node_modules/@mongodb-js/compass-serverstats", "node_modules/@mongodb-js/compass-document-validation", "node_modules/@mongodb-js/compass-deployment-awareness", - "node_modules/@mongodb-js/compass-charts" - ], - "styles": [ - "index" - ] - }, - "compass-charts": { - "name": "mongodb-compass-charts", - "productName": "MongoDB Compass Visualisations", - "plugins-directory": ".mongodb/compass-charts/plugins", - "packages": [ - "node_modules/@mongodb-js/compass-serverstats", - "node_modules/@mongodb-js/compass-document-validation", - "node_modules/@mongodb-js/compass-deployment-awareness", - "node_modules/@mongodb-js/compass-charts" + "node_modules/@mongodb-js/compass-charts", + "node_modules/@mongodb-js/compass-crud", + "node_modules/@mongodb-js/compass-query-history" ], "styles": [ "index" @@ -142,6 +131,7 @@ }, "dependencies": { "@mongodb-js/compass-charts": "^0.2.3", + "@mongodb-js/compass-crud": "0.0.1", "@mongodb-js/compass-deployment-awareness": "3.2.0", "@mongodb-js/compass-document-validation": "4.1.0", "@mongodb-js/compass-serverstats": "9.1.1", From 10222835aec0ca778adebd94ac4b01ac54f932ca Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Mon, 7 Aug 2017 16:29:36 -0400 Subject: [PATCH 3/9] Remove crud styles --- src/app/index.less | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/index.less b/src/app/index.less index c8953b04254..b888049a7d5 100644 --- a/src/app/index.less +++ b/src/app/index.less @@ -17,7 +17,6 @@ @import "../internal-packages/app/styles/index.less"; @import "../internal-packages/collection/styles/index.less"; @import "../internal-packages/collection-stats/styles/index.less"; -@import "../internal-packages/crud/styles/index.less"; @import "../internal-packages/database-ddl/styles/index.less"; @import "../internal-packages/home/styles/index.less"; @import "../internal-packages/status/styles/index.less"; From db7d341db8a0c721dd1d78e8c6dc2b1d1e8f36f5 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Mon, 7 Aug 2017 16:44:47 -0400 Subject: [PATCH 4/9] COMPASS-1101: Use CRUD from external package --- package.json | 2 +- test/functional/crud.test.js | 156 -------- .../support/packages/spectron-crud.js | 362 ------------------ 3 files changed, 1 insertion(+), 519 deletions(-) delete mode 100644 test/functional/crud.test.js delete mode 100644 test/functional/support/packages/spectron-crud.js diff --git a/package.json b/package.json index cdf1ee4496f..b3b37ac9b5d 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,7 @@ }, "dependencies": { "@mongodb-js/compass-charts": "^0.2.3", - "@mongodb-js/compass-crud": "0.0.1", + "@mongodb-js/compass-crud": "0.0.2", "@mongodb-js/compass-deployment-awareness": "3.2.0", "@mongodb-js/compass-document-validation": "4.1.0", "@mongodb-js/compass-serverstats": "9.1.1", diff --git a/test/functional/crud.test.js b/test/functional/crud.test.js deleted file mode 100644 index 781c5719aaa..00000000000 --- a/test/functional/crud.test.js +++ /dev/null @@ -1,156 +0,0 @@ -const Connection = require('mongodb-connection-model'); -const DataService = require('mongodb-data-service'); -const { launchCompass, quitCompass} = require('./support/spectron-support'); - -/** - * Global connection model for this test. - */ -const CONNECTION = new Connection({ hostname: '127.0.0.1', port: 27018, ns: 'music' }); - -describe.skip('#crud #race', function() { - this.slow(30000); - this.timeout(60000); - let app = null; - let client = null; - - before(function() { - return launchCompass() - .then(function(application) { - app = application; - client = application.client; - return client.connectToCompass({ hostname: 'localhost', port: 27018 }); - }); - }); - - after(function() { - return quitCompass(app); - }); - - context('when manipulating documents in the crud view', function() { - const dataService = new DataService(CONNECTION); - - before(function(done) { - dataService.connect(function() { - dataService.createCollection('music.artists', {}, function() { - return client - .goToCollection('music', 'artists').then(function() { - done(); - }); - }); - }); - }); - - after(function(done) { - dataService.dropDatabase('music', function() { - dataService.disconnect(); - done(); - }); - }); - - context('when inserting a document', function() { - context('when the document is valid', function() { - it('creates the document #race', function() { - return client - .clickDocumentsTab() - .clickInsertDocumentButton() - .waitForInsertDocumentModal() - .inputNewDocumentDetails({ - 'name': 'Aphex Twin', - 'genre': 'Electronic', - 'location': 'London' - }) - .clickInsertDocumentModalButton() - .waitForDocumentInsert(1) - .getDocumentValues(1) - .should.eventually.include('\"Aphex Twin\"'); - }); - }); - - context('when pressing escape key twice', function() { - it('does not close the insert documents modal on first press', function() { - return client - .clickInsertDocumentButton() - .waitForInsertDocumentModal() - .pressEscape() - .waitForInsertDocumentModal() - .should.eventually.be.true; - }); - it('closes the insert documents modal on second press', function() { - return client - .pressEscape() - .waitForInsertDocumentModalHidden() - .should.eventually.be.true; - }); - }); - }); - - context('when editing a document', function() { - it.skip('saves the changes to the document #race', function() { - return client - .clickEditDocumentButton(1) - .inputDocumentValueChange(1, 'Aphex Twin', 'Aphex Twin (edited)') - .clickUpdateDocumentButton(1) - .waitForDocumentUpdate(1) - .getDocumentValues(1) - .should.eventually.include('\"Aphex Twin (edited)\"'); - }); - }); - - context('when cloning a document', function() { - it('creates the cloned document', function() { - return client - .clickCloneDocumentButton(1) - .waitForInsertDocumentModal() - .inputClonedDocumentValueChange(1, 'London', 'Essex') - .clickInsertDocumentModalButton() - .waitForDocumentInsert(2) - .getDocumentValues(2) - .should.eventually.include('\"Essex\"'); - }); - }); - - context('when double clicking a field', function() { - it('opens document edit dialog and focuses cursor on the field', function() { - return client - .doubleClickDocumentField(2, 2) - .inputDocumentFieldChange(2, 'genre', 'category') - .clickUpdateDocumentButton(2) - .waitForDocumentUpdate(2) - .getDocumentFields(2) - .should.eventually.include('category'); - }); - - it('opens document edit dialog and focuses cursor on the value', function() { - return client - .doubleClickDocumentValue(2, 2) - .inputDocumentValueChange(2, 'Electronic', 'ska') - .clickUpdateDocumentButton(2) - .waitForDocumentUpdate(2) - .getDocumentValues(2) - .should.eventually.include('\"ska\"'); - }); - }); - - context('when deleting a document', function() { - it('deletes upon confirmation #race', function() { - return client - .clickDeleteDocumentButton(2) - .clickConfirmDeleteDocumentButton(2) - .waitForDocumentDeletionToComplete(2) - .getSamplingMessageFromDocumentsTab() - .should.eventually.include('Query returned 1 document.'); - }); - }); - - context('when applying a filter', function() { - const filter = '{"name":"Bonobo"}'; - it('updates the document list', function() { - return client - .inputFilterFromDocumentsTab(filter) - .clickApplyFilterButtonFromDocumentsTab() - .getSamplingMessageFromDocumentsTab() - .should.eventually.include('Query returned 0 documents.'); - }); - }); - }); -}); diff --git a/test/functional/support/packages/spectron-crud.js b/test/functional/support/packages/spectron-crud.js deleted file mode 100644 index d331e2de970..00000000000 --- a/test/functional/support/packages/spectron-crud.js +++ /dev/null @@ -1,362 +0,0 @@ -const _ = require('lodash'); -const { selector } = require('hadron-spectron'); - - -function addWaitCRUDCommands(client) { - /** - * Wait for the insert document modal to open. - */ - client.addCommand('waitForInsertDocumentModal', function() { - return this.waitForVisibleInCompass(selector('insert-document-modal')); - }); - client.addCommand('waitForInsertDocumentModalHidden', function() { - return this.waitForVisibleInCompass(selector('insert-document-modal'), true); - }); - - /** - * Wait for a document to be inserted at the index. - * - * @param {Number} index - The document index. - */ - client.addCommand('waitForDocumentInsert', function(index) { - const base = selector('document-list-item'); - return this.waitForExistInCompass(`${base}:nth-child(${index})`); - }); - - /** - * Wait for the edit document to complete. - * - * @param {Number} index - The index of the document in the list. - */ - client.addCommand('waitForDocumentUpdate', function(index) { - const base = selector('document-list-item'); - const message = `${base}:nth-child(${index}) ${selector('document-message')}`; - return this.waitForExistInCompass(message, true); - }); - - /** - * Wait for document deletion to finish. - * - * @param {Number} index - The index of the document being deleted. - */ - client.addCommand('waitForDocumentDeletionToComplete', function(index) { - const base = `${selector('document-list-item')}:nth-child(${index})`; - return this.waitForExistInCompass(base, true); - }); -} - - -function addClickCRUDCommands(client) { - /** - * Click on the documents tab. - */ - client.addCommand('clickDocumentsTab', function() { - return this.waitForStatusBar().click(selector('documents-tab')); - }); - - /** - * Click the apply filter button from the documents tab. - */ - client.addCommand('clickApplyFilterButtonFromDocumentsTab', function() { - const base = selector('documents-content'); - const button = `${base} ${selector('apply-filter-button')}`; - return this.waitForVisibleInCompass(button).click(button); - }); - - /** - * Click the reset filter button from the documents tab. - */ - client.addCommand('clickResetFilterButtonFromDocumentsTab', function() { - const base = selector('documents-content'); - const button = `${base} ${selector('reset-filter-button')}`; - return this.waitForVisibleInCompass(button).click(button); - }); - - /** - * Click the refresh documents button. - */ - client.addCommand('clickRefreshDocumentsButton', function() { - const button = selector('refresh-documents-button'); - return this.waitForVisibleInCompass(button).click(button); - }); - - /** - * Click the insert document button. - */ - client.addCommand('clickInsertDocumentButton', function() { - return this.click(selector('open-insert-document-modal-button')); - }); - - /** - * Click the edit document button. - * - * @param {Number} index - The index of the document, starting at 1. - */ - client.addCommand('clickEditDocumentButton', function(index) { - const base = `${selector('document-list-item')}:nth-child(${index})`; - const button = `${base} ${selector('edit-document-button')}`; - return this.moveToObject(base).waitForVisibleInCompass(button).click(button); - }); - - /** - * Double click the document field at docIndex and at field fieldIndex - */ - client.addCommand('doubleClickDocumentField', function(docIndex, fieldIndex) { - const base = `${selector('document-list-item')}:nth-child(${docIndex})`; - const field = `${base} .editable-element:nth-child(${fieldIndex}) .editable-element-field`; - return this.moveToObject(base).waitForVisibleInCompass(field).doubleClick(field); - }); - - /** - * Double click the document value at docIndex and at value fieldIndex - */ - client.addCommand('doubleClickDocumentValue', function(docIndex, fieldIndex) { - const base = `${selector('document-list-item')}:nth-child(${docIndex})`; - const value = `${base} .editable-element:nth-child(${fieldIndex}) .element-value`; - return this.moveToObject(base).waitForVisibleInCompass(value).doubleClick(value); - }); - - /** - * Click the update document button. - * - * @param {Number} index - The index of the document, starting at 1. - */ - client.addCommand('clickUpdateDocumentButton', function(index) { - const base = `${selector('document-list-item')}:nth-child(${index})`; - const button = `${base} ${selector('update-document-button')}`; - return this.click(button); - }); - - /** - * Click the clone document button. - * - * @param {Number} index - The index of the document, starting at 1. - */ - client.addCommand('clickCloneDocumentButton', function(index) { - const base = `${selector('document-list-item')}:nth-child(${index})`; - const button = `${base} ${selector('clone-document-button')}`; - return this.moveToObject(base).waitForVisibleInCompass(button).click(button); - }); - - /** - * Click the delete document button. - * - * @param {Number} index - The index of the document, starting at 1. - */ - client.addCommand('clickDeleteDocumentButton', function(index) { - const base = `${selector('document-list-item')}:nth-child(${index})`; - const button = `${base} ${selector('delete-document-button')}`; - return this.moveToObject(base).waitForVisibleInCompass(button).click(button); - }); - - /** - * Click the insert button in the insert document modal. - */ - client.addCommand('clickInsertDocumentModalButton', function() { - const base = selector('insert-document-button'); - return this.click(base).waitForVisibleInCompass(base, true); - }); - - /** - * Click the confirm delete document button. - * - * @param {Number} index - The index of the document, starting at 1. - */ - client.addCommand('clickConfirmDeleteDocumentButton', function(index) { - const base = `${selector('document-list-item')}:nth-child(${index})`; - const button = `${base} ${selector('confirm-delete-document-button')}`; - return this.click(button); - }); -} - - -function addGetCRUDCommands(client) { - /** - * Get the sampling message on the documents tab. - */ - client.addCommand('getSamplingMessageFromDocumentsTab', function() { - const base = selector('documents-content'); - const div = `${base} .sampling-message`; - return this.waitForVisibleInCompass(div).getText(div); - }); - - /** - * Get the document updated message. - */ - client.addCommand('getDocumentMessage', function() { - return this.getText(selector('document-message')); - }); - - /** - * Get the document at the provided index in the list - * - * @param {Number} index - The index in the list, starting at 1. - */ - client.addCommand('getDocumentAtIndex', function(index) { - const base = selector('document-list-item'); - return this.getText(`${base}:nth-child(${index}) .editable-element-field, ${base}:nth-child(${index}) .element-field`).then((keys) => { - return this.getText(`${base}:nth-child(${index}) .element-value`).then((values) => { - return _.zipObject(keys, values); - }); - }); - }); - - /** - * Get the read only status of the document at the provided index in the list - * @type {Number} index - the index in the list, starting at 1. - */ - client.addCommand('getDocumentReadonlyStatus', function(index) { - const base = `${selector('document-list-item')} ${selector('readonly-document')}`; - return this.isExisting(`${base}:nth-child(${index})`); - }); - - client.addCommand('getDocumentFields', function(index) { - const base = selector('document-list-item'); - return this.getText(`${base}:nth-child(${index}) .editable-element-field`); - }); - /** - * Get the values of a document at the provided index in the list. - * - * @param {Number} index - The index in the list, starting at 1. - */ - client.addCommand('getDocumentValues', function(index) { - const base = selector('document-list-item'); - return this.getText(`${base}:nth-child(${index}) .element-value`); - }); -} - - -function addInputCRUDCommands(client) { - /** - * Inputs a filter into the collection level query bar from the documents tab. - * - * @param {String} filter - The filter. - */ - client.addCommand('inputFilterFromDocumentsTab', function(filter) { - const base = selector('documents-content'); - const input = `${base} .input-filter`; - return this.waitForVisibleInCompass(input).click(input).keys(filter); - }); - - /** - * Inputs a projection into the query bar from the documents tab. - * - * @param {String} filter - The filter. - */ - client.addCommand('inputProjectFromDocumentsTab', function(projection) { - const base = selector('documents-content'); - const input = `${base} .input-project`; - return this.waitForVisibleInCompass(input).click(input).keys(projection); - }); - - /** - * Inputs a sort into the query bar from the documents tab. - * - * @param {String} filter - The filter. - */ - client.addCommand('inputSortFromDocumentsTab', function(sort) { - const base = selector('documents-content'); - const input = `${base} .input-sort`; - return this.waitForVisibleInCompass(input).click(input).keys(sort); - }); - - /** - * Inputs a skip into the query bar from the documents tab. - * - * @param {String} filter - The filter. - */ - client.addCommand('inputSkipFromDocumentsTab', function(filter) { - const base = selector('documents-content'); - const input = `${base} .input-skip`; - return this.setValue(input, filter); - }); - - /** - * Inputs a limit into the query bar from the documents tab. - * - * @param {String} filter - The filter. - */ - client.addCommand('inputLimitFromDocumentsTab', function(filter) { - const base = selector('documents-content'); - const input = `${base} .input-limit`; - return this.setValue(input, filter); - }); - - /** - * Input a change to a document value. - * - * @param {Number} index - The index of the document in the list. - * @param {Object} oldValue - The old value. - * @param {Object} newValue - The new value. - */ - client.addCommand('inputDocumentFieldChange', function(index, oldValue, newValue) { - const base = `${selector('document-list-item')}:nth-child(${index})`; - return this.setValue(`${base} input.editable-element-field[value='${oldValue}']`, newValue); - }); - - /** - * Input a change to a document value. - * - * @param {Number} index - The index of the document in the list. - * @param {Object} oldValue - The old value. - * @param {Object} newValue - The new value. - */ - client.addCommand('inputDocumentValueChange', function(index, oldValue, newValue) { - const base = `${selector('document-list-item')}:nth-child(${index})`; - return this.setValue(`${base} input.editable-element-value[value='${oldValue}']`, newValue); - }); - - /** - * Input a change to a cloned document value. - * - * @param {Number} index - The index of the document in the list. - * @param {Object} oldValue - The old value. - * @param {Object) newValue - The new value. - */ - client.addCommand('inputClonedDocumentValueChange', function(index, oldValue, newValue) { - const base = selector('insert-document-modal'); - return this.setValue(`${base} input.editable-element-value[value='${oldValue}']`, newValue); - }); - - /** - * Insert a new document into the collection via the insert modal. - * - * @param {Object} model - The document to insert. - */ - client.addCommand('inputNewDocumentDetails', function(model) { - const base = selector('insert-document-modal'); - const that = this; - const lineNumber = `${base} .document-elements .editable-element:last-child div.line-number`; - const addField = `${lineNumber} ${selector('add-field-after')}`; - let sequence = Promise.resolve(); - - _.each(model, function(value, key) { - sequence = sequence.then(function() { - return that - .setValue(`${base} input.editable-element-field[value='']`, key) - .setValue(`${base} input.editable-element-value[value='']`, value) - .moveToObject(lineNumber) - .click(lineNumber) - .waitForVisibleInCompass(addField) - .click(addField); - }); - }); - return sequence; - }); -} - - -/** - * Add commands to the client related to the Documents Tab. - * - * @param {Client} client - The client. - */ -function addCRUDCommands(client) { - addWaitCRUDCommands(client); - addClickCRUDCommands(client); - addGetCRUDCommands(client); - addInputCRUDCommands(client); -} - - -module.exports = addCRUDCommands; From 8bfa6588f164c7749acd54532e322395fd11bf46 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Mon, 7 Aug 2017 17:16:33 -0400 Subject: [PATCH 5/9] Remove editable document crud test --- .../crud.editable-document-component.test.js | 70 ------------------- 1 file changed, 70 deletions(-) delete mode 100644 test/enzyme/crud.editable-document-component.test.js diff --git a/test/enzyme/crud.editable-document-component.test.js b/test/enzyme/crud.editable-document-component.test.js deleted file mode 100644 index e345308b715..00000000000 --- a/test/enzyme/crud.editable-document-component.test.js +++ /dev/null @@ -1,70 +0,0 @@ -/* eslint no-unused-vars: 0, no-unused-expressions: 0 */ -const app = require('hadron-app'); -const chai = require('chai'); -const chaiEnzyme = require('chai-enzyme'); -const expect = chai.expect; -const React = require('react'); -const { mount } = require('enzyme'); -const AppRegistry = require('hadron-app-registry'); - -const EditableDocument = require('../../src/internal-packages/crud/lib/component/editable-document'); -const EditableElement = require('../../src/internal-packages/crud/lib/component/editable-element'); - -// use chai-enzyme assertions, see https://github.com/producthunt/chai-enzyme -chai.use(chaiEnzyme()); - -describe('', function() { - let appRegistry = app.appRegistry; - let appInstance = app.instance; - beforeEach(() => { - // Mock the AppRegistry with a new one so tests don't complain about - // appRegistry.getComponent (i.e. appRegistry being undefined) - app.appRegistry = new AppRegistry(); - app.instance = {build: {version: '3.2.0'}}; - }); - afterEach(() => { - // Restore properties on the global app, so they don't affect other tests - app.appRegistry = appRegistry; - app.instance = appInstance; - }); - - const doc = {a: {b: {c: {d: 1}}}}; - it('if expandAll is true, renders children', () => { - const component = mount(); - component.setState({ - expandAll: true - }); - const children = component.find(EditableElement); - expect(children).to.be.of.length(4); - }); - context('COMPASS_1306, if expandAll is false', () => { - it('if expandAll is false, does not render children', () => { - const component = mount( - - ); - component.setState({ - expandAll: false - }); - const children = component.find(EditableElement); - expect(children).to.be.of.length(1); - }); - it('if expandAll is false but expand true, renders another child', () => { - // Work around https://github.com/airbnb/enzyme/issues/361 - // by mounting the EditableElement rather than the EditableDocument, - // note this requires the HadronDocument constructor - const document = EditableDocument.loadDocument(doc); - for (const element of document.elements) { - const component = mount( - - ); - let children = component.find(EditableElement); - expect(children).to.be.of.length(1); - component.setState({ - expanded: true - }); - children = component.find(EditableElement); - expect(children).to.be.of.length(2); - } - }); - }); -}); From 36df14d37af11edc27b0136809220c546cde5eb3 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Mon, 7 Aug 2017 17:46:01 -0400 Subject: [PATCH 6/9] Remove crud store renderer test --- ....open-insert-document-dialog-store.test.js | 59 ------------------- 1 file changed, 59 deletions(-) delete mode 100644 test/renderer/crud.open-insert-document-dialog-store.test.js diff --git a/test/renderer/crud.open-insert-document-dialog-store.test.js b/test/renderer/crud.open-insert-document-dialog-store.test.js deleted file mode 100644 index 68f28a22a36..00000000000 --- a/test/renderer/crud.open-insert-document-dialog-store.test.js +++ /dev/null @@ -1,59 +0,0 @@ -/* eslint no-unused-expressions: 0 */ - -const expect = require('chai').expect; - -// const debug = require('debug')('mongodb-compass:test:query-changed-store'); - -let OpenInsertDocumentDialogStore = require('../../src/internal-packages/crud/lib/store/open-insert-document-dialog-store'); - -describe('OpenInsertDocumentDialogStore', () => { - let unsubscribe = () => {}; - - afterEach(() => { - unsubscribe(); - unsubscribe = () => {}; - }); - - context('when inserting a new document', () => { - it('keeps the document as is without modifications', (done) => { - const doc = { - _id: 'foo', - field: 'bar' - }; - unsubscribe = OpenInsertDocumentDialogStore.listen((hadronDoc) => { - expect(hadronDoc.generateObject()).to.be.deep.equal(doc); - done(); - }); - OpenInsertDocumentDialogStore.openInsertDocumentDialog(doc, false); - }); - }); - - context('when cloning a document', () => { - it('removes the _id element when it is at the first position', (done) => { - const doc = { - _id: 'foo', - field: 'bar' - }; - unsubscribe = OpenInsertDocumentDialogStore.listen((hadronDoc) => { - expect(hadronDoc.generateObject()).to.be.deep.equal({field: 'bar'}); - done(); - }); - OpenInsertDocumentDialogStore.openInsertDocumentDialog(doc, true); - }); - it('removes the _id element when it is not at the first position', (done) => { - const doc = { - _a_surprise_: 'indeed', - _id: 'foo', - field: 'bar' - }; - unsubscribe = OpenInsertDocumentDialogStore.listen((hadronDoc) => { - expect(hadronDoc.generateObject()).to.be.deep.equal({ - _a_surprise_: 'indeed', - field: 'bar' - }); - done(); - }); - OpenInsertDocumentDialogStore.openInsertDocumentDialog(doc, true); - }); - }); -}); From 9f1cd9a6973e45c8ca391503d30defb66828736d Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Mon, 7 Aug 2017 23:04:27 -0400 Subject: [PATCH 7/9] Remove crud commands --- test/functional/support/spectron-support.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/functional/support/spectron-support.js b/test/functional/support/spectron-support.js index 0c7fdaf0014..0c264a07175 100644 --- a/test/functional/support/spectron-support.js +++ b/test/functional/support/spectron-support.js @@ -4,7 +4,6 @@ const addChartsCommands = require('./packages/spectron-charts'); const addCollectionCommands = require('./packages/spectron-collection'); const addCollectionDDLCommands = require('./packages/spectron-collection-ddl'); const addConnectCommands = require('./packages/spectron-connect'); -const addCRUDCommands = require('./packages/spectron-crud'); const addDatabaseCommands = require('./packages/spectron-database'); const addDatabaseDDLCommands = require('./packages/spectron-database-ddl'); const addExplainCommands = require('./packages/spectron-explain'); @@ -33,7 +32,6 @@ function addCommands(client) { addCollectionCommands(client); addCollectionDDLCommands(client); addConnectCommands(client); - addCRUDCommands(client); addDatabaseCommands(client); addDatabaseDDLCommands(client); addExplainCommands(client); From b8f6fca8107115537ffffa2f12f1c3a436d52678 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 8 Aug 2017 09:04:49 -0400 Subject: [PATCH 8/9] Bring back documents tab spectron commands --- .../support/packages/spectron-crud.js | 360 ++++++++++++++++++ test/functional/support/spectron-support.js | 2 + 2 files changed, 362 insertions(+) create mode 100644 test/functional/support/packages/spectron-crud.js diff --git a/test/functional/support/packages/spectron-crud.js b/test/functional/support/packages/spectron-crud.js new file mode 100644 index 00000000000..9170cd3165f --- /dev/null +++ b/test/functional/support/packages/spectron-crud.js @@ -0,0 +1,360 @@ +const _ = require('lodash'); +const { selector } = require('hadron-spectron'); + +function addWaitCRUDCommands(client) { + /** + * Wait for the insert document modal to open. + */ + client.addCommand('waitForInsertDocumentModal', function() { + return this.waitForVisibleInCompass(selector('insert-document-modal')); + }); + client.addCommand('waitForInsertDocumentModalHidden', function() { + return this.waitForVisibleInCompass(selector('insert-document-modal'), true); + }); + + /** + * Wait for a document to be inserted at the index. + * + * @param {Number} index - The document index. + */ + client.addCommand('waitForDocumentInsert', function(index) { + const base = selector('document-list-item'); + return this.waitForExistInCompass(`${base}:nth-child(${index})`); + }); + + /** + * Wait for the edit document to complete. + * + * @param {Number} index - The index of the document in the list. + */ + client.addCommand('waitForDocumentUpdate', function(index) { + const base = selector('document-list-item'); + const message = `${base}:nth-child(${index}) ${selector('document-message')}`; + return this.waitForExistInCompass(message, true); + }); + + /** + * Wait for document deletion to finish. + * + * @param {Number} index - The index of the document being deleted. + */ + client.addCommand('waitForDocumentDeletionToComplete', function(index) { + const base = `${selector('document-list-item')}:nth-child(${index})`; + return this.waitForExistInCompass(base, true); + }); +} + + +function addClickCRUDCommands(client) { + /** + * Click on the documents tab. + */ + client.addCommand('clickDocumentsTab', function() { + return this.waitForStatusBar().click(selector('documents-tab')); + }); + + /** + * Click the apply filter button from the documents tab. + */ + client.addCommand('clickApplyFilterButtonFromDocumentsTab', function() { + const base = selector('documents-content'); + const button = `${base} ${selector('apply-filter-button')}`; + return this.waitForVisibleInCompass(button).click(button); + }); + + /** + * Click the reset filter button from the documents tab. + */ + client.addCommand('clickResetFilterButtonFromDocumentsTab', function() { + const base = selector('documents-content'); + const button = `${base} ${selector('reset-filter-button')}`; + return this.waitForVisibleInCompass(button).click(button); + }); + + /** + * Click the refresh documents button. + */ + client.addCommand('clickRefreshDocumentsButton', function() { + const button = selector('refresh-documents-button'); + return this.waitForVisibleInCompass(button).click(button); + }); + + /** + * Click the insert document button. + */ + client.addCommand('clickInsertDocumentButton', function() { + return this.click(selector('open-insert-document-modal-button')); + }); + + /** + * Click the edit document button. + * + * @param {Number} index - The index of the document, starting at 1. + */ + client.addCommand('clickEditDocumentButton', function(index) { + const base = `${selector('document-list-item')}:nth-child(${index})`; + const button = `${base} ${selector('edit-document-button')}`; + return this.moveToObject(base).waitForVisibleInCompass(button).click(button); + }); + + /** + * Double click the document field at docIndex and at field fieldIndex + */ + client.addCommand('doubleClickDocumentField', function(docIndex, fieldIndex) { + const base = `${selector('document-list-item')}:nth-child(${docIndex})`; + const field = `${base} .editable-element:nth-child(${fieldIndex}) .editable-element-field`; + return this.moveToObject(base).waitForVisibleInCompass(field).doubleClick(field); + }); + + /** + * Double click the document value at docIndex and at value fieldIndex + */ + client.addCommand('doubleClickDocumentValue', function(docIndex, fieldIndex) { + const base = `${selector('document-list-item')}:nth-child(${docIndex})`; + const value = `${base} .editable-element:nth-child(${fieldIndex}) .element-value`; + return this.moveToObject(base).waitForVisibleInCompass(value).doubleClick(value); + }); + + /** + * Click the update document button. + * + * @param {Number} index - The index of the document, starting at 1. + */ + client.addCommand('clickUpdateDocumentButton', function(index) { + const base = `${selector('document-list-item')}:nth-child(${index})`; + const button = `${base} ${selector('update-document-button')}`; + return this.click(button); + }); + + /** + * Click the clone document button. + * + * @param {Number} index - The index of the document, starting at 1. + */ + client.addCommand('clickCloneDocumentButton', function(index) { + const base = `${selector('document-list-item')}:nth-child(${index})`; + const button = `${base} ${selector('clone-document-button')}`; + return this.moveToObject(base).waitForVisibleInCompass(button).click(button); + }); + + /** + * Click the delete document button. + * + * @param {Number} index - The index of the document, starting at 1. + */ + client.addCommand('clickDeleteDocumentButton', function(index) { + const base = `${selector('document-list-item')}:nth-child(${index})`; + const button = `${base} ${selector('delete-document-button')}`; + return this.moveToObject(base).waitForVisibleInCompass(button).click(button); + }); + + /** + * Click the insert button in the insert document modal. + */ + client.addCommand('clickInsertDocumentModalButton', function() { + const base = selector('insert-document-button'); + return this.click(base).waitForVisibleInCompass(base, true); + }); + + /** + * Click the confirm delete document button. + * + * @param {Number} index - The index of the document, starting at 1. + */ + client.addCommand('clickConfirmDeleteDocumentButton', function(index) { + const base = `${selector('document-list-item')}:nth-child(${index})`; + const button = `${base} ${selector('confirm-delete-document-button')}`; + return this.click(button); + }); +} + + +function addGetCRUDCommands(client) { + /** + * Get the sampling message on the documents tab. + */ + client.addCommand('getSamplingMessageFromDocumentsTab', function() { + const base = selector('documents-content'); + const div = `${base} .sampling-message`; + return this.waitForVisibleInCompass(div).getText(div); + }); + + /** + * Get the document updated message. + */ + client.addCommand('getDocumentMessage', function() { + return this.getText(selector('document-message')); + }); + + /** + * Get the document at the provided index in the list + * + * @param {Number} index - The index in the list, starting at 1. + */ + client.addCommand('getDocumentAtIndex', function(index) { + const base = selector('document-list-item'); + return this.getText(`${base}:nth-child(${index}) .editable-element-field, ${base}:nth-child(${index}) .element-field`).then((keys) => { + return this.getText(`${base}:nth-child(${index}) .element-value`).then((values) => { + return _.zipObject(keys, values); + }); + }); + }); + + /** + * Get the read only status of the document at the provided index in the list + * @type {Number} index - the index in the list, starting at 1. + */ + client.addCommand('getDocumentReadonlyStatus', function(index) { + const base = `${selector('document-list-item')} ${selector('readonly-document')}`; + return this.isExisting(`${base}:nth-child(${index})`); + }); + + client.addCommand('getDocumentFields', function(index) { + const base = selector('document-list-item'); + return this.getText(`${base}:nth-child(${index}) .editable-element-field`); + }); + /** + * Get the values of a document at the provided index in the list. + * + * @param {Number} index - The index in the list, starting at 1. + */ + client.addCommand('getDocumentValues', function(index) { + const base = selector('document-list-item'); + return this.getText(`${base}:nth-child(${index}) .element-value`); + }); +} + + +function addInputCRUDCommands(client) { + /** + * Inputs a filter into the collection level query bar from the documents tab. + * + * @param {String} filter - The filter. + */ + client.addCommand('inputFilterFromDocumentsTab', function(filter) { + const base = selector('documents-content'); + const input = `${base} .input-filter`; + return this.waitForVisibleInCompass(input).click(input).keys(filter); + }); + + /** + * Inputs a projection into the query bar from the documents tab. + * + * @param {String} filter - The filter. + */ + client.addCommand('inputProjectFromDocumentsTab', function(projection) { + const base = selector('documents-content'); + const input = `${base} .input-project`; + return this.waitForVisibleInCompass(input).click(input).keys(projection); + }); + + /** + * Inputs a sort into the query bar from the documents tab. + * + * @param {String} filter - The filter. + */ + client.addCommand('inputSortFromDocumentsTab', function(sort) { + const base = selector('documents-content'); + const input = `${base} .input-sort`; + return this.waitForVisibleInCompass(input).click(input).keys(sort); + }); + + /** + * Inputs a skip into the query bar from the documents tab. + * + * @param {String} filter - The filter. + */ + client.addCommand('inputSkipFromDocumentsTab', function(filter) { + const base = selector('documents-content'); + const input = `${base} .input-skip`; + return this.setValue(input, filter); + }); + + /** + * Inputs a limit into the query bar from the documents tab. + * + * @param {String} filter - The filter. + */ + client.addCommand('inputLimitFromDocumentsTab', function(filter) { + const base = selector('documents-content'); + const input = `${base} .input-limit`; + return this.setValue(input, filter); + }); + + /** + * Input a change to a document value. + * + * @param {Number} index - The index of the document in the list. + * @param {Object} oldValue - The old value. + * @param {Object} newValue - The new value. + */ + client.addCommand('inputDocumentFieldChange', function(index, oldValue, newValue) { + const base = `${selector('document-list-item')}:nth-child(${index})`; + return this.setValue(`${base} input.editable-element-field[value='${oldValue}']`, newValue); + }); + + /** + * Input a change to a document value. + * + * @param {Number} index - The index of the document in the list. + * @param {Object} oldValue - The old value. + * @param {Object} newValue - The new value. + */ + client.addCommand('inputDocumentValueChange', function(index, oldValue, newValue) { + const base = `${selector('document-list-item')}:nth-child(${index})`; + return this.setValue(`${base} input.editable-element-value[value='${oldValue}']`, newValue); + }); + + /** + * Input a change to a cloned document value. + * + * @param {Number} index - The index of the document in the list. + * @param {Object} oldValue - The old value. + * @param {Object) newValue - The new value. + */ + client.addCommand('inputClonedDocumentValueChange', function(index, oldValue, newValue) { + const base = selector('insert-document-modal'); + return this.setValue(`${base} input.editable-element-value[value='${oldValue}']`, newValue); + }); + + /** + * Insert a new document into the collection via the insert modal. + * + * @param {Object} model - The document to insert. + */ + client.addCommand('inputNewDocumentDetails', function(model) { + const base = selector('insert-document-modal'); + const that = this; + const lineNumber = `${base} .document-elements .editable-element:last-child div.line-number`; + const addField = `${lineNumber} ${selector('add-field-after')}`; + let sequence = Promise.resolve(); + + _.each(model, function(value, key) { + sequence = sequence.then(function() { + return that + .setValue(`${base} input.editable-element-field[value='']`, key) + .setValue(`${base} input.editable-element-value[value='']`, value) + .moveToObject(lineNumber) + .click(lineNumber) + .waitForVisibleInCompass(addField) + .click(addField); + }); + }); + return sequence; + }); +} + + +/** + * Add commands to the client related to the Documents Tab. + * + * @param {Client} client - The client. + */ +function addCRUDCommands(client) { + addWaitCRUDCommands(client); + addClickCRUDCommands(client); + addGetCRUDCommands(client); + addInputCRUDCommands(client); +} + +module.exports = addCRUDCommands; diff --git a/test/functional/support/spectron-support.js b/test/functional/support/spectron-support.js index 0c264a07175..0c7fdaf0014 100644 --- a/test/functional/support/spectron-support.js +++ b/test/functional/support/spectron-support.js @@ -4,6 +4,7 @@ const addChartsCommands = require('./packages/spectron-charts'); const addCollectionCommands = require('./packages/spectron-collection'); const addCollectionDDLCommands = require('./packages/spectron-collection-ddl'); const addConnectCommands = require('./packages/spectron-connect'); +const addCRUDCommands = require('./packages/spectron-crud'); const addDatabaseCommands = require('./packages/spectron-database'); const addDatabaseDDLCommands = require('./packages/spectron-database-ddl'); const addExplainCommands = require('./packages/spectron-explain'); @@ -32,6 +33,7 @@ function addCommands(client) { addCollectionCommands(client); addCollectionDDLCommands(client); addConnectCommands(client); + addCRUDCommands(client); addDatabaseCommands(client); addDatabaseDDLCommands(client); addExplainCommands(client); From 259349a904324ddcfebee7c7faff86680c185c8b Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 8 Aug 2017 09:46:08 -0400 Subject: [PATCH 9/9] COMPASS-1677: Fixing key/value focus on tab --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b3b37ac9b5d..3b84286189f 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,7 @@ }, "dependencies": { "@mongodb-js/compass-charts": "^0.2.3", - "@mongodb-js/compass-crud": "0.0.2", + "@mongodb-js/compass-crud": "0.0.3", "@mongodb-js/compass-deployment-awareness": "3.2.0", "@mongodb-js/compass-document-validation": "4.1.0", "@mongodb-js/compass-serverstats": "9.1.1",