From 0f2067aa2696be5d204153d6d3ec9f3a3bf4f1b3 Mon Sep 17 00:00:00 2001 From: Basit Chonka Date: Thu, 23 Jan 2025 12:04:27 +0100 Subject: [PATCH 1/5] retain current query when new query is applied from autocomplete --- .../src/components/option-editor.tsx | 106 +++++++++++++----- .../src/stores/query-bar-reducer.ts | 11 +- 2 files changed, 85 insertions(+), 32 deletions(-) diff --git a/packages/compass-query-bar/src/components/option-editor.tsx b/packages/compass-query-bar/src/components/option-editor.tsx index db74d34b930..7027db11e9c 100644 --- a/packages/compass-query-bar/src/components/option-editor.tsx +++ b/packages/compass-query-bar/src/components/option-editor.tsx @@ -27,9 +27,19 @@ import type { RootState } from '../stores/query-bar-store'; import { useAutocompleteFields } from '@mongodb-js/compass-field-store'; import { applyFromHistory } from '../stores/query-bar-reducer'; import { getQueryAttributes } from '../utils'; -import type { BaseQuery, QueryFormFields } from '../constants/query-properties'; +import type { + BaseQuery, + QueryFormFields, + QueryProperty, +} from '../constants/query-properties'; +import { QUERY_PROPERTIES } from '../constants/query-properties'; import { mapQueryToFormFields } from '../utils/query'; import { DEFAULT_FIELD_VALUES } from '../constants/query-bar-store'; +import type { + FavoriteQuery, + RecentQuery, +} from '@mongodb-js/my-queries-storage'; +import _ from 'lodash'; const editorContainerStyles = css({ position: 'relative', @@ -87,8 +97,9 @@ const insightsBadgeStyles = css({ flex: 'none', }); +type OptionEditorName = Exclude; type OptionEditorProps = { - optionName: string; + optionName: OptionEditorName; namespace: string; id?: string; hasError?: boolean; @@ -107,7 +118,7 @@ type OptionEditorProps = { insights?: Signal | Signal[]; disabled?: boolean; savedQueries: SavedQuery[]; - onApplyQuery: (query: BaseQuery) => void; + onApplyQuery: (query: BaseQuery, fieldsToPreserve: QueryProperty[]) => void; }; export const OptionEditor: React.FunctionComponent = ({ @@ -166,27 +177,19 @@ export const OptionEditor: React.FunctionComponent = ({ return isQueryHistoryAutocompleteEnabled ? createQueryWithHistoryAutocompleter({ queryProperty: optionName, - savedQueries: savedQueries - .filter((query) => { - const isOptionNameInQuery = - optionName === 'filter' || optionName in query.queryProperties; - const isUpdateNotInQuery = !('update' in query.queryProperties); - return isOptionNameInQuery && isUpdateNotInQuery; - }) - .map((query) => ({ - type: query.type, - lastExecuted: query.lastExecuted, - queryProperties: query.queryProperties, - })) - .sort( - (a, b) => a.lastExecuted.getTime() - b.lastExecuted.getTime() - ), + savedQueries, options: { fields: schemaFields, serverVersion, }, onApply: (query: SavedQuery['queryProperties']) => { - onApplyQuery(query); + // When we are applying a query from `filter` field, we want to apply the whole query, + // otherwise we want to preserve the other fields that are already in the current query. + const fieldsToPreserve = + optionName === 'filter' + ? [] + : QUERY_PROPERTIES.filter((x) => x !== optionName); + onApplyQuery(query, fieldsToPreserve); if (!query[optionName]) { return; } @@ -295,20 +298,61 @@ export const OptionEditor: React.FunctionComponent = ({ ); }; -const ConnectedOptionEditor = (state: RootState) => ({ +export function getOptionBasedQueries( + optionName: OptionEditorName, + type: 'recent' | 'favorite', + queries: (RecentQuery | FavoriteQuery)[] +) { + return ( + queries + .map((query) => ({ + type, + lastExecuted: query._lastExecuted, + // For query that's being autocompeted from the main `filter`, we want to + // show whole query to the user, so that when its applied, it will replace + // the whole query (filter, project, sort etc). + // For other options, we only want to show the query for that specific option. + queryProperties: getQueryAttributes( + optionName !== 'filter' ? { [optionName]: query[optionName] } : query + ), + })) + // Filter the query if: + // - its empty + // - its an `update` query + // - its a duplicate + .filter((query, i, arr) => { + const queryIsUpdate = _.has(query.queryProperties, 'update'); + const queryIsEmpty = _.isEmpty(query.queryProperties); + if (queryIsEmpty || queryIsUpdate) { + return false; + } + return ( + i === + arr.findIndex( + (t) => + JSON.stringify(t.queryProperties) === + JSON.stringify(query.queryProperties) + ) + ); + }) + .sort((a, b) => a.lastExecuted.getTime() - b.lastExecuted.getTime()) + ); +} + +const mapStateToProps = (state: RootState, ownProps: OptionEditorProps) => ({ namespace: state.queryBar.namespace, serverVersion: state.queryBar.serverVersion, savedQueries: [ - ...state.queryBar.recentQueries.map((query) => ({ - type: 'recent', - lastExecuted: query._lastExecuted, - queryProperties: getQueryAttributes(query), - })), - ...state.queryBar.favoriteQueries.map((query) => ({ - type: 'favorite', - lastExecuted: query._lastExecuted, - queryProperties: getQueryAttributes(query), - })), + ...getOptionBasedQueries( + ownProps.optionName, + 'recent', + state.queryBar.recentQueries + ), + ...getOptionBasedQueries( + ownProps.optionName, + 'favorite', + state.queryBar.favoriteQueries + ), ], }); @@ -316,4 +360,4 @@ const mapDispatchToProps = { onApplyQuery: applyFromHistory, }; -export default connect(ConnectedOptionEditor, mapDispatchToProps)(OptionEditor); +export default connect(mapStateToProps, mapDispatchToProps)(OptionEditor); diff --git a/packages/compass-query-bar/src/stores/query-bar-reducer.ts b/packages/compass-query-bar/src/stores/query-bar-reducer.ts index d57ddde9463..7477affc85a 100644 --- a/packages/compass-query-bar/src/stores/query-bar-reducer.ts +++ b/packages/compass-query-bar/src/stores/query-bar-reducer.ts @@ -224,14 +224,23 @@ type ApplyFromHistoryAction = { }; export const applyFromHistory = ( - query: BaseQuery & { update?: Document } + query: BaseQuery & { update?: Document }, + currentQueryFieldsToRetain: QueryProperty[] = [] ): QueryBarThunkAction => { return (dispatch, getState, { localAppRegistry, preferences }) => { + const currentFields = getState().queryBar.fields; + const currentQuery = currentQueryFieldsToRetain.reduce((acc, key) => { + if (currentFields[key]?.value) { + acc[key] = currentFields[key].value; + } + return acc; + }, {} as Record); const fields = mapQueryToFormFields( { maxTimeMS: preferences.getPreferences().maxTimeMS }, { ...DEFAULT_FIELD_VALUES, ...query, + ...currentQuery, } ); dispatch({ From 2fb87f6d89faf9468dda90b8d2f059a91df1d5fa Mon Sep 17 00:00:00 2001 From: Basit Chonka Date: Thu, 23 Jan 2025 13:41:46 +0100 Subject: [PATCH 2/5] tests --- .../src/components/option-editor.spec.tsx | 162 ++++++++++++------ 1 file changed, 111 insertions(+), 51 deletions(-) diff --git a/packages/compass-query-bar/src/components/option-editor.spec.tsx b/packages/compass-query-bar/src/components/option-editor.spec.tsx index d8e15fa5961..f7b5e67b9e8 100644 --- a/packages/compass-query-bar/src/components/option-editor.spec.tsx +++ b/packages/compass-query-bar/src/components/option-editor.spec.tsx @@ -7,7 +7,7 @@ import { waitFor, userEvent, } from '@mongodb-js/testing-library-compass'; -import { OptionEditor } from './option-editor'; +import { OptionEditor, getOptionBasedQueries } from './option-editor'; import type { SinonSpy } from 'sinon'; import { applyFromHistory } from '../stores/query-bar-reducer'; import sinon from 'sinon'; @@ -167,7 +167,7 @@ describe('OptionEditor', function () { }); }); - describe('when render filter bar with the query history autocompleter', function () { + describe('when rendering filter option', function () { let onApplySpy: SinonSpy; let preferencesAccess: PreferencesAccess; @@ -198,48 +198,42 @@ describe('OptionEditor', function () { sort: { a: -1 }, }, }, - { - type: 'recent', - lastExecuted: new Date(), - queryProperties: { - filter: { a: 2 }, - sort: { a: -1 }, - update: { a: 10 }, - }, - }, ]} onApplyQuery={onApplySpy} /> ); + userEvent.click(screen.getByRole('textbox')); + await waitFor(() => { + screen.getByLabelText('Completions'); + }); }); afterEach(function () { cleanup(); }); - it('filter applied correctly when autocomplete option is clicked', async function () { - userEvent.click(screen.getByRole('textbox')); - await waitFor(() => { - expect(screen.getAllByText('{ a: 1 }')[0]).to.be.visible; - expect(screen.getByText('{ a: 2 }, sort: { a: -1 }')).to.be.visible; - expect( - screen.queryByText('{ a: 2 }, sort: { a: -1 }, update: { a: 10 }') - ).to.be.null; - }); + it('renders autocomplete options', function () { + expect(screen.getAllByText('{ a: 1 }')[0]).to.be.visible; + expect(screen.getByText('{ a: 2 }, sort: { a: -1 }')).to.be.visible; + }); + it('calls onApply with correct params', async function () { // Simulate selecting the autocomplete option userEvent.click(screen.getByText('{ a: 2 }, sort: { a: -1 }')); await waitFor(() => { - expect(onApplySpy.lastCall).to.be.calledWithExactly({ - filter: { a: 2 }, - sort: { a: -1 }, - }); + expect(onApplySpy.lastCall).to.be.calledWithExactly( + { + filter: { a: 2 }, + sort: { a: -1 }, + }, + [] + ); }); }); }); - describe('when render project bar with the query history autocompleter', function () { + describe('when rendering project option', function () { let onApplySpy: SinonSpy; let preferencesAccess: PreferencesAccess; @@ -262,20 +256,10 @@ describe('OptionEditor', function () { project: { a: 1 }, }, }, - { - type: 'favorite', - lastExecuted: new Date(), - queryProperties: { - filter: { a: 2 }, - sort: { a: -1 }, - }, - }, { type: 'recent', lastExecuted: new Date(), queryProperties: { - filter: { a: 2 }, - sort: { a: -1 }, project: { a: 0 }, }, }, @@ -284,32 +268,108 @@ describe('OptionEditor', function () { /> ); + userEvent.click(screen.getByRole('textbox')); + await waitFor(() => { + screen.getByLabelText('Completions'); + }); }); afterEach(function () { cleanup(); }); - it('only queries with project property are shown in project editor', async function () { - userEvent.click(screen.getByRole('textbox')); - await waitFor(() => { - expect(screen.getAllByText('project: { a: 1 }')[0]).to.be.visible; - expect(screen.queryByText('{ a: 2 }, sort: { a: -1 }')).to.be.null; - expect(screen.getByText('{ a: 2 }, sort: { a: -1 }, project: { a: 0 }')) - .to.be.visible; - }); + it('renders autocomplete options', function () { + expect(screen.getAllByText('project: { a: 1 }')[0]).to.be.visible; + expect(screen.getAllByText('project: { a: 0 }')[0]).to.be.visible; + }); + it('calls onApply with correct params', async function () { // Simulate selecting the autocomplete option - userEvent.click( - screen.getByText('{ a: 2 }, sort: { a: -1 }, project: { a: 0 }') - ); + userEvent.click(screen.getByText('project: { a: 0 }')); await waitFor(() => { - expect(onApplySpy.lastCall).to.be.calledWithExactly({ - filter: { a: 2 }, - sort: { a: -1 }, - project: { a: 0 }, - }); + expect(onApplySpy).to.have.been.calledOnceWithExactly( + { + project: { a: 0 }, + }, + ['filter', 'collation', 'sort', 'hint', 'skip', 'limit', 'maxTimeMS'] + ); }); }); }); + + describe('getOptionBasedQueries', function () { + const savedQueries = [ + { + _lastExecuted: new Date(), + filter: { a: 1 }, + project: { b: 1 }, + sort: { c: 1 }, + collation: { locale: 'en' }, + hint: { a: 1 }, + skip: 1, + limit: 1, + }, + ]; + + it('filters out update queries', function () { + const queries = getOptionBasedQueries('filter', 'recent', [ + ...savedQueries, + { _lastExecuted: new Date(), update: { a: 1 }, filter: { a: 2 } }, + ]); + expect(queries.length).to.equal(1); + }); + + it('filters out empty queries', function () { + const queries = getOptionBasedQueries('filter', 'recent', [ + ...savedQueries, + { _lastExecuted: new Date() }, + ]); + expect(queries.length).to.equal(1); + }); + + it('filters out duplicate queries', function () { + const queries = getOptionBasedQueries('filter', 'recent', [ + ...savedQueries, + ...savedQueries, + ...savedQueries, + { _lastExecuted: new Date() }, + { _lastExecuted: new Date() }, + ]); + expect(queries.length).to.equal(1); + }); + + const optionNames = [ + 'filter', + 'project', + 'sort', + 'collation', + 'hint', + ] as const; + for (const name of optionNames) { + it(`maps query for ${name}`, function () { + const queries = getOptionBasedQueries(name, 'recent', savedQueries); + + // For filter, we include all the query properties and for the rest + // we only include that specific option. + const queryProperties = + name === 'filter' + ? Object.fromEntries( + Object.entries(savedQueries[0]).filter( + ([key]) => key !== '_lastExecuted' + ) + ) + : { + [name]: savedQueries[0][name], + }; + + expect(queries).to.deep.equal([ + { + type: 'recent', + lastExecuted: savedQueries[0]._lastExecuted, + queryProperties, + }, + ]); + }); + } + }); }); From 188afe756bbf28760d5b5bfb768f022a0040ae9c Mon Sep 17 00:00:00 2001 From: Basit Chonka Date: Thu, 23 Jan 2025 21:00:25 +0100 Subject: [PATCH 3/5] fix up --- .../src/components/option-editor.spec.tsx | 9 +++++---- .../src/components/option-editor.tsx | 11 +++++++---- .../compass-query-bar/src/components/query-option.tsx | 7 +++++-- .../src/constants/query-option-definition.ts | 4 ++++ .../compass-query-bar/src/stores/query-bar-reducer.ts | 11 +++++++---- 5 files changed, 28 insertions(+), 14 deletions(-) diff --git a/packages/compass-query-bar/src/components/option-editor.spec.tsx b/packages/compass-query-bar/src/components/option-editor.spec.tsx index f7b5e67b9e8..dd0773c554e 100644 --- a/packages/compass-query-bar/src/components/option-editor.spec.tsx +++ b/packages/compass-query-bar/src/components/option-editor.spec.tsx @@ -14,6 +14,7 @@ import sinon from 'sinon'; import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; import type { PreferencesAccess } from 'compass-preferences-model'; import { PreferencesProvider } from 'compass-preferences-model/provider'; +import type { RecentQuery } from '@mongodb-js/my-queries-storage'; class MockPasteEvent extends window.Event { constructor(private text: string) { @@ -309,13 +310,13 @@ describe('OptionEditor', function () { skip: 1, limit: 1, }, - ]; + ] as unknown as RecentQuery[]; it('filters out update queries', function () { const queries = getOptionBasedQueries('filter', 'recent', [ ...savedQueries, { _lastExecuted: new Date(), update: { a: 1 }, filter: { a: 2 } }, - ]); + ] as unknown as RecentQuery[]); expect(queries.length).to.equal(1); }); @@ -323,7 +324,7 @@ describe('OptionEditor', function () { const queries = getOptionBasedQueries('filter', 'recent', [ ...savedQueries, { _lastExecuted: new Date() }, - ]); + ] as unknown as RecentQuery[]); expect(queries.length).to.equal(1); }); @@ -334,7 +335,7 @@ describe('OptionEditor', function () { ...savedQueries, { _lastExecuted: new Date() }, { _lastExecuted: new Date() }, - ]); + ] as unknown as RecentQuery[]); expect(queries.length).to.equal(1); }); diff --git a/packages/compass-query-bar/src/components/option-editor.tsx b/packages/compass-query-bar/src/components/option-editor.tsx index 7027db11e9c..2ee5a794d19 100644 --- a/packages/compass-query-bar/src/components/option-editor.tsx +++ b/packages/compass-query-bar/src/components/option-editor.tsx @@ -40,6 +40,7 @@ import type { RecentQuery, } from '@mongodb-js/my-queries-storage'; import _ from 'lodash'; +import type { QueryOptionOfTypeDocument } from '../constants/query-option-definition'; const editorContainerStyles = css({ position: 'relative', @@ -97,9 +98,8 @@ const insightsBadgeStyles = css({ flex: 'none', }); -type OptionEditorName = Exclude; type OptionEditorProps = { - optionName: OptionEditorName; + optionName: QueryOptionOfTypeDocument; namespace: string; id?: string; hasError?: boolean; @@ -299,7 +299,7 @@ export const OptionEditor: React.FunctionComponent = ({ }; export function getOptionBasedQueries( - optionName: OptionEditorName, + optionName: QueryOptionOfTypeDocument, type: 'recent' | 'favorite', queries: (RecentQuery | FavoriteQuery)[] ) { @@ -339,7 +339,10 @@ export function getOptionBasedQueries( ); } -const mapStateToProps = (state: RootState, ownProps: OptionEditorProps) => ({ +const mapStateToProps = ( + state: RootState, + ownProps: Pick +) => ({ namespace: state.queryBar.namespace, serverVersion: state.queryBar.serverVersion, savedQueries: [ diff --git a/packages/compass-query-bar/src/components/query-option.tsx b/packages/compass-query-bar/src/components/query-option.tsx index 429aeedf004..01a2208fd4c 100644 --- a/packages/compass-query-bar/src/components/query-option.tsx +++ b/packages/compass-query-bar/src/components/query-option.tsx @@ -12,7 +12,10 @@ import { import { connect } from '../stores/context'; import OptionEditor from './option-editor'; import { OPTION_DEFINITION } from '../constants/query-option-definition'; -import type { QueryOption as QueryOptionType } from '../constants/query-option-definition'; +import type { + QueryOptionOfTypeDocument, + QueryOption as QueryOptionType, +} from '../constants/query-option-definition'; import { changeField } from '../stores/query-bar-reducer'; import type { QueryProperty } from '../constants/query-properties'; import type { RootState } from '../stores/query-bar-store'; @@ -187,7 +190,7 @@ const QueryOption: React.FunctionComponent = ({
{isDocumentEditor ? ( ; +export type QueryOptionOfTypeDocument = Exclude< + QueryProperty, + 'maxTimeMS' | 'limit' | 'skip' +>; export const OPTION_DEFINITION: { [optionName in QueryOption]: { diff --git a/packages/compass-query-bar/src/stores/query-bar-reducer.ts b/packages/compass-query-bar/src/stores/query-bar-reducer.ts index 7477affc85a..0069a473753 100644 --- a/packages/compass-query-bar/src/stores/query-bar-reducer.ts +++ b/packages/compass-query-bar/src/stores/query-bar-reducer.ts @@ -229,12 +229,15 @@ export const applyFromHistory = ( ): QueryBarThunkAction => { return (dispatch, getState, { localAppRegistry, preferences }) => { const currentFields = getState().queryBar.fields; - const currentQuery = currentQueryFieldsToRetain.reduce((acc, key) => { - if (currentFields[key]?.value) { - acc[key] = currentFields[key].value; + const currentQuery = currentQueryFieldsToRetain.reduce< + Record + >((acc, key) => { + const { value } = currentFields[key]; + if (value) { + acc[key] = value; } return acc; - }, {} as Record); + }, {}); const fields = mapQueryToFormFields( { maxTimeMS: preferences.getPreferences().maxTimeMS }, { From ee1e6d8b3fe363d4b2204cb63cfc18f296ac581c Mon Sep 17 00:00:00 2001 From: Basit Chonka Date: Mon, 27 Jan 2025 10:05:13 +0100 Subject: [PATCH 4/5] remove usage of lodash --- packages/compass-query-bar/src/components/option-editor.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/compass-query-bar/src/components/option-editor.tsx b/packages/compass-query-bar/src/components/option-editor.tsx index 2ee5a794d19..bdcabc369a9 100644 --- a/packages/compass-query-bar/src/components/option-editor.tsx +++ b/packages/compass-query-bar/src/components/option-editor.tsx @@ -39,7 +39,6 @@ import type { FavoriteQuery, RecentQuery, } from '@mongodb-js/my-queries-storage'; -import _ from 'lodash'; import type { QueryOptionOfTypeDocument } from '../constants/query-option-definition'; const editorContainerStyles = css({ @@ -321,8 +320,8 @@ export function getOptionBasedQueries( // - its an `update` query // - its a duplicate .filter((query, i, arr) => { - const queryIsUpdate = _.has(query.queryProperties, 'update'); - const queryIsEmpty = _.isEmpty(query.queryProperties); + const queryIsUpdate = 'update' in query.queryProperties; + const queryIsEmpty = Object.keys(query.queryProperties).length === 0; if (queryIsEmpty || queryIsUpdate) { return false; } From 3d8cc61f1d93b9d7e1cc9a4515af82a323ebae29 Mon Sep 17 00:00:00 2001 From: Basit Chonka Date: Mon, 27 Jan 2025 13:28:54 +0100 Subject: [PATCH 5/5] memo map function --- .../src/components/option-editor.spec.tsx | 64 +++++++++---------- .../src/components/option-editor.tsx | 45 +++++++------ 2 files changed, 54 insertions(+), 55 deletions(-) diff --git a/packages/compass-query-bar/src/components/option-editor.spec.tsx b/packages/compass-query-bar/src/components/option-editor.spec.tsx index dd0773c554e..891532f2f84 100644 --- a/packages/compass-query-bar/src/components/option-editor.spec.tsx +++ b/packages/compass-query-bar/src/components/option-editor.spec.tsx @@ -14,7 +14,6 @@ import sinon from 'sinon'; import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; import type { PreferencesAccess } from 'compass-preferences-model'; import { PreferencesProvider } from 'compass-preferences-model/provider'; -import type { RecentQuery } from '@mongodb-js/my-queries-storage'; class MockPasteEvent extends window.Event { constructor(private text: string) { @@ -48,7 +47,8 @@ describe('OptionEditor', function () { insertEmptyDocOnFocus onChange={() => {}} value="" - savedQueries={[]} + recentQueries={[]} + favoriteQueries={[]} onApplyQuery={applyFromHistory} > ); @@ -70,7 +70,8 @@ describe('OptionEditor', function () { insertEmptyDocOnFocus onChange={() => {}} value="{ foo: 1 }" - savedQueries={[]} + recentQueries={[]} + favoriteQueries={[]} onApplyQuery={applyFromHistory} > ); @@ -92,7 +93,8 @@ describe('OptionEditor', function () { insertEmptyDocOnFocus onChange={() => {}} value="" - savedQueries={[]} + favoriteQueries={[]} + recentQueries={[]} onApplyQuery={applyFromHistory} > ); @@ -120,7 +122,8 @@ describe('OptionEditor', function () { insertEmptyDocOnFocus onChange={() => {}} value="" - savedQueries={[]} + favoriteQueries={[]} + recentQueries={[]} onApplyQuery={applyFromHistory} > ); @@ -150,7 +153,8 @@ describe('OptionEditor', function () { insertEmptyDocOnFocus onChange={() => {}} value="" - savedQueries={[]} + favoriteQueries={[]} + recentQueries={[]} onApplyQuery={applyFromHistory} > ); @@ -183,21 +187,17 @@ describe('OptionEditor', function () { insertEmptyDocOnFocus onChange={() => {}} value="" - savedQueries={[ + recentQueries={[ { - type: 'recent', - lastExecuted: new Date(), - queryProperties: { - filter: { a: 1 }, - }, + _lastExecuted: new Date(), + filter: { a: 1 }, }, + ]} + favoriteQueries={[ { - type: 'favorite', - lastExecuted: new Date(), - queryProperties: { - filter: { a: 2 }, - sort: { a: -1 }, - }, + _lastExecuted: new Date(), + filter: { a: 2 }, + sort: { a: -1 }, }, ]} onApplyQuery={onApplySpy} @@ -249,20 +249,16 @@ describe('OptionEditor', function () { insertEmptyDocOnFocus onChange={() => {}} value="" - savedQueries={[ + favoriteQueries={[ { - type: 'favorite', - lastExecuted: new Date(), - queryProperties: { - project: { a: 1 }, - }, + _lastExecuted: new Date(), + project: { a: 1 }, }, + ]} + recentQueries={[ { - type: 'recent', - lastExecuted: new Date(), - queryProperties: { - project: { a: 0 }, - }, + _lastExecuted: new Date(), + project: { a: 0 }, }, ]} onApplyQuery={onApplySpy} @@ -310,13 +306,13 @@ describe('OptionEditor', function () { skip: 1, limit: 1, }, - ] as unknown as RecentQuery[]; + ]; it('filters out update queries', function () { const queries = getOptionBasedQueries('filter', 'recent', [ ...savedQueries, { _lastExecuted: new Date(), update: { a: 1 }, filter: { a: 2 } }, - ] as unknown as RecentQuery[]); + ]); expect(queries.length).to.equal(1); }); @@ -324,7 +320,7 @@ describe('OptionEditor', function () { const queries = getOptionBasedQueries('filter', 'recent', [ ...savedQueries, { _lastExecuted: new Date() }, - ] as unknown as RecentQuery[]); + ]); expect(queries.length).to.equal(1); }); @@ -335,7 +331,7 @@ describe('OptionEditor', function () { ...savedQueries, { _lastExecuted: new Date() }, { _lastExecuted: new Date() }, - ] as unknown as RecentQuery[]); + ]); expect(queries.length).to.equal(1); }); @@ -365,9 +361,9 @@ describe('OptionEditor', function () { expect(queries).to.deep.equal([ { - type: 'recent', lastExecuted: savedQueries[0]._lastExecuted, queryProperties, + type: 'recent', }, ]); }); diff --git a/packages/compass-query-bar/src/components/option-editor.tsx b/packages/compass-query-bar/src/components/option-editor.tsx index bdcabc369a9..a008c9d3ce7 100644 --- a/packages/compass-query-bar/src/components/option-editor.tsx +++ b/packages/compass-query-bar/src/components/option-editor.tsx @@ -41,6 +41,11 @@ import type { } from '@mongodb-js/my-queries-storage'; import type { QueryOptionOfTypeDocument } from '../constants/query-option-definition'; +type AutoCompleteQuery = Partial & + Pick; +type AutoCompleteRecentQuery = AutoCompleteQuery; +type AutoCompleteFavoriteQuery = AutoCompleteQuery; + const editorContainerStyles = css({ position: 'relative', display: 'flex', @@ -116,7 +121,8 @@ type OptionEditorProps = { ['data-testid']?: string; insights?: Signal | Signal[]; disabled?: boolean; - savedQueries: SavedQuery[]; + recentQueries: AutoCompleteRecentQuery[]; + favoriteQueries: AutoCompleteFavoriteQuery[]; onApplyQuery: (query: BaseQuery, fieldsToPreserve: QueryProperty[]) => void; }; @@ -135,7 +141,8 @@ export const OptionEditor: React.FunctionComponent = ({ ['data-testid']: dataTestId, insights, disabled = false, - savedQueries, + recentQueries, + favoriteQueries, onApplyQuery, }) => { const showInsights = usePreference('showInsights'); @@ -172,6 +179,13 @@ export const OptionEditor: React.FunctionComponent = ({ const schemaFields = useAutocompleteFields(namespace); const maxTimeMSPreference = usePreference('maxTimeMS'); + const savedQueries = useMemo(() => { + return [ + ...getOptionBasedQueries(optionName, 'recent', recentQueries), + ...getOptionBasedQueries(optionName, 'favorite', favoriteQueries), + ]; + }, [optionName, recentQueries, favoriteQueries]); + const completer = useMemo(() => { return isQueryHistoryAutocompleteEnabled ? createQueryWithHistoryAutocompleter({ @@ -300,7 +314,7 @@ export const OptionEditor: React.FunctionComponent = ({ export function getOptionBasedQueries( optionName: QueryOptionOfTypeDocument, type: 'recent' | 'favorite', - queries: (RecentQuery | FavoriteQuery)[] + queries: (AutoCompleteRecentQuery | AutoCompleteFavoriteQuery)[] ) { return ( queries @@ -338,24 +352,13 @@ export function getOptionBasedQueries( ); } -const mapStateToProps = ( - state: RootState, - ownProps: Pick -) => ({ - namespace: state.queryBar.namespace, - serverVersion: state.queryBar.serverVersion, - savedQueries: [ - ...getOptionBasedQueries( - ownProps.optionName, - 'recent', - state.queryBar.recentQueries - ), - ...getOptionBasedQueries( - ownProps.optionName, - 'favorite', - state.queryBar.favoriteQueries - ), - ], +const mapStateToProps = ({ + queryBar: { namespace, serverVersion, recentQueries, favoriteQueries }, +}: RootState) => ({ + namespace, + serverVersion, + recentQueries, + favoriteQueries, }); const mapDispatchToProps = {