mirror of
				https://github.com/gristlabs/grist-core.git
				synced 2025-06-13 20:53:59 +00:00 
			
		
		
		
	(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
This commit is contained in:
		
							parent
							
								
									f74002fe32
								
							
						
					
					
						commit
						a685707d50
					
				@ -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": [
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
    # 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})
 | 
			
		||||
 | 
			
		||||
    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})
 | 
			
		||||
    # 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.
 | 
			
		||||
 | 
			
		||||
@ -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<number>(`
 | 
			
		||||
    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<void>) {
 | 
			
		||||
export function revertChanges(test: () => Promise<void>, 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);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user