Skip to content

Commit b3629b0

Browse files
feat(query-bar): allow users to set the default sort to be recent first COMPASS-6706 (#6663)
* changes * changes * remove stray comments * copy * changes * changes * changes * changes * c * this works still * changes * c * c * changes * c * crud test * leafygreen * longer description * remove selectable values code * remove a selectable values ref * value : string not value: string | undefined * unit test fix in settings * fix unused var
1 parent 8f1ece5 commit b3629b0

File tree

7 files changed

+167
-13
lines changed

7 files changed

+167
-13
lines changed

packages/compass-crud/src/stores/crud-store.spec.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1663,7 +1663,8 @@ describe('store', function () {
16631663
const plugin = activatePlugin();
16641664
store = plugin.store;
16651665
deactivate = () => plugin.deactivate();
1666-
await dataService.insertOne('compass-crud.test', { name: 'testing' });
1666+
await dataService.insertOne('compass-crud.test', { name: 'testing1' });
1667+
await dataService.insertOne('compass-crud.test', { name: 'testing2' });
16671668
});
16681669

16691670
afterEach(function () {
@@ -1680,9 +1681,36 @@ describe('store', function () {
16801681

16811682
(state) => {
16821683
expect(state.error).to.equal(null);
1683-
expect(state.docs).to.have.length(1);
1684+
expect(state.docs).to.have.length(2);
1685+
expect(state.docs[0].doc.name).to.equal('testing1');
16841686
expect(state.debouncingLoad).to.equal(false);
1685-
expect(state.count).to.equal(1);
1687+
expect(state.count).to.equal(2);
1688+
expect(state.start).to.equal(1);
1689+
expect(state.shardKeys).to.deep.equal({});
1690+
},
1691+
]);
1692+
1693+
void store.refreshDocuments();
1694+
1695+
await listener;
1696+
});
1697+
1698+
it('uses the sort order from preferences', async function () {
1699+
await preferences.savePreferences({
1700+
defaultSortOrder: '{ _id: -1 }',
1701+
});
1702+
const listener = waitForStates(store, [
1703+
(state) => {
1704+
expect(state.debouncingLoad).to.equal(true);
1705+
expect(state.count).to.equal(null);
1706+
},
1707+
1708+
(state) => {
1709+
expect(state.error).to.equal(null);
1710+
expect(state.docs).to.have.length(2);
1711+
expect(state.docs[0].doc.name).to.equal('testing2');
1712+
expect(state.debouncingLoad).to.equal(false);
1713+
expect(state.count).to.equal(2);
16861714
expect(state.start).to.equal(1);
16871715
expect(state.shardKeys).to.deep.equal({});
16881716
},

packages/compass-crud/src/stores/crud-store.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import semver from 'semver';
66
import StateMixin from '@mongodb-js/reflux-state-mixin';
77
import type { Element } from 'hadron-document';
88
import { Document } from 'hadron-document';
9+
import { validate } from 'mongodb-query-parser';
910
import HadronDocument from 'hadron-document';
1011
import _parseShellBSON, { ParseMode } from '@mongodb-js/shell-bson-parser';
1112
import type { PreferencesAccess } from 'compass-preferences-model/provider';
@@ -1615,8 +1616,16 @@ class CrudStoreImpl
16151616
countOptions.hint = '_id_';
16161617
}
16171618

1619+
let sort = query.sort;
1620+
if (!sort && this.preferences.getPreferences().defaultSortOrder) {
1621+
sort = validate(
1622+
'sort',
1623+
this.preferences.getPreferences().defaultSortOrder
1624+
);
1625+
}
1626+
16181627
const findOptions = {
1619-
sort: query.sort,
1628+
sort,
16201629
projection: query.project,
16211630
skip: query.skip,
16221631
limit: docsPerPage,

packages/compass-preferences-model/src/preferences-schema.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ import {
1717
export const THEMES_VALUES = ['DARK', 'LIGHT', 'OS_THEME'] as const;
1818
export type THEMES = typeof THEMES_VALUES[number];
1919

20+
export const SORT_ORDER_VALUES = [
21+
'',
22+
'{ $natural: -1 }',
23+
'{ _id: 1 }',
24+
'{ _id: -1 }',
25+
] as const;
26+
export type SORT_ORDERS = typeof SORT_ORDER_VALUES[number];
27+
2028
export type PermanentFeatureFlags = {
2129
showDevFeatureFlags?: boolean;
2230
enableDebugUseCsfleSchemaMap?: boolean;
@@ -69,6 +77,7 @@ export type UserConfigurablePreferences = PermanentFeatureFlags &
6977
enableGenAISampleDocumentPassing: boolean;
7078
enablePerformanceAdvisorBanner: boolean;
7179
maximumNumberOfActiveConnections?: number;
80+
defaultSortOrder: SORT_ORDERS;
7281
enableShowDialogOnQuit: boolean;
7382
enableCreatingNewConnections: boolean;
7483
enableProxySupport: boolean;
@@ -187,7 +196,13 @@ type PreferenceDefinition<K extends keyof AllPreferences> = {
187196
/** A description used for the --help text and the Settings UI */
188197
description: K extends keyof InternalUserPreferences
189198
? null
190-
: { short: string; long?: string };
199+
: {
200+
short: string;
201+
long?: string;
202+
options?: AllPreferences[K] extends string
203+
? { [k in AllPreferences[K]]: { label: string; description: string } }
204+
: never;
205+
};
191206
/** A method for deriving the current semantic value of this option, even if it differs from the stored value */
192207
deriveValue?: DeriveValueFunction<AllPreferences[K]>;
193208
/** A method for cleaning up/normalizing input from the command line or global config file */
@@ -199,6 +214,7 @@ type PreferenceDefinition<K extends keyof AllPreferences> = {
199214
? boolean
200215
: false
201216
: boolean;
217+
202218
validator: z.Schema<
203219
AllPreferences[K],
204220
z.ZodTypeDef,
@@ -537,6 +553,39 @@ export const storedUserPreferencesProps: Required<{
537553
validator: z.boolean().default(false),
538554
type: 'boolean',
539555
},
556+
/**
557+
* Set the default sort.
558+
*/
559+
defaultSortOrder: {
560+
ui: true,
561+
cli: true,
562+
global: true,
563+
description: {
564+
short: 'Default Sort for Query Bar',
565+
long: "All queries executed from the query bar will apply the sort order '$natural: -1'.",
566+
options: {
567+
'': {
568+
label: '$natural: 1 (MongoDB server default)',
569+
description: 'in natural order of documents',
570+
},
571+
'{ $natural: -1 }': {
572+
label: '$natural: -1',
573+
description: 'in reverse natural order of documents',
574+
},
575+
'{ _id: 1 }': {
576+
label: '_id: 1',
577+
description: 'in ascending order by id',
578+
},
579+
'{ _id: -1 }': {
580+
label: '_id: -1',
581+
description: 'in descending order by id',
582+
},
583+
},
584+
},
585+
validator: z.enum(SORT_ORDER_VALUES).default(''),
586+
type: 'string',
587+
},
588+
540589
/**
541590
* Switch to enable DevTools in Electron.
542591
*/

packages/compass-preferences-model/src/provider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ export {
1010
} from './utils';
1111
export { capMaxTimeMSAtPreferenceLimit } from './maxtimems';
1212
export { featureFlags } from './feature-flags';
13-
export { getSettingDescription } from './preferences-schema';
14-
export type { AllPreferences } from './preferences-schema';
13+
export { getSettingDescription, SORT_ORDER_VALUES } from './preferences-schema';
14+
export type { AllPreferences, SORT_ORDERS } from './preferences-schema';
1515
export type { DevtoolsProxyOptions } from '@mongodb-js/devtools-proxy-support';

packages/compass-settings/src/components/settings/general.spec.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ describe('GeneralSettings', function () {
5555
});
5656
});
5757

58+
it('renders defaultSortOrder', function () {
59+
expect(within(container).getByTestId('defaultSortOrder')).to.exist;
60+
});
61+
62+
it('changes defaultSortOrder value when selecting an option', function () {
63+
within(container).getByTestId('defaultSortOrder').click();
64+
within(container).getByText('_id: 1').click();
65+
expect(getSettings()).to.have.property('defaultSortOrder', '{ _id: 1 }');
66+
});
67+
5868
['maxTimeMS'].forEach((option) => {
5969
it(`renders ${option}`, function () {
6070
expect(within(container).getByTestId(option)).to.exist;

packages/compass-settings/src/components/settings/general.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const generalFields = [
55
'readOnly',
66
'enableShell',
77
'protectConnectionStrings',
8+
'defaultSortOrder',
89
'showKerberosPasswordField',
910
'maxTimeMS',
1011
'enableDevTools',

packages/compass-settings/src/components/settings/settings-list.tsx

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
getSettingDescription,
55
featureFlags,
66
} from 'compass-preferences-model/provider';
7+
import { SORT_ORDER_VALUES } from 'compass-preferences-model/provider';
78
import { settingStateLabels } from './state-labels';
89
import {
910
Checkbox,
@@ -12,6 +13,8 @@ import {
1213
css,
1314
spacing,
1415
TextInput,
16+
Select,
17+
Option,
1518
FormFieldContainer,
1619
Badge,
1720
} from '@mongodb-js/compass-components';
@@ -157,6 +160,55 @@ function NumericSetting<PreferenceName extends NumericPreferences>({
157160
);
158161
}
159162

163+
function DefaultSortOrderSetting<PreferenceName extends 'defaultSortOrder'>({
164+
name,
165+
onChange,
166+
value,
167+
disabled,
168+
}: {
169+
name: PreferenceName;
170+
onChange: HandleChange<PreferenceName>;
171+
value: string;
172+
disabled: boolean;
173+
}) {
174+
const optionDescriptions = getSettingDescription(name).description.options;
175+
const onChangeCallback = useCallback(
176+
(value: string) => {
177+
onChange(name, value as UserConfigurablePreferences[PreferenceName]);
178+
},
179+
[name, onChange]
180+
);
181+
182+
return (
183+
<>
184+
<SettingLabel name={name} />
185+
<Select
186+
className={inputStyles}
187+
allowDeselect={false}
188+
aria-labelledby={`${name}-label`}
189+
id={name}
190+
name={name}
191+
data-testid={name}
192+
value={value}
193+
onChange={onChangeCallback}
194+
disabled={disabled}
195+
>
196+
{SORT_ORDER_VALUES.map((option) => (
197+
<Option
198+
key={option}
199+
value={option}
200+
description={
201+
optionDescriptions && optionDescriptions[option].description
202+
}
203+
>
204+
{optionDescriptions && optionDescriptions[option].label}
205+
</Option>
206+
))}
207+
</Select>
208+
</>
209+
);
210+
}
211+
160212
function StringSetting<PreferenceName extends StringPreferences>({
161213
name,
162214
onChange,
@@ -263,9 +315,16 @@ function SettingsInput({
263315
disabled={!!disabled}
264316
/>
265317
);
266-
}
267-
268-
if (type === 'number') {
318+
} else if (type === 'string' && name === 'defaultSortOrder') {
319+
input = (
320+
<DefaultSortOrderSetting
321+
name={name}
322+
onChange={onChange}
323+
value={value as string}
324+
disabled={!!disabled}
325+
/>
326+
);
327+
} else if (type === 'number') {
269328
input = (
270329
<NumericSetting
271330
name={name}
@@ -275,9 +334,7 @@ function SettingsInput({
275334
disabled={!!disabled}
276335
/>
277336
);
278-
}
279-
280-
if (type === 'string') {
337+
} else if (type === 'string') {
281338
input = (
282339
<StringSetting
283340
name={name}

0 commit comments

Comments
 (0)