From a685707d50af69902cbc90645024f6e99f526a62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Sadzi=C5=84ski?= Date: Tue, 25 Jan 2022 10:45:54 +0100 Subject: [PATCH] (core) Renaming filters for choice columns Summary: Updating filters when user renames labels in a choice/choice list column. When there are unsaved filters they are reverted to orginal values (only for the affected column). Test Plan: new tests Reviewers: alexmojaki Reviewed By: alexmojaki Differential Revision: https://phab.getgrist.com/D3230 --- sandbox/grist/test_useractions.py | 33 ++++++++++++++++++++++++++++ sandbox/grist/useractions.py | 36 +++++++++++++++++++++++++------ test/nbrowser/gristUtils.ts | 33 +++++++++++++++++++++++----- 3 files changed, 90 insertions(+), 12 deletions(-) diff --git a/sandbox/grist/test_useractions.py b/sandbox/grist/test_useractions.py index 87118ede..818d90a0 100644 --- a/sandbox/grist/test_useractions.py +++ b/sandbox/grist/test_useractions.py @@ -1,3 +1,4 @@ +import json import types import logger import useractions @@ -860,6 +861,38 @@ class TestUserActions(test_engine.EngineTestCase): [12, [[]]], ]) + # Test filters rename + + # Create new view section + self.apply_user_action(["CreateViewSection", 1, 0, "record", None]) + + # Filter it by first column + self.apply_user_action(['BulkAddRecord', '_grist_Filters', [None], { + "viewSectionRef": [1], + "colRef": [1], + "filter": [json.dumps({"included": ["b", "c"]})] + }]) + + # Add the same filter for second column (to make sure it is not renamed) + self.apply_user_action(['BulkAddRecord', '_grist_Filters', [None], { + "viewSectionRef": [1], + "colRef": [2], + "filter": [json.dumps({"included": ["b", "c"]})] + }]) + + # Rename choices + renames = {"b": "z", "c": "b"} + self.apply_user_action( + ["RenameChoices", "ChoiceTable", "ChoiceColumn", renames]) + + # Test filters + self.assertTableData('_grist_Filters', data=[ + ["id", "colRef", "filter", "setAutoRemove", "viewSectionRef"], + [1, 1, json.dumps({"included": ["z", "b"]}), None, 1], + [2, 2, json.dumps({"included": ["b", "c"]}), None, 1] + ]) + + def test_reference_lookup(self): sample = testutil.parse_test_sample({ "SCHEMA": [ diff --git a/sandbox/grist/useractions.py b/sandbox/grist/useractions.py index 49db3cae..8891f0e1 100644 --- a/sandbox/grist/useractions.py +++ b/sandbox/grist/useractions.py @@ -1291,13 +1291,35 @@ class UserActions(object): table = self._engine.tables[table_id] col = table.get_column(col_id) - if col.is_formula(): - # We don't set the values of formula columns, they should just recalculate themselves - return None - - row_ids, values = col.rename_choices(renames) - values = [encode_object(v) for v in values] - return self.BulkUpdateRecord(table_id, row_ids, {col_id: values}) + # We don't set the values of formula columns, they should just recalculate themselves + if not col.is_formula(): + row_ids, values = col.rename_choices(renames) + values = [encode_object(v) for v in values] + self.BulkUpdateRecord(table_id, row_ids, {col_id: values}) + + # Helper to rename only string values + def rename(value): + return renames.get(value, value) if isinstance(value, six.string_types) else value + + # Rename filters + filters = self._engine.tables['_grist_Filters'] + colRef = self._docmodel.get_column_rec(table_id, col_id).id + col_filters = filters.filter_records(colRef=colRef) + row_ids = [] + values = [] + for rec in col_filters: + if not rec.filter: + continue + col_filter = json.loads(rec.filter) + new_filter = { + include_exclude: [rename(value) for value in values] + for include_exclude, values in col_filter.items() + } + if col_filter != new_filter: + row_ids.append(rec.id) + values.append(json.dumps(new_filter)) + if row_ids: + self.BulkUpdateRecord('_grist_Filters', row_ids, {"filter": values}) #---------------------------------------- # User actions on tables. diff --git a/test/nbrowser/gristUtils.ts b/test/nbrowser/gristUtils.ts index 29cbabbc..3ce3a07b 100644 --- a/test/nbrowser/gristUtils.ts +++ b/test/nbrowser/gristUtils.ts @@ -892,28 +892,33 @@ export async function undo(optCount: number = 1, optTimeout?: number) { /** * Returns a function to undo all user actions from a particular point in time. + * Optionally accepts a function which should return the same result before and after the test. */ -export async function begin() { +export async function begin(invariant: () => any = () => true) { const undoStackPointer = () => driver.executeScript(` return window.gristDocPageModel.gristDoc.get()._undoStack._pointer; `); const start = await undoStackPointer(); - return async () => undo(await undoStackPointer() - start); + const previous = await invariant(); + return async () => { + await undo(await undoStackPointer() - start); + assert.deepEqual(await invariant(), previous); + }; } /** * Simulates a transaction on the GristDoc. Use with cautions, as there is no guarantee it will undo correctly * in a case of failure. - * + * Optionally accepts a function which should return the same result before and after the test. * Example: * * it('should ...', revertChanges(async function() { * ... * })); */ -export function revertChanges(test: () => Promise) { +export function revertChanges(test: () => Promise, invariant: () => any = () => false) { return async function() { - const revert = await begin(); + const revert = await begin(invariant); try { await test(); } finally { @@ -1953,6 +1958,24 @@ export async function scrollActiveView(x: number, y: number) { await driver.sleep(10); // wait a bit for the scroll to happen (this is async operation in Grist). } +/** + * Filters a column in a Grid using the filter menu. + */ +export async function filterBy(col: IColHeader|string, save: boolean, values: (string|RegExp)[]) { + await openColumnMenu(col, 'Filter'); + // Select none at start + await driver.findContent('.test-filter-menu-bulk-action', /None/).click(); + for(const value of values) { + await driver.findContent('.test-filter-menu-list label', value).click(); + } + // Save filters + await driver.find('.test-filter-menu-apply-btn').click(); + if (save) { + await driver.find('.test-section-menu-small-btn-save').click(); + } + await waitForServer(); +} + } // end of namespace gristUtils stackWrapOwnMethods(gristUtils);