(core) Add dropdown conditions

Summary:
Dropdown conditions let you specify a predicate formula that's used to filter
choices and references in their respective autocomplete dropdown menus.

Test Plan: Python and browser tests (WIP).

Reviewers: jarek, paulfitz

Reviewed By: jarek

Subscribers: dsagal, paulfitz

Differential Revision: https://phab.getgrist.com/D4235
This commit is contained in:
George Gevoian
2024-04-26 16:34:16 -04:00
parent 34c85757f1
commit 3112433a58
86 changed files with 4221 additions and 1060 deletions

View File

@@ -378,7 +378,12 @@ class BruteForceACIndexImpl<Item extends ACItem> implements ACIndex<Item> {
public search(searchText: string): ACResults<Item> {
const cleanedSearchText = searchText.trim().toLowerCase();
if (!cleanedSearchText) {
return {items: this._allItems.slice(0, this._maxResults), highlightFunc: highlightNone, selectIndex: -1};
return {
items: this._allItems.slice(0, this._maxResults),
extraItems: [],
highlightFunc: highlightNone,
selectIndex: -1,
};
}
const searchWords = cleanedSearchText.split(/\s+/);
@@ -397,7 +402,7 @@ class BruteForceACIndexImpl<Item extends ACItem> implements ACIndex<Item> {
matches.sort((a, b) => nativeCompare(b[0], a[0]) || nativeCompare(a[1], b[1]));
const items = matches.slice(0, this._maxResults).map((m) => m[2]);
return {items, highlightFunc: highlightNone, selectIndex: -1};
return {items, extraItems: [], highlightFunc: highlightNone, selectIndex: -1};
}
}

View File

@@ -3,13 +3,11 @@ import { Disposable } from 'app/client/lib/dispose';
import { ClientProcess, SafeBrowser } from 'app/client/lib/SafeBrowser';
import { LocalPlugin } from 'app/common/plugin';
import { PluginInstance } from 'app/common/PluginInstance';
import { GristLight } from 'app/common/themes/GristLight';
import { GristAPI, RPC_GRISTAPI_INTERFACE } from 'app/plugin/GristAPI';
import { Storage } from 'app/plugin/StorageAPI';
import { checkers } from 'app/plugin/TypeCheckers';
import { assert } from 'chai';
import { Rpc } from 'grain-rpc';
import { Computed } from 'grainjs';
import { noop } from 'lodash';
import { basename } from 'path';
import * as sinon from 'sinon';
@@ -188,7 +186,6 @@ describe('SafeBrowser', function() {
untrustedContentOrigin: '',
mainPath,
baseLogger: {},
theme: Computed.create(null, () => ({appearance: 'light', colors: GristLight})),
});
cleanup.push(() => safeBrowser.deactivate());
pluginInstance.rpc.registerForwarder(mainPath, safeBrowser);

View File

@@ -0,0 +1,30 @@
import {checkName} from 'app/client/lib/nameUtils';
import {assert} from 'chai';
describe("nameUtils", function() {
describe("isValidName", function() {
it("should detect invalid name", function() {
assert.equal(checkName('santa'), true);
assert.equal(checkName('_santa'), true);
assert.equal(checkName("O'Neil"), true);
assert.equal(checkName("Emily"), true);
assert.equal(checkName("santa(2)"), true);
assert.equal(checkName("Dr. noname"), true);
assert.equal(checkName("santa-klaus"), true);
assert.equal(checkName("Noémie"), true);
assert.equal(checkName("张伟"), true);
assert.equal(checkName(',,__()'), false);
assert.equal(checkName('<foo>'), false);
assert.equal(checkName('<foo>'), false);
assert.equal(checkName('(bar)'), false);
assert.equal(checkName('foo <baz>'), false);
assert.equal(checkName('-foo'), false);
assert.equal(checkName("'foo"), false);
assert.equal(checkName(' Bob'), false);
assert.equal(checkName('='), false);
assert.equal(checkName('santa='), false);
});
});
});

View File

@@ -0,0 +1,22 @@
import {getTimeFromNow} from 'app/client/lib/timeUtils';
import {assert} from 'chai';
import moment from 'moment';
describe("timeUtils", function() {
describe("getTimeFromNow", function() {
it("should give good summary of time that just passed", function() {
const t = moment().subtract(10, 's');
assert.equal(getTimeFromNow(t.toISOString()), 'a few seconds ago');
});
it("should gloss over times slightly in future", function() {
const t = moment().add(2, 's');
assert.equal(getTimeFromNow(t.toISOString()), 'a few seconds ago');
});
it("should not gloss over times further in future", function() {
const t = moment().add(2, 'minutes');
assert.equal(getTimeFromNow(t.toISOString()), 'in 2 minutes');
});
});
});