From 74facd87282b82be537618acc98346942511a11c Mon Sep 17 00:00:00 2001 From: Anemy Date: Thu, 14 Jul 2022 11:09:51 -0400 Subject: [PATCH 01/15] open query history plugin as popover --- .../compass-collection/src/stores/context.tsx | 99 ++++++++++++--- packages/compass-components/package.json | 1 + .../src/components/leafygreen.tsx | 2 + .../src/components/query-bar/query-bar.tsx | 114 +++++++++++++++++- packages/compass-query-bar/src/plugin.jsx | 1 + .../query-history/query-history.jsx | 15 +-- .../query-history/query-history.module.less | 17 +-- .../src/components/toolbar/toolbar.tsx | 30 +++-- packages/compass-query-history/src/index.ts | 5 +- .../src/stores/query-history-store.js | 27 ----- 10 files changed, 229 insertions(+), 82 deletions(-) diff --git a/packages/compass-collection/src/stores/context.tsx b/packages/compass-collection/src/stores/context.tsx index f0cb8c58fee..89007f4076d 100644 --- a/packages/compass-collection/src/stores/context.tsx +++ b/packages/compass-collection/src/stores/context.tsx @@ -79,6 +79,11 @@ type ContextProps = { connectionString?: string; }; +type ContextWithAppRegistry = ContextProps & { + globalAppRegistry: AppRegistry; + localAppRegistry: AppRegistry; +} + /** * Setup a scoped store to the collection. * @@ -115,7 +120,7 @@ const setupStore = ({ query, aggregation, connectionString, -}: ContextProps) => { +}: ContextWithAppRegistry) => { const store = role.configureStore({ localAppRegistry, globalAppRegistry, @@ -138,7 +143,7 @@ const setupStore = ({ aggregation, connectionString, }); - localAppRegistry?.registerStore(role.storeName, store); + localAppRegistry.registerStore(role.storeName, store); return store; }; @@ -176,7 +181,7 @@ const setupPlugin = ({ sourceName, connectionString, key, -}: ContextProps) => { +}: ContextWithAppRegistry) => { const actions = role.configureActions(); const store = setupStore({ role, @@ -232,7 +237,7 @@ const setupScopedModals = ({ isFLE, sourceName, connectionString, -}: ContextProps) => { +}: ContextWithAppRegistry) => { const roles = globalAppRegistry?.getRole('Collection.ScopedModal'); if (roles) { return roles.map((role: any, i: number) => { @@ -257,6 +262,77 @@ const setupScopedModals = ({ return []; }; +/** + * Setup the query bar plugins. Need to instantiate the store and actions + * and put them in the app registry for use by all the plugins. This way + * there is only 1 query bar store per collection tab instead of one per + * plugin that uses it. + * + * @param {Object} options - The scope modal plugin options. + * @property {Object} options.globalAppRegistry - The global app registry. + * @property {Object} options.localAppRegistry - The scoped app registry to the collection. + * @property {Object} options.dataService - The data service. + * @property {String} options.namespace - The namespace. + * @property {String} options.serverVersion - The server version. + * @property {Boolean} options.isReadonly - If the collection is a readonly view. + * @property {Boolean} options.isTimeSeries - If the collection is a time-series. + * @property {Boolean} options.isClustered - If the collection is a time-series. + * @property {Boolean} options.isFLE - If the collection is a FLE collection. + * + * @returns {Array} The components. + */ +const setupQueryPlugins = ({ + globalAppRegistry, + localAppRegistry, + serverVersion, + state, + namespace, + isReadonly, + isTimeSeries, + isClustered, + isFLE, + query, + aggregation, +}: ContextWithAppRegistry) => { + const queryBarRole = globalAppRegistry.getRole('Query.QueryBar')![0]; + localAppRegistry.registerRole('Query.QueryBar', queryBarRole); + const queryBarActions = setupActions(queryBarRole, localAppRegistry); + setupStore({ + role: queryBarRole, + globalAppRegistry, + localAppRegistry, + dataService: state.dataService, + namespace, + serverVersion, + isReadonly, + isTimeSeries, + isClustered, + isFLE, + actions: queryBarActions, + query, + aggregation, + }); + + const queryHistoryRole = globalAppRegistry.getRole('Query.QueryHistory')![0]; + localAppRegistry.registerRole('Query.QueryHistory', queryHistoryRole); + const queryHistoryActions = setupActions(queryHistoryRole, localAppRegistry); + setupStore({ + role: queryHistoryRole, + globalAppRegistry, + localAppRegistry, + dataService: state.dataService, + namespace, + serverVersion, + isReadonly, + isTimeSeries, + isClustered, + isFLE, + actions: queryHistoryActions, + query, + aggregation, + }); +} + /** * Create the context in which a tab is created. * @@ -306,25 +382,16 @@ const createContext = ({ const views: JSX.Element[] = []; const queryHistoryIndexes: number[] = []; - // Setup the query bar plugin. Need to instantiate the store and actions - // and put them in the app registry for use by all the plugins. This way - // there is only 1 query bar store per collection tab instead of one per - // plugin that uses it. - const queryBarRole = globalAppRegistry.getRole('Query.QueryBar')[0]; - localAppRegistry.registerRole('Query.QueryBar', queryBarRole); - const queryBarActions = setupActions(queryBarRole, localAppRegistry); - setupStore({ - role: queryBarRole, + setupQueryPlugins({ globalAppRegistry, localAppRegistry, - dataService: state.dataService, - namespace, serverVersion, + state, + namespace, isReadonly, isTimeSeries, isClustered, isFLE, - actions: queryBarActions, query, aggregation, }); diff --git a/packages/compass-components/package.json b/packages/compass-components/package.json index c8f2f14d77e..7aa0e691495 100644 --- a/packages/compass-components/package.json +++ b/packages/compass-components/package.json @@ -48,6 +48,7 @@ "@leafygreen-ui/modal": "^7.0.0", "@leafygreen-ui/palette": "^3.3.1", "@leafygreen-ui/pipeline": "^2.1.5", + "@leafygreen-ui/popover": "^7.2.2", "@leafygreen-ui/portal": "^4.0.2", "@leafygreen-ui/radio-box-group": "^6.1.5", "@leafygreen-ui/radio-group": "^7.0.6", diff --git a/packages/compass-components/src/components/leafygreen.tsx b/packages/compass-components/src/components/leafygreen.tsx index 2bb25517b0b..d1a21a5543a 100644 --- a/packages/compass-components/src/components/leafygreen.tsx +++ b/packages/compass-components/src/components/leafygreen.tsx @@ -25,6 +25,7 @@ import { default as LeafyGreenModal, Footer as LeafyGreenModalFooter, } from '@leafygreen-ui/modal'; +import Popover from '@leafygreen-ui/popover'; import { RadioBox, RadioBoxGroup } from '@leafygreen-ui/radio-box-group'; import { Radio, @@ -160,6 +161,7 @@ export { ModalFooter, MongoDBLogoMark, MongoDBLogo, + Popover, RadioBox, RadioBoxGroup, Radio, diff --git a/packages/compass-query-bar/src/components/query-bar/query-bar.tsx b/packages/compass-query-bar/src/components/query-bar/query-bar.tsx index 9a2480f45e0..4cdcd675599 100644 --- a/packages/compass-query-bar/src/components/query-bar/query-bar.tsx +++ b/packages/compass-query-bar/src/components/query-bar/query-bar.tsx @@ -1,9 +1,10 @@ -import React from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { Button, Icon, Label, MoreOptionsToggle, + Popover, css, cx, focusRingStyles, @@ -11,6 +12,10 @@ import { spacing, uiColors, } from '@mongodb-js/compass-components'; +import type AppRegistry from 'hadron-app-registry'; +import { createLoggerAndTelemetry } from '@mongodb-js/compass-logging'; + +const { track } = createLoggerAndTelemetry('COMPASS-QUERY-BAR-UI'); const queryBarStyles = css({ display: 'flex', @@ -30,6 +35,16 @@ const openQueryHistoryLabelStyles = css({ padding: 0, }); +const queryHistoryContainerStyles = css({ + display: 'flex', + height: '100%', +}); + +const queryHistoryPopoverStyles = css({ + maxHeight: 'calc(100vh - 260px)', + display: 'flex', +}); + const openQueryHistoryStyles = cx( css({ border: 'none', @@ -45,6 +60,43 @@ const openQueryHistoryStyles = cx( focusRingStyles ); +function useOnClickOutside( + ref: React.RefObject, + buttonRef: React.RefObject, + useHook: boolean, + handler: (event: Event) => void +) { + useEffect( + () => { + if (useHook) { + const listener: EventListener = (event) => { + // Do nothing if clicking ref's element or descendent elements + if (!ref.current || ref.current.contains(event.target)) { + return; + } + if (!buttonRef.current || buttonRef.current.contains(event.target)) { + return; + } + handler(event); + }; + document.addEventListener('mousedown', listener); + document.addEventListener('touchstart', listener); + return () => { + document.removeEventListener('mousedown', listener); + document.removeEventListener('touchstart', listener); + }; + } + }, + // Add ref and handler to effect dependencies + // It's worth noting that because passed in handler is a new ... + // ... function on every render that will cause this effect ... + // ... callback/cleanup to run every render. It's not a big deal ... + // ... but to optimize you can wrap handler in useCallback before ... + // ... passing it into this hook. + [ref, buttonRef, handler, useHook] + ); +} + type QueryBarProps = { buttonLabel?: string; expanded: boolean; @@ -54,6 +106,10 @@ type QueryBarProps = { showQueryHistoryButton?: boolean; toggleExpandQueryOptions: () => void; toggleQueryHistory: () => void; + store: { + globalAppRegistry: AppRegistry; + localAppRegistry: AppRegistry; + }; }; export const QueryBar: React.FunctionComponent = ({ @@ -63,8 +119,37 @@ export const QueryBar: React.FunctionComponent = ({ queryState, showQueryHistoryButton = true, toggleExpandQueryOptions, - toggleQueryHistory, + store, }) => { + const queryHistoryStore = store.localAppRegistry.getStore('Query.History'); + const queryHistoryActions = store.localAppRegistry.getAction( + 'Query.History.Actions' + ); + // TODO: When `showQueryHistoryButton` is false let's assume this doesn't exist. + const QueryHistoryComponent = + store.globalAppRegistry.getRole('Query.QueryHistory')![0].component; + + const queryHistoryButtonRef = useRef(null); + + const queryHistoryContainerRef = useRef(null); + + const [showQueryHistory, setShowQueryHistory] = useState(false); + + useOnClickOutside( + queryHistoryContainerRef, + queryHistoryButtonRef, + showQueryHistory, + () => setShowQueryHistory(false) + ); + + const onClickToggleQueryHistory = useCallback(() => { + if (!showQueryHistory) { + track('Query History Opened'); + } + + setShowQueryHistory(!showQueryHistory); + }, [showQueryHistory, setShowQueryHistory]); + return (
{showQueryHistoryButton && ( @@ -77,14 +162,37 @@ export const QueryBar: React.FunctionComponent = ({ + +
+ setShowQueryHistory(false)} + store={queryHistoryStore} + actions={queryHistoryActions} + /> +
+
)}
Query Area (coming soon)
diff --git a/packages/compass-query-bar/src/plugin.jsx b/packages/compass-query-bar/src/plugin.jsx index f9ca8768360..b7f462337bb 100644 --- a/packages/compass-query-bar/src/plugin.jsx +++ b/packages/compass-query-bar/src/plugin.jsx @@ -25,6 +25,7 @@ class Plugin extends Component { ) : ( diff --git a/packages/compass-query-history/src/components/query-history/query-history.jsx b/packages/compass-query-history/src/components/query-history/query-history.jsx index 5b048aafbc7..00ea3616f1f 100644 --- a/packages/compass-query-history/src/components/query-history/query-history.jsx +++ b/packages/compass-query-history/src/components/query-history/query-history.jsx @@ -16,9 +16,9 @@ class QueryHistory extends PureComponent { static propTypes = { actions: PropTypes.object.isRequired, + onClose: PropTypes.func.isRequired, store: PropTypes.object.isRequired, showing: PropTypes.oneOf(['recent', 'favorites']), - collapsed: PropTypes.bool, ns: PropTypes.object, }; @@ -59,16 +59,17 @@ class QueryHistory extends PureComponent { ); render() { - const { collapsed, showing, actions } = this.props; - - if (collapsed) { - return null; - } + const { showing, actions, onClose } = this.props; return (
- + {showing === 'favorites' ? this.renderFavorites() : null} {showing === 'recent' ? this.renderRecents() : null} diff --git a/packages/compass-query-history/src/components/query-history/query-history.module.less b/packages/compass-query-history/src/components/query-history/query-history.module.less index d0f9c37bdf0..ee680e9d73a 100644 --- a/packages/compass-query-history/src/components/query-history/query-history.module.less +++ b/packages/compass-query-history/src/components/query-history/query-history.module.less @@ -1,23 +1,12 @@ -@keyframes slidein { - from { - width: 0px; - } - to { - width: 325px; - } -} - .component { display: flex; flex-direction: column; flex: 1; - animation: 400ms slidein; background-color: #f5f6f7; - border-left: solid 1px #dddddd; - min-width: 325px; - max-width: 325px; + border: 1px solid #dddddd; + width: 325px; height: 100%; - order: 3; + box-shadow: rgba(0, 30, 43, 0.3) 0px 4px 10px -4px; } .inner { diff --git a/packages/compass-query-history/src/components/toolbar/toolbar.tsx b/packages/compass-query-history/src/components/toolbar/toolbar.tsx index ee4a852984c..13c0a12a4bc 100644 --- a/packages/compass-query-history/src/components/toolbar/toolbar.tsx +++ b/packages/compass-query-history/src/components/toolbar/toolbar.tsx @@ -10,6 +10,9 @@ import { css, spacing, } from '@mongodb-js/compass-components'; +import { createLoggerAndTelemetry } from '@mongodb-js/compass-logging'; + +const { track } = createLoggerAndTelemetry('COMPASS-QUERY-HISTORY-UI'); const toolbarStyles = css({ display: 'flex', @@ -33,32 +36,33 @@ const closeButtonStyles = css({ }); type ToolbarProps = { - actions: { - showRecent: () => void; - showFavorites: () => void; - collapse: () => void; - }; // Query history actions are not currently typed. + onClose: () => void; showing: 'recent' | 'favorites'; + showFavorites: () => void; + showRecent: () => void; }; const Toolbar: React.FunctionComponent = ({ - actions, + onClose, showing, + showRecent, + showFavorites, }) => { const onViewSwitch = useCallback( (label: 'recent' | 'favorites') => { if (label === 'recent') { - actions.showRecent(); + showRecent(); } else if (label === 'favorites') { - actions.showFavorites(); + showFavorites(); } }, - [actions] + [showRecent, showFavorites] ); - const onCollapse = useCallback(() => { - actions.collapse(); - }, [actions]); + const onClickClose = useCallback(() => { + track('Query History Closed'); + onClose(); + }, [onClose]); const labelId = useId(); const controlId = useId(); @@ -95,7 +99,7 @@ const Toolbar: React.FunctionComponent = ({ diff --git a/packages/compass-query-history/src/index.ts b/packages/compass-query-history/src/index.ts index 32ce6045b4b..53cd7170ec7 100644 --- a/packages/compass-query-history/src/index.ts +++ b/packages/compass-query-history/src/index.ts @@ -14,6 +14,7 @@ const ROLE = { configureStore: configureStore, configureActions: configureActions, storeName: 'Query.History', + actionName: 'Query.History.Actions', }; /** @@ -21,7 +22,7 @@ const ROLE = { * @param {Object} appRegistry - The Hadron appRegisrty to activate this plugin with. **/ function activate(appRegistry: AppRegistry): void { - appRegistry.registerRole('Collection.ScopedModal', ROLE); + appRegistry.registerRole('Query.QueryHistory', ROLE); } /** @@ -29,7 +30,7 @@ function activate(appRegistry: AppRegistry): void { * @param {Object} appRegistry - The Hadron appRegisrty to deactivate this plugin with. **/ function deactivate(appRegistry: AppRegistry): void { - appRegistry.deregisterRole('Collection.ScopedModal', ROLE); + appRegistry.deregisterRole('Query.QueryHistory', ROLE); } export default QueryHistoryPlugin; diff --git a/packages/compass-query-history/src/stores/query-history-store.js b/packages/compass-query-history/src/stores/query-history-store.js index c0b88ea5f63..c766ef302c3 100644 --- a/packages/compass-query-history/src/stores/query-history-store.js +++ b/packages/compass-query-history/src/stores/query-history-store.js @@ -42,24 +42,6 @@ const configureStore = (options = {}) => { }); }, - collapse() { - if (!this.state.collapsed) { - track('Query History Closed'); - this.setState({ - collapsed: true, - }); - } - }, - - toggleCollapse() { - track( - this.state.collapsed ? 'Query History Opened' : 'Query History Closed' - ); - this.setState({ - collapsed: !this.state.collapsed, - }); - }, - /** * Plugin lifecycle method that is called when the namespace changes in Compass. * @@ -79,7 +61,6 @@ const configureStore = (options = {}) => { getInitialState() { return { showing: 'recent', - collapsed: true, ns: mongodbns(''), }; }, @@ -92,14 +73,6 @@ const configureStore = (options = {}) => { if (options.localAppRegistry) { const localAppRegistry = options.localAppRegistry; - localAppRegistry.on('collapse-query-history', () => { - store.collapse(); - }); - - localAppRegistry.on('toggle-query-history', () => { - store.toggleCollapse(); - }); - // Configure all the other stores. const favoriteListStore = localAppRegistry.getStore(FAVORITE_LIST_STORE); const recentListStore = localAppRegistry.getStore(RECENT_LIST_STORE); From 202590a737fe38056953550fae88592b2b5403d0 Mon Sep 17 00:00:00 2001 From: Anemy Date: Thu, 14 Jul 2022 14:32:50 -0400 Subject: [PATCH 02/15] remove feature flag for toolbars, remove old toolbar code --- .../src/components/toolbar.tsx | 3 +- .../src/constants/mongodb-ace-theme-query.ts | 115 +---- .../src/components/document-list.less | 1 - .../explain-states/explain-states.jsx | 132 +---- .../explain-states/explain-states.module.less | 24 - .../explain-states/explain-states.spec.jsx | 12 +- .../explain-toolbar/explain-toolbar.tsx | 1 - packages/compass-home/src/index.less | 1 - .../components/legacy-option-editor/index.js | 3 - .../legacy-option-editor.jsx | 139 ------ .../option-editor.module.less | 3 - .../option-editor.spec.jsx | 54 --- .../components/legacy-options-toggle/index.js | 4 - .../legacy-options-toggle/options-toggle.jsx | 52 -- .../options-toggle.module.less | 15 - .../options-toggle.spec.jsx | 55 --- .../src/components/legacy-query-bar/index.js | 4 - .../components/legacy-query-bar/query-bar.jsx | 459 ------------------ .../legacy-query-bar/query-bar.module.less | 103 ---- .../legacy-query-bar/query-bar.spec.jsx | 438 ----------------- .../components/legacy-query-option/index.js | 4 - .../legacy-query-option.jsx | 135 ------ .../legacy-query-option.spec.jsx | 94 ---- .../query-option.module.less | 127 ----- .../src/components/query-bar.tsx | 5 - packages/compass-query-bar/src/plugin.jsx | 34 +- .../analysis-complete-message.jsx | 46 -- .../analysis-complete-message/index.js | 2 - .../compass-schema/compass-schema.jsx | 83 +--- packages/compass/src/index.d.ts | 6 - 30 files changed, 47 insertions(+), 2107 deletions(-) delete mode 100644 packages/compass-query-bar/src/components/legacy-option-editor/index.js delete mode 100644 packages/compass-query-bar/src/components/legacy-option-editor/legacy-option-editor.jsx delete mode 100644 packages/compass-query-bar/src/components/legacy-option-editor/option-editor.module.less delete mode 100644 packages/compass-query-bar/src/components/legacy-option-editor/option-editor.spec.jsx delete mode 100644 packages/compass-query-bar/src/components/legacy-options-toggle/index.js delete mode 100644 packages/compass-query-bar/src/components/legacy-options-toggle/options-toggle.jsx delete mode 100644 packages/compass-query-bar/src/components/legacy-options-toggle/options-toggle.module.less delete mode 100644 packages/compass-query-bar/src/components/legacy-options-toggle/options-toggle.spec.jsx delete mode 100644 packages/compass-query-bar/src/components/legacy-query-bar/index.js delete mode 100644 packages/compass-query-bar/src/components/legacy-query-bar/query-bar.jsx delete mode 100644 packages/compass-query-bar/src/components/legacy-query-bar/query-bar.module.less delete mode 100644 packages/compass-query-bar/src/components/legacy-query-bar/query-bar.spec.jsx delete mode 100644 packages/compass-query-bar/src/components/legacy-query-option/index.js delete mode 100644 packages/compass-query-bar/src/components/legacy-query-option/legacy-query-option.jsx delete mode 100644 packages/compass-query-bar/src/components/legacy-query-option/legacy-query-option.spec.jsx delete mode 100644 packages/compass-query-bar/src/components/legacy-query-option/query-option.module.less delete mode 100644 packages/compass-schema/src/components/analysis-complete-message/analysis-complete-message.jsx delete mode 100644 packages/compass-schema/src/components/analysis-complete-message/index.js diff --git a/packages/compass-components/src/components/toolbar.tsx b/packages/compass-components/src/components/toolbar.tsx index 711d9c07a0c..2dc48d72cd2 100644 --- a/packages/compass-components/src/components/toolbar.tsx +++ b/packages/compass-components/src/components/toolbar.tsx @@ -3,10 +3,9 @@ import { cx, css } from '@leafygreen-ui/emotion'; import { uiColors } from '@leafygreen-ui/palette'; import { withTheme } from '../hooks/use-theme'; -import { gray8 } from '../compass-ui-colors'; const toolbarLightThemeStyles = css({ - backgroundColor: gray8, + backgroundColor: uiColors.white, color: uiColors.gray.dark2, }); diff --git a/packages/compass-components/src/constants/mongodb-ace-theme-query.ts b/packages/compass-components/src/constants/mongodb-ace-theme-query.ts index 960333137c1..a0b925a95d6 100644 --- a/packages/compass-components/src/constants/mongodb-ace-theme-query.ts +++ b/packages/compass-components/src/constants/mongodb-ace-theme-query.ts @@ -1,113 +1,3 @@ -const mongodbAceThemeQueryCssTextLegacy = ` -.ace-mongodb-query .ace_gutter { -background: #ffffff; -color: #999999; -} -.ace-mongodb-query { -background: #ffffff; -color: #000; -} -.ace-mongodb-query .ace_placeholder { -font-family: inherit; -transform: none; -opacity: 1; -margin: 0; -} -.ace-mongodb-query .ace_keyword { -color: #999999; -font-weight: normal; -} -.ace-mongodb-query .ace_gutter-cell { -padding-left: 5px; -padding-right: 10px; -} -.ace-mongodb-query .ace_string { -color: #5b81a9; -} -.ace-mongodb-query .ace_boolean { -color: #5b81a9; -font-weight: normal; -} -.ace-mongodb-query .ace_constant.ace_numeric { -color: #5b81a9; -} -.ace-mongodb-query .ace_string.ace_regexp { -color: #5b81a9; -} -.ace-mongodb-query .ace_variable.ace_class { -color: teal; -} -.ace-mongodb-query .ace_constant.ace_buildin { -color: #0086B3; -} -.ace-mongodb-query .ace_support.ace_function { -color: #0086B3; -} -.ace-mongodb-query .ace_comment { -color: #998; -font-style: italic; -} -.ace-mongodb-query .ace_variable.ace_language { -color: #0086B3; -} -.ace-mongodb-query .ace_paren { -font-weight: normal; -} -.ace-mongodb-query .ace_variable.ace_instance { -color: teal; -} -.ace-mongodb-query .ace_constant.ace_language { -font-weight: bold; -} -.ace-mongodb-query .ace_cursor { -color: #999999; -} -.ace-mongodb-query.ace_focus .ace_marker-layer .ace_active-line { -background: #ffffff; -} -.ace-mongodb-query .ace_marker-layer .ace_active-line { -background: #ffffff; -} -.ace-mongodb-query .ace_marker-layer .ace_selection { -background: rgb(181, 213, 255); -} -.ace-mongodb-query.ace_multiselect .ace_selection.ace_start { -box-shadow: 0 0 3px 0px white; -} -.ace-mongodb-query.ace_nobold .ace_line > span { -font-weight: normal !important; -} -.ace-mongodb-query .ace_marker-layer .ace_step { -background: rgb(252, 255, 0); -} -.ace-mongodb-query .ace_marker-layer .ace_stack { -background: rgb(164, 229, 101); -} -.ace-mongodb-query .ace_marker-layer .ace_bracket { -margin: -1px 0 0 -1px; -border: 1px solid rgb(192, 192, 192); -} -.ace-mongodb-query .ace_gutter-active-line { -background: #ffffff; -} -.ace-mongodb-query .ace_marker-layer .ace_selected-word { -background: rgb(250, 250, 255); -border: 1px solid rgb(200, 200, 250); -} -.ace-mongodb-query .ace_invisible { -color: #BFBFBF -} -.ace-mongodb-query .ace_print-margin { -width: 1px; -background: #e8e8e8; -} -.ace-mongodb-query .ace_hidden-cursors { - opacity: 0; -} -.ace-mongodb-query .ace_indent-guide { -background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y; -}`; - const mongodbAceThemeQueryCssText = ` .ace-mongodb-query .ace_scroller { margin: 0px 6px; @@ -224,10 +114,7 @@ background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZg function mongodbAceThemeQuery(acequire: any, exports: any) { exports.isDark = false; exports.cssClass = 'ace-mongodb-query'; - exports.cssText = - process?.env?.COMPASS_SHOW_NEW_TOOLBARS === 'true' - ? mongodbAceThemeQueryCssText - : mongodbAceThemeQueryCssTextLegacy; + exports.cssText = mongodbAceThemeQueryCssText; const dom = acequire('../lib/dom'); dom.importCssString(exports.cssText, exports.cssClass); } diff --git a/packages/compass-crud/src/components/document-list.less b/packages/compass-crud/src/components/document-list.less index b224f76b86f..62941404ddb 100644 --- a/packages/compass-crud/src/components/document-list.less +++ b/packages/compass-crud/src/components/document-list.less @@ -42,7 +42,6 @@ flex-direction: row; justify-content: space-between; align-items: center; - background: #f5f6f7; border-bottom: 1px solid #ebebed; padding: 5px 15px; min-width: max-content; diff --git a/packages/compass-explain-plan/src/components/explain-states/explain-states.jsx b/packages/compass-explain-plan/src/components/explain-states/explain-states.jsx index d3315412fe8..2396a788cb3 100644 --- a/packages/compass-explain-plan/src/components/explain-states/explain-states.jsx +++ b/packages/compass-explain-plan/src/components/explain-states/explain-states.jsx @@ -6,29 +6,16 @@ import { ButtonVariant, Link, } from '@mongodb-js/compass-components'; -import { ZeroState, StatusRow, ViewSwitcher } from 'hadron-react-components'; +import { ZeroState } from 'hadron-react-components'; import { ZeroGraphic } from '../zero-graphic'; import { ExplainBody } from '../explain-body'; import INDEX_TYPES from '../../constants/index-types'; import EXPLAIN_STATES from '../../constants/explain-states'; -import EXPLAIN_VIEWS from '../../constants/explain-views'; import styles from './explain-states.module.less'; import { ExplainToolbar } from '../explain-toolbar/explain-toolbar'; -/** - * Readonly warning for the status row. - */ -const READ_ONLY_WARNING = 'Explain plans on readonly views are not supported.'; - -/** - * Outdated warning for the status row. - */ -const OUTDATED_WARNING = `The explain content is outdated and no longer in sync -with the documents view. Press "Explain" again to see the explain plan for -the current query.`; - /** * Header for zero state. */ @@ -92,19 +79,6 @@ class ExplainStates extends Component { this.props.queryExecuted(); } - /** - * On view switch handler. - * - * @param {String} label - The label. - */ - onViewSwitch(label) { - if (label === 'Visual Tree') { - this.props.switchToTreeView(); - } else if (label === 'Raw JSON') { - this.props.switchToJSONView(); - } - } - /** * Executes the explain plan. */ @@ -129,27 +103,6 @@ class ExplainStates extends Component { ); } - /** - * Render banner with information. - * - * @returns {React.Component} The component. - */ - renderBanner() { - if (!this.props.isEditable) { - return {READ_ONLY_WARNING}; - } - - if (this.props.explain.explainState === EXPLAIN_STATES.OUTDATED) { - return {OUTDATED_WARNING}; - } - - if (this.props.explain.error) { - return ( - {this.props.explain.error.message} - ); - } - } - /** * Render the schema validation component zero state. * @@ -196,84 +149,27 @@ class ExplainStates extends Component { } } - /** - * Renders QueryBar component. - * - * @returns {React.Component} The component. - */ - renderQueryBar() { - return ( - - ); - } - - /** - * Renders ViewSwitcher component. - * - * @returns {React.Component} The component. - */ - renderViewSwitcher() { - const activeViewTypeButton = - this.props.explain.viewType === EXPLAIN_VIEWS.tree - ? 'Visual Tree' - : 'Raw JSON'; - - return ( -
- -
- ); - } - /** * Renders ExplainPlan component. * * @returns {React.Component} The rendered component. */ render() { - const useNewToolbars = process?.env?.COMPASS_SHOW_NEW_TOOLBARS; return ( <> - {useNewToolbars ? ( - - ) : ( -
- {this.renderBanner()} - {this.renderQueryBar()} - {this.renderViewSwitcher()} -
- )} + {this.renderZeroState()} {this.renderContent()} diff --git a/packages/compass-explain-plan/src/components/explain-states/explain-states.module.less b/packages/compass-explain-plan/src/components/explain-states/explain-states.module.less index f5cb1bb2a78..91fb050bf59 100644 --- a/packages/compass-explain-plan/src/components/explain-states/explain-states.module.less +++ b/packages/compass-explain-plan/src/components/explain-states/explain-states.module.less @@ -1,29 +1,5 @@ @import (reference) 'mongodb-compass/src/app/styles/index.less'; -.controls-container { - flex-grow: 0; - flex-shrink: 0; - flex-basis: auto; - box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); - z-index: 4; - border-top: 1px solid #ebebed; -} - -// Just a drop-shadow for now but should think about where to put these styles eventually -.action-bar { - height: 32px; - background: @gray8; - border-bottom: 1px solid @gray7; - padding-top: 4px; - padding-left: 15px; - - span { - text-transform: uppercase; - padding-right: 3px; - font-size: 11px; - } -} - .zero-state-container { height: 100%; display: flex; diff --git a/packages/compass-explain-plan/src/components/explain-states/explain-states.spec.jsx b/packages/compass-explain-plan/src/components/explain-states/explain-states.spec.jsx index 06653385b50..9465c4c3a7e 100644 --- a/packages/compass-explain-plan/src/components/explain-states/explain-states.spec.jsx +++ b/packages/compass-explain-plan/src/components/explain-states/explain-states.spec.jsx @@ -3,9 +3,9 @@ import { mount } from 'enzyme'; import AppRegistry from 'hadron-app-registry'; import sinon from 'sinon'; import { expect } from 'chai'; +import { Banner, Toolbar } from '@mongodb-js/compass-components'; import ExplainStates from '../explain-states'; -import styles from './explain-states.module.less'; describe('ExplainStates [Component]', function () { let component; @@ -63,11 +63,15 @@ describe('ExplainStates [Component]', function () { component = null; }); - it('renders the wrapper div', function () { - expect(component.find(`.${styles['controls-container']}`)).to.be.present(); + it('renders the toolbar', function () { + expect(component.find(Toolbar)).to.be.present(); }); it('renders the read only banner', function () { - expect(component.find('StatusRow')).to.be.present(); + expect(component.find(Banner)).to.be.present(); + }); + + it('renders the query bar', function () { + expect(component.find('Query Bar')).to.be.present(); }); }); diff --git a/packages/compass-explain-plan/src/components/explain-toolbar/explain-toolbar.tsx b/packages/compass-explain-plan/src/components/explain-toolbar/explain-toolbar.tsx index f879cda2572..c6bd52c9b71 100644 --- a/packages/compass-explain-plan/src/components/explain-toolbar/explain-toolbar.tsx +++ b/packages/compass-explain-plan/src/components/explain-toolbar/explain-toolbar.tsx @@ -22,7 +22,6 @@ const explainToolbarStyles = css({ alignItems: 'center', gap: spacing[3], padding: spacing[3], - boxShadow: '0 2px 2px rgb(0 0 0 / 20%)', zIndex: 10, }); diff --git a/packages/compass-home/src/index.less b/packages/compass-home/src/index.less index ba427998668..7e3001bd79c 100644 --- a/packages/compass-home/src/index.less +++ b/packages/compass-home/src/index.less @@ -1,6 +1,5 @@ @gray4: #a09f9e; @gray7: #ebebed; -@gray8: #f5f6f7; @gray-base: #000; @gray-lighter: lighten(@gray-base, 93.5%); // #eee .badge { diff --git a/packages/compass-query-bar/src/components/legacy-option-editor/index.js b/packages/compass-query-bar/src/components/legacy-option-editor/index.js deleted file mode 100644 index 2b98c6f3f55..00000000000 --- a/packages/compass-query-bar/src/components/legacy-option-editor/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import LegacyOptionEditor from './legacy-option-editor'; - -export default LegacyOptionEditor; diff --git a/packages/compass-query-bar/src/components/legacy-option-editor/legacy-option-editor.jsx b/packages/compass-query-bar/src/components/legacy-option-editor/legacy-option-editor.jsx deleted file mode 100644 index bbf4ecb5098..00000000000 --- a/packages/compass-query-bar/src/components/legacy-option-editor/legacy-option-editor.jsx +++ /dev/null @@ -1,139 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { QueryAutoCompleter } from 'mongodb-ace-autocompleter'; -import { - Editor, - EditorVariant, - EditorTextCompleter, -} from '@mongodb-js/compass-components'; - -import styles from './option-editor.module.less'; - -class LegacyOptionEditor extends Component { - static displayName = 'OptionEditor'; - - static propTypes = { - label: PropTypes.string.isRequired, - serverVersion: PropTypes.string.isRequired, - autoPopulated: PropTypes.bool.isRequired, - refreshEditorAction: PropTypes.func.isRequired, - value: PropTypes.any, - onChange: PropTypes.func, - onApply: PropTypes.func, - placeholder: PropTypes.string, - schemaFields: PropTypes.array, - }; - - static defaultProps = { - label: '', - value: '', - serverVersion: '3.6.0', - autoPopulated: false, - schemaFields: [], - }; - - /** - * Set up the autocompleters once on initialization. - * - * @param {Object} props - The properties. - */ - constructor(props) { - super(props); - this.completer = new QueryAutoCompleter( - props.serverVersion, - EditorTextCompleter, - props.schemaFields - ); - this.boundOnFieldsChanged = this.onFieldsChanged.bind(this); - } - - /** - * Subscribe on mount. - */ - componentDidMount() { - this.unsub = this.props.refreshEditorAction.listen(() => { - this.editor.setValue(this.props.value); - this.editor.clearSelection(); - }); - } - - /** - * @param {Object} nextProps - The next properties. - * - * @returns {Boolean} If the component should update. - */ - shouldComponentUpdate(nextProps) { - this.boundOnFieldsChanged(nextProps.schemaFields); - return ( - nextProps.autoPopulated || - nextProps.serverVersion !== this.props.serverVersion - ); - } - - /** - * Unsubscribe listeners. - */ - componentWillUnmount() { - this.unsub(); - } - - onFieldsChanged(fields) { - this.completer.update(fields); - } - - /** - * Handle the changing of the query text. - * - * @param {String} newCode - The new query. - */ - onChangeQuery = (newCode) => { - this.props.onChange({ - target: { - value: newCode, - }, - }); - }; - - /** - * Render the editor. - * - * @returns {Component} The component. - */ - render() { - return ( - { - this.editor = editor; - this.editor.setBehavioursEnabled(true); - this.editor.commands.addCommand({ - name: 'executeQuery', - bindKey: { - win: 'Enter', - mac: 'Enter', - }, - exec: () => { - this.props.onApply(); - }, - }); - }} - /> - ); - } -} - -export default LegacyOptionEditor; diff --git a/packages/compass-query-bar/src/components/legacy-option-editor/option-editor.module.less b/packages/compass-query-bar/src/components/legacy-option-editor/option-editor.module.less deleted file mode 100644 index 19126f4b771..00000000000 --- a/packages/compass-query-bar/src/components/legacy-option-editor/option-editor.module.less +++ /dev/null @@ -1,3 +0,0 @@ -.option-editor { - align-self: center; -} diff --git a/packages/compass-query-bar/src/components/legacy-option-editor/option-editor.spec.jsx b/packages/compass-query-bar/src/components/legacy-option-editor/option-editor.spec.jsx deleted file mode 100644 index c410a95d96e..00000000000 --- a/packages/compass-query-bar/src/components/legacy-option-editor/option-editor.spec.jsx +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react'; -import { mount } from 'enzyme'; -import { expect } from 'chai'; -import sinon from 'sinon'; - -import OptionEditor from '.'; -import configureActions from '../../actions'; - -import styles from './option-editor.module.less'; - -describe('OptionEditor [Component]', function () { - let onChangeSpy; - let onApplySpy; - let component; - - beforeEach(function () { - onChangeSpy = sinon.spy(); - onApplySpy = sinon.spy(); - }); - - afterEach(function () { - onChangeSpy = null; - onApplySpy = null; - }); - - context('when rendering the component', function () { - before(function () { - component = mount( - - ); - }); - - after(function () { - component = null; - }); - - it('renders the editor', function () { - expect(component.find('#query-bar-option-input-Apply')).to.be.present(); - }); - - it('has the correct class', function () { - expect(component.find(`.${styles['option-editor']}`)).to.be.present(); - }); - }); -}); diff --git a/packages/compass-query-bar/src/components/legacy-options-toggle/index.js b/packages/compass-query-bar/src/components/legacy-options-toggle/index.js deleted file mode 100644 index 8cbb49ababc..00000000000 --- a/packages/compass-query-bar/src/components/legacy-options-toggle/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import OptionsToggle from './options-toggle'; - -export default OptionsToggle; -export { OptionsToggle }; diff --git a/packages/compass-query-bar/src/components/legacy-options-toggle/options-toggle.jsx b/packages/compass-query-bar/src/components/legacy-options-toggle/options-toggle.jsx deleted file mode 100644 index cfac195d9c3..00000000000 --- a/packages/compass-query-bar/src/components/legacy-options-toggle/options-toggle.jsx +++ /dev/null @@ -1,52 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import FontAwesome from 'react-fontawesome'; - -import styles from './options-toggle.module.less'; - -class OptionsToggle extends PureComponent { - static displayName = 'OptionsToggle'; - - static propTypes = { - actions: PropTypes.object.isRequired, - className: PropTypes.string, - expanded: PropTypes.bool.isRequired, - }; - - static defaultProps = { - className: '', - }; - - onClick = () => { - this.props.actions.toggleQueryOptions(); - }; - - render() { - const { expanded, className } = this.props; - const symbol = expanded ? 'caret-down' : 'caret-right'; - - const _className = classnames( - 'btn', - 'btn-default', - 'btn-xs', - styles.component, - { [styles['is-open']]: expanded }, - className - ); - - return ( - - ); - } -} - -export default OptionsToggle; -export { OptionsToggle }; diff --git a/packages/compass-query-bar/src/components/legacy-options-toggle/options-toggle.module.less b/packages/compass-query-bar/src/components/legacy-options-toggle/options-toggle.module.less deleted file mode 100644 index 2b0d0c06ad5..00000000000 --- a/packages/compass-query-bar/src/components/legacy-options-toggle/options-toggle.module.less +++ /dev/null @@ -1,15 +0,0 @@ -.component { - margin-right: 5px; - margin-top: 3px; - padding-left: 5px !important; - cursor: pointer; - border-radius: 0 9px 9px 0 !important; - z-index: 100; - display: flex; - flex-direction: row; - align-items: center; - - &.is-open { - border-bottom-right-radius: 0 !important; - } -} diff --git a/packages/compass-query-bar/src/components/legacy-options-toggle/options-toggle.spec.jsx b/packages/compass-query-bar/src/components/legacy-options-toggle/options-toggle.spec.jsx deleted file mode 100644 index 6e55f5a8273..00000000000 --- a/packages/compass-query-bar/src/components/legacy-options-toggle/options-toggle.spec.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from 'react'; -import { shallow } from 'enzyme'; -import { expect } from 'chai'; -import sinon from 'sinon'; -import FontAwesome from 'react-fontawesome'; - -import OptionsToggle from '.'; - -describe('OptionsToggle [Component]', function () { - let actions; - - beforeEach(function (done) { - actions = { toggleQueryOptions: sinon.stub() }; - done(); - }); - - afterEach(function (done) { - actions = null; - done(); - }); - - describe('#rendering', function () { - it('should render the correct icon when it is not expanded', function () { - const component = shallow( - - ); - expect(component.find(FontAwesome)).to.have.prop('name', 'caret-right'); - }); - - it('should render the correct icon when it is expanded', function () { - const component = shallow(); - expect(component.find(FontAwesome)).to.have.prop('name', 'caret-down'); - }); - - it('should render the correct text', function () { - const component = shallow( - - ); - expect( - component.find('[data-test-id="query-bar-options-toggle-text"]') - ).to.have.text('Options'); - }); - }); - - describe('#behaviour', function () { - it('should trigger the toggleQueryOptions action when clicked', function () { - const component = shallow( - - ); - - component.simulate('click'); - expect(actions.toggleQueryOptions).to.be.calledOnce; - }); - }); -}); diff --git a/packages/compass-query-bar/src/components/legacy-query-bar/index.js b/packages/compass-query-bar/src/components/legacy-query-bar/index.js deleted file mode 100644 index 9f66da8b348..00000000000 --- a/packages/compass-query-bar/src/components/legacy-query-bar/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import QueryBar from './query-bar'; - -export default QueryBar; -export { QueryBar }; diff --git a/packages/compass-query-bar/src/components/legacy-query-bar/query-bar.jsx b/packages/compass-query-bar/src/components/legacy-query-bar/query-bar.jsx deleted file mode 100644 index 6f274309d0d..00000000000 --- a/packages/compass-query-bar/src/components/legacy-query-bar/query-bar.jsx +++ /dev/null @@ -1,459 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import { Dropdown, MenuItem } from 'react-bootstrap'; -import { pick, isEqual, isString, isArray, map } from 'lodash'; -import FontAwesome from 'react-fontawesome'; - -import QueryOption from '../legacy-query-option'; -import OptionsToggle from '../legacy-options-toggle'; -import QUERY_PROPERTIES from '../../constants/query-properties'; - -import styles from './query-bar.module.less'; - -/** - * @type {Record} - */ -const OPTION_DEFINITION = { - filter: { - type: 'document', - placeholder: "{ field: 'value' }", - link: 'https://docs.mongodb.com/compass/current/query/filter/', - }, - project: { - type: 'document', - placeholder: '{ field: 0 }', - link: 'https://docs.mongodb.com/manual/tutorial/project-fields-from-query-results/', - }, - sort: { - type: 'document', - placeholder: "{ field: -1 } or [['field', -1]]", - link: 'https://docs.mongodb.com/manual/reference/method/cursor.sort/', - }, - collation: { - type: 'document', - placeholder: "{ locale: 'simple' }", - link: 'https://docs.mongodb.com/master/reference/collation/', - }, - skip: { - type: 'numeric', - placeholder: '0', - link: 'https://docs.mongodb.com/manual/reference/method/cursor.skip/', - }, - limit: { - type: 'numeric', - placeholder: '0', - link: 'https://docs.mongodb.com/manual/reference/method/cursor.limit/', - }, - maxTimeMS: { - label: 'Max Time MS', - type: 'numeric', - placeholder: '60000', - link: 'https://docs.mongodb.com/manual/reference/method/cursor.maxTimeMS/', - }, - sample: { - type: 'boolean', - placeholder: null, - link: 'https://docs.mongodb.com/TBD', - }, -}; - -class QueryBar extends Component { - static displayName = 'QueryBar'; - - static propTypes = { - filter: PropTypes.object, - project: PropTypes.object, - sort: PropTypes.object, - collation: PropTypes.object, - skip: PropTypes.number, - limit: PropTypes.number, - maxTimeMS: PropTypes.number, - sample: PropTypes.bool, - - valid: PropTypes.bool, - filterValid: PropTypes.bool, - projectValid: PropTypes.bool, - collationValid: PropTypes.bool, - sortValid: PropTypes.bool, - skipValid: PropTypes.bool, - limitValid: PropTypes.bool, - - autoPopulated: PropTypes.bool, - filterString: PropTypes.string, - projectString: PropTypes.string, - collationString: PropTypes.string, - sortString: PropTypes.string, - skipString: PropTypes.string, - limitString: PropTypes.string, - - filterPlaceholder: PropTypes.string, - projectPlaceholder: PropTypes.string, - collationPlaceholder: PropTypes.string, - sortPlaceholder: PropTypes.string, - skipPlaceholder: PropTypes.string, - limitPlaceholder: PropTypes.string, - maxTimeMSPlaceholder: PropTypes.string, - - actions: PropTypes.object, - buttonLabel: PropTypes.string, - queryState: PropTypes.string, - serverVersion: PropTypes.string, - layout: PropTypes.array, - expanded: PropTypes.bool, - lastExecutedQuery: PropTypes.object, - onReset: PropTypes.func, - onApply: PropTypes.func, - resultId: PropTypes.number, - schemaFields: PropTypes.array, - showQueryHistoryButton: PropTypes.bool, - showExportToLanguageButton: PropTypes.bool, - }; - - static defaultProps = { - expanded: false, - buttonLabel: 'Apply', - layout: [ - 'filter', - 'project', - ['sort', 'maxTimeMS'], - ['collation', 'skip', 'limit'], - ], - schemaFields: [], - showQueryHistoryButton: true, - showExportToLanguageButton: true, - resultId: 0, - }; - - state = { - hasFocus: false, - }; - - onChange(label, evt) { - const type = OPTION_DEFINITION[label].type; - const { actions } = this.props; - - if (['numeric', 'document'].includes(type)) { - return actions.typeQueryString(label, evt.target.value); - } - if (type === 'boolean') { - // there is only one boolean toggle: sample - return actions.toggleSample(evt.target.checked); - } - } - - onApplyButtonClicked = (evt) => { - // No evt when pressing enter from ACE. - if (evt) { - evt.preventDefault(); - evt.stopPropagation(); - } - - this.props.onApply(); - }; - - onResetButtonClicked = () => { - this.props.onReset(); - }; - - getQueryOption( - label, - autoPopulated, - hasToggle, - hasError, - id, - value, - placeholder, - option - ) { - return ( - - ); - } - - _onFocus = () => { - this.setState({ hasFocus: true }); - }; - - _onBlur = () => { - this.setState({ hasFocus: false }); - }; - - _showToggle() { - return this.props.layout.length > 1; - } - - _queryHasChanges() { - const query = pick(this.props, QUERY_PROPERTIES); - return !isEqual(query, this.props.lastExecutedQuery); - } - - /** - * renders the options toggle button in the top right corner, if the layout - * is multi-line. - * - * @returns {Component|null} the toggle component or null. - */ - renderToggle() { - const { expanded, actions } = this.props; - - return this._showToggle() ? ( - - ) : null; - } - - /** - * renders a single query option, either as its own row, or as part of a - * option group. - * - * @param {String} option the option name to render - * @param {Number} id the option number - * @param {Boolean} hasToggle this option contains the expand toggle - * - * @return {Component} the option component - */ - renderOption(option, id, hasToggle) { - const { filterValid, autoPopulated } = this.props; - - // for filter only, also validate feature flag directives - const hasError = - option === 'filter' ? !filterValid : !this.props[`${option}Valid`]; - - // checkbox options use the value directly, text inputs use the - // `