mirror of
				https://github.com/gristlabs/grist-core.git
				synced 2025-06-13 20:53:59 +00:00 
			
		
		
		
	(core) updates from grist-core
This commit is contained in:
		
						commit
						a822a5771c
					
				@ -7,6 +7,7 @@
 | 
				
			|||||||
import {isString} from 'app/client/lib/sessionObs';
 | 
					import {isString} from 'app/client/lib/sessionObs';
 | 
				
			||||||
import {DocModel} from 'app/client/models/DocModel';
 | 
					import {DocModel} from 'app/client/models/DocModel';
 | 
				
			||||||
import {ColumnRec} from 'app/client/models/entities/ColumnRec';
 | 
					import {ColumnRec} from 'app/client/models/entities/ColumnRec';
 | 
				
			||||||
 | 
					import {csvDecodeRow} from 'app/common/csvFormat';
 | 
				
			||||||
import * as gristTypes from 'app/common/gristTypes';
 | 
					import * as gristTypes from 'app/common/gristTypes';
 | 
				
			||||||
import {isFullReferencingType} from 'app/common/gristTypes';
 | 
					import {isFullReferencingType} from 'app/common/gristTypes';
 | 
				
			||||||
import * as gutil from 'app/common/gutil';
 | 
					import * as gutil from 'app/common/gutil';
 | 
				
			||||||
@ -175,7 +176,7 @@ export async function prepTransformColInfo(docModel: DocModel, origCol: ColumnRe
 | 
				
			|||||||
        for (let value of tableData.getColValues(sourceCol.colId()) || []) {
 | 
					        for (let value of tableData.getColValues(sourceCol.colId()) || []) {
 | 
				
			||||||
          if (value === null) { continue; }
 | 
					          if (value === null) { continue; }
 | 
				
			||||||
          value = String(decodeObject(value)).trim();
 | 
					          value = String(decodeObject(value)).trim();
 | 
				
			||||||
          const tags: unknown[] = (value.startsWith('[') && gutil.safeJsonParse(value, null)) || value.split(",");
 | 
					          const tags: unknown[] = (value.startsWith('[') && gutil.safeJsonParse(value, null)) || csvDecodeRow(value);
 | 
				
			||||||
          for (const tag of tags) {
 | 
					          for (const tag of tags) {
 | 
				
			||||||
            choices.add(String(tag).trim());
 | 
					            choices.add(String(tag).trim());
 | 
				
			||||||
            if (choices.size > 100) { break; }    // Don't suggest excessively many choices.
 | 
					            if (choices.size > 100) { break; }    // Don't suggest excessively many choices.
 | 
				
			||||||
 | 
				
			|||||||
@ -65,20 +65,66 @@ const getKeysFromFile = (filePath, fileName) => {
 | 
				
			|||||||
  return keys;
 | 
					  return keys;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// It is highly desirable to retain existing order, to not generate
 | 
				
			||||||
 | 
					// unnecessary merges/conflicts, so we do a specialized merge.
 | 
				
			||||||
 | 
					function merge(target, scanned) {
 | 
				
			||||||
 | 
					  let merges = 0;
 | 
				
			||||||
 | 
					  for (const key of Object.keys(scanned)) {
 | 
				
			||||||
 | 
					    if (!(key in target)) {
 | 
				
			||||||
 | 
					      console.log("Merging key", {key});
 | 
				
			||||||
 | 
					      target[key] = scanned[key];
 | 
				
			||||||
 | 
					      merges++;
 | 
				
			||||||
 | 
					    } else if (typeof target[key] === 'object') {
 | 
				
			||||||
 | 
					      merges += merge(target[key], scanned[key]);
 | 
				
			||||||
 | 
					    } else if (scanned[key] !== target[key]) {
 | 
				
			||||||
 | 
					      if (!key.endsWith('_one')) {
 | 
				
			||||||
 | 
					        console.log("Value difference", {key, value: target[key]});
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return merges;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Look for keys that are listed in json file but not found in source
 | 
				
			||||||
 | 
					// code. These may be stale and need deleting in weblate.
 | 
				
			||||||
 | 
					function reportUnrecognizedKeys(originalKeys, foundKeys) {
 | 
				
			||||||
 | 
					  let unknowns = 0;
 | 
				
			||||||
 | 
					  for (const section of Object.keys(originalKeys)) {
 | 
				
			||||||
 | 
					    if (!(section in foundKeys)) {
 | 
				
			||||||
 | 
					      console.log("Unknown section found", {section});
 | 
				
			||||||
 | 
					      unknowns++;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      for (const key of Object.keys(originalKeys[section])) {
 | 
				
			||||||
 | 
					        if (!(key in foundKeys[section])) {
 | 
				
			||||||
 | 
					          console.log("Unknown key found", {section, key});
 | 
				
			||||||
 | 
					          unknowns++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return unknowns;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function walkTranslation(dirs) {
 | 
					async function walkTranslation(dirs) {
 | 
				
			||||||
 | 
					  const originalKeys = _.cloneDeep(englishKeys);
 | 
				
			||||||
  for await (const p of walk(dirs)) {
 | 
					  for await (const p of walk(dirs)) {
 | 
				
			||||||
    const { name } = path.parse(p);
 | 
					    const { name } = path.parse(p);
 | 
				
			||||||
    if (p.endsWith('.map')) { continue; }
 | 
					    if (p.endsWith('.map')) { continue; }
 | 
				
			||||||
    getKeysFromFile(p, name);
 | 
					    getKeysFromFile(p, name);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  const keys = parser.get({ sort: true });
 | 
					  const keys = parser.get({ sort: true });
 | 
				
			||||||
  const newTranslations = _.merge(keys.en.translation, englishKeys);
 | 
					  const foundKeys = _.cloneDeep(keys.en.translation);
 | 
				
			||||||
 | 
					  const mergeCount = merge(englishKeys, sort(keys.en.translation));
 | 
				
			||||||
  await fs.promises.writeFile(
 | 
					  await fs.promises.writeFile(
 | 
				
			||||||
    "static/locales/en.client.json",
 | 
					    "static/locales/en.client.json",
 | 
				
			||||||
    JSON.stringify(sort(newTranslations), null, 2),
 | 
					    JSON.stringify(englishKeys, null, 4) + '\n',  // match weblate's default
 | 
				
			||||||
    "utf-8"
 | 
					    "utf-8"
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
  return keys;
 | 
					  // Now, print a report of unrecognized keys - candidates
 | 
				
			||||||
 | 
					  // for deletion in weblate.
 | 
				
			||||||
 | 
					  const unknownCount = reportUnrecognizedKeys(originalKeys, foundKeys);
 | 
				
			||||||
 | 
					  console.log(`Found ${unknownCount} unknown key(s).`);
 | 
				
			||||||
 | 
					  console.log(`Make ${mergeCount} merge(s).`);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
walkTranslation(["_build/app/client", ...process.argv.slice(2)]);
 | 
					walkTranslation(["_build/app/client", ...process.argv.slice(2)]);
 | 
				
			||||||
 | 
				
			|||||||
@ -36,7 +36,9 @@
 | 
				
			|||||||
        "Special Rules": "Special Rules",
 | 
					        "Special Rules": "Special Rules",
 | 
				
			||||||
        "Type a message...": "Type a message...",
 | 
					        "Type a message...": "Type a message...",
 | 
				
			||||||
        "User Attributes": "User Attributes",
 | 
					        "User Attributes": "User Attributes",
 | 
				
			||||||
        "View As": "View As"
 | 
					        "View As": "View As",
 | 
				
			||||||
 | 
					        "Seed rules": "Seed rules",
 | 
				
			||||||
 | 
					        "When adding table rules, automatically add a rule to grant OWNER full access.": "When adding table rules, automatically add a rule to grant OWNER full access."
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "AccountPage": {
 | 
					    "AccountPage": {
 | 
				
			||||||
        "API": "API",
 | 
					        "API": "API",
 | 
				
			||||||
@ -332,7 +334,8 @@
 | 
				
			|||||||
        "Add Column": "Add Column"
 | 
					        "Add Column": "Add Column"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "FilterBar": {
 | 
					    "FilterBar": {
 | 
				
			||||||
        "SearchColumns": "Search columns"
 | 
					        "SearchColumns": "Search columns",
 | 
				
			||||||
 | 
					        "Search Columns": "Search Columns"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "GridOptions": {
 | 
					    "GridOptions": {
 | 
				
			||||||
        "Grid Options": "Grid Options",
 | 
					        "Grid Options": "Grid Options",
 | 
				
			||||||
@ -761,5 +764,17 @@
 | 
				
			|||||||
    "NTextBox": {
 | 
					    "NTextBox": {
 | 
				
			||||||
        "false": "false",
 | 
					        "false": "false",
 | 
				
			||||||
        "true": "true"
 | 
					        "true": "true"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "ACLUsers": {
 | 
				
			||||||
 | 
					        "Example Users": "Example Users",
 | 
				
			||||||
 | 
					        "Users from table": "Users from table",
 | 
				
			||||||
 | 
					        "View As": "View As"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "TypeTransform": {
 | 
				
			||||||
 | 
					        "Apply": "Apply",
 | 
				
			||||||
 | 
					        "Cancel": "Cancel",
 | 
				
			||||||
 | 
					        "Preview": "Preview",
 | 
				
			||||||
 | 
					        "Revise": "Revise",
 | 
				
			||||||
 | 
					        "Update formula (Shift+Enter)": "Update formula (Shift+Enter)"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -447,7 +447,7 @@ describe('ChoiceList', function() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  it('should allow reasonable conversions between ChoiceList and other types', async function() {
 | 
					  it('should allow reasonable conversions between ChoiceList and other types', async function() {
 | 
				
			||||||
    await gu.enterGridRows({rowNum: 1, col: 'A'},
 | 
					    await gu.enterGridRows({rowNum: 1, col: 'A'},
 | 
				
			||||||
      [['Hello'], ['World'], [' Foo,Bar;Baz!,']]);
 | 
					      [['Hello'], ['World'], [' Foo,Bar;Baz!,"Qux, quux corge", "80\'s",']]);
 | 
				
			||||||
    await testTextChoiceListConversions();
 | 
					    await testTextChoiceListConversions();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -468,17 +468,17 @@ describe('ChoiceList', function() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Check that choices got populated.
 | 
					    // Check that choices got populated.
 | 
				
			||||||
    await driver.find('.test-right-tab-field').click();
 | 
					    await driver.find('.test-right-tab-field').click();
 | 
				
			||||||
    assert.deepEqual(await getChoiceLabels(), ['Hello', 'World', 'Foo', 'Bar;Baz!']);
 | 
					    assert.deepEqual(await getChoiceLabels(), ['Hello', 'World', 'Foo', 'Bar;Baz!', 'Qux, quux corge', '80\'s']);
 | 
				
			||||||
    assert.deepEqual(
 | 
					    assert.deepEqual(
 | 
				
			||||||
      await getChoiceColors(),
 | 
					      await getChoiceColors(),
 | 
				
			||||||
      [UNSET_FILL, UNSET_FILL, UNSET_FILL, UNSET_FILL]
 | 
					      [UNSET_FILL, UNSET_FILL, UNSET_FILL, UNSET_FILL, UNSET_FILL, UNSET_FILL]
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Check that the result contains the right tags.
 | 
					    // Check that the result contains the right tags.
 | 
				
			||||||
    assert.deepEqual(await gu.getVisibleGridCells({rowNums: [1, 2, 3], cols: ['A']}), [
 | 
					    assert.deepEqual(await gu.getVisibleGridCells({rowNums: [1, 2, 3], cols: ['A']}), [
 | 
				
			||||||
      'Hello',
 | 
					      'Hello',
 | 
				
			||||||
      'World',
 | 
					      'World',
 | 
				
			||||||
      'Foo\nBar;Baz!'
 | 
					      'Foo\nBar;Baz!\nQux, quux corge\n80\'s'
 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
    await gu.checkForErrors();
 | 
					    await gu.checkForErrors();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -494,17 +494,21 @@ describe('ChoiceList', function() {
 | 
				
			|||||||
      [
 | 
					      [
 | 
				
			||||||
        {fillColor: DEFAULT_FILL, textColor: DEFAULT_TEXT, ...VALID_CHOICE},
 | 
					        {fillColor: DEFAULT_FILL, textColor: DEFAULT_TEXT, ...VALID_CHOICE},
 | 
				
			||||||
        {fillColor: DEFAULT_FILL, textColor: DEFAULT_TEXT, ...VALID_CHOICE},
 | 
					        {fillColor: DEFAULT_FILL, textColor: DEFAULT_TEXT, ...VALID_CHOICE},
 | 
				
			||||||
 | 
					        {fillColor: DEFAULT_FILL, textColor: DEFAULT_TEXT, ...VALID_CHOICE},
 | 
				
			||||||
 | 
					        {fillColor: DEFAULT_FILL, textColor: DEFAULT_TEXT, ...VALID_CHOICE},
 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Open a cell to see the actual tags.
 | 
					    // Open a cell to see the actual tags.
 | 
				
			||||||
    await gu.getCell({rowNum: 3, col: 'A'}).click();
 | 
					    await gu.getCell({rowNum: 3, col: 'A'}).click();
 | 
				
			||||||
    await driver.sendKeys(Key.ENTER);
 | 
					    await driver.sendKeys(Key.ENTER);
 | 
				
			||||||
    assert.deepEqual(await getEditorTokens(), ['Foo', 'Bar;Baz!']);
 | 
					    assert.deepEqual(await getEditorTokens(), ['Foo', 'Bar;Baz!', 'Qux, quux corge', '80\'s']);
 | 
				
			||||||
    assert.deepEqual(await getEditorTokensIsInvalid(), [false, false]);
 | 
					    assert.deepEqual(await getEditorTokensIsInvalid(), [ false, false, false, false ]);
 | 
				
			||||||
    assert.deepEqual(
 | 
					    assert.deepEqual(
 | 
				
			||||||
      await getEditorTokenStyles(),
 | 
					      await getEditorTokenStyles(),
 | 
				
			||||||
      [
 | 
					      [
 | 
				
			||||||
 | 
					        {fillColor: DEFAULT_FILL, textColor: DEFAULT_TEXT, ...VALID_CHOICE},
 | 
				
			||||||
 | 
					        {fillColor: DEFAULT_FILL, textColor: DEFAULT_TEXT, ...VALID_CHOICE},
 | 
				
			||||||
        {fillColor: DEFAULT_FILL, textColor: DEFAULT_TEXT, ...VALID_CHOICE},
 | 
					        {fillColor: DEFAULT_FILL, textColor: DEFAULT_TEXT, ...VALID_CHOICE},
 | 
				
			||||||
        {fillColor: DEFAULT_FILL, textColor: DEFAULT_TEXT, ...VALID_CHOICE}
 | 
					        {fillColor: DEFAULT_FILL, textColor: DEFAULT_TEXT, ...VALID_CHOICE}
 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
@ -520,7 +524,7 @@ describe('ChoiceList', function() {
 | 
				
			|||||||
    assert.deepEqual(await gu.getVisibleGridCells({rowNums: [1, 2, 3], cols: ['A']}), [
 | 
					    assert.deepEqual(await gu.getVisibleGridCells({rowNums: [1, 2, 3], cols: ['A']}), [
 | 
				
			||||||
      'Hello',
 | 
					      'Hello',
 | 
				
			||||||
      'World',
 | 
					      'World',
 | 
				
			||||||
      'Foo, Bar;Baz!, hooray'
 | 
					      'Foo, Bar;Baz!, "Qux, quux corge", 80\'s, hooray'
 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Undo the cell change and both conversions (back to ChoiceList, back to Text), and check
 | 
					    // Undo the cell change and both conversions (back to ChoiceList, back to Text), and check
 | 
				
			||||||
@ -529,7 +533,7 @@ describe('ChoiceList', function() {
 | 
				
			|||||||
    assert.deepEqual(await gu.getVisibleGridCells({rowNums: [1, 2, 3], cols: ['A']}), [
 | 
					    assert.deepEqual(await gu.getVisibleGridCells({rowNums: [1, 2, 3], cols: ['A']}), [
 | 
				
			||||||
      'Hello',
 | 
					      'Hello',
 | 
				
			||||||
      'World',
 | 
					      'World',
 | 
				
			||||||
      ' Foo,Bar;Baz!,',   // That's the text originally entered into this Text cell.
 | 
					      ' Foo,Bar;Baz!,"Qux, quux corge", "80\'s",',   // That's the text originally entered into this Text cell.
 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user