mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +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