Skip to content

Commit

Permalink
fix: loading tooltips before messages (google#7504)
Browse files Browse the repository at this point in the history
* fix: loading tooltips before messages

* fix: typing
  • Loading branch information
BeksOmega authored Sep 20, 2023
1 parent 3b234c7 commit 12ac358
Showing 1 changed file with 20 additions and 81 deletions.
101 changes: 20 additions & 81 deletions core/extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,16 +388,6 @@ export function runAfterPageLoad(fn: () => void) {
* Builds an extension function that will map a dropdown value to a tooltip
* string.
*
* This method includes multiple checks to ensure tooltips, dropdown options,
* and message references are aligned. This aims to catch errors as early as
* possible, without requiring developers to manually test tooltips under each
* option. After the page is loaded, each tooltip text string will be checked
* for matching message keys in the internationalized string table. Deferring
* this until the page is loaded decouples loading dependencies. Later, upon
* loading the first block of any given type, the extension will validate every
* dropdown option has a matching tooltip in the lookupTable. Errors are
* reported as warnings in the console, and are never fatal.
*
* @param dropdownName The name of the field whose value is the key to the
* lookup table.
* @param lookupTable The table of field values to tooltip text.
Expand All @@ -406,56 +396,23 @@ export function runAfterPageLoad(fn: () => void) {
export function buildTooltipForDropdown(
dropdownName: string,
lookupTable: {[key: string]: string},
): Function {
): (this: Block) => void {
// List of block types already validated, to minimize duplicate warnings.
const blockTypesChecked: AnyDuringMigration[] = [];

// Check the tooltip string messages for invalid references.
// Wait for load, in case Blockly.Msg is not yet populated.
// runAfterPageLoad() does not run in a Node.js environment due to lack
// of document object, in which case skip the validation.
if (typeof document === 'object') {
// Relies on document.readyState
runAfterPageLoad(function () {
for (const key in lookupTable) {
// Will print warnings if reference is missing.
parsing.checkMessageReferences(lookupTable[key]);
}
});
}
const blockTypesChecked: string[] = [];

/** The actual extension. */
function extensionFn(this: Block) {
if (this.type && blockTypesChecked.indexOf(this.type) === -1) {
return function (this: Block) {
if (blockTypesChecked.indexOf(this.type) === -1) {
checkDropdownOptionsInTable(this, dropdownName, lookupTable);
blockTypesChecked.push(this.type);
}

this.setTooltip(
function (this: Block) {
const value = String(this.getFieldValue(dropdownName));
let tooltip = lookupTable[value];
if (tooltip === null) {
if (blockTypesChecked.indexOf(this.type) === -1) {
// Warn for missing values on generated tooltips.
let warning =
'No tooltip mapping for value ' +
value +
' of field ' +
dropdownName;
if (this.type !== null) {
warning += ' of block type ' + this.type;
}
console.warn(warning + '.');
}
} else {
tooltip = parsing.replaceMessageReferences(tooltip);
}
return tooltip;
return parsing.replaceMessageReferences(lookupTable[value]);
}.bind(this),
);
}
return extensionFn;
};
}

/**
Expand All @@ -471,22 +428,18 @@ function checkDropdownOptionsInTable(
dropdownName: string,
lookupTable: {[key: string]: string},
) {
// Validate all dropdown options have values.
const dropdown = block.getField(dropdownName);
if (dropdown instanceof FieldDropdown && !dropdown.isOptionListDynamic()) {
const options = dropdown.getOptions();
for (let i = 0; i < options.length; i++) {
const optionKey = options[i][1]; // label, then value
if (lookupTable[optionKey] === null) {
console.warn(
'No tooltip mapping for value ' +
optionKey +
' of field ' +
dropdownName +
' of block type ' +
block.type,
);
}
if (!(dropdown instanceof FieldDropdown) || dropdown.isOptionListDynamic()) {
return;
}

const options = dropdown.getOptions();
for (const [, key] of options) {
if (lookupTable[key] === undefined) {
console.warn(
`No tooltip mapping for value ${key} of field ` +
`${dropdownName} of block type ${block.type}.`,
);
}
}
}
Expand All @@ -504,21 +457,8 @@ function checkDropdownOptionsInTable(
export function buildTooltipWithFieldText(
msgTemplate: string,
fieldName: string,
): Function {
// Check the tooltip string messages for invalid references.
// Wait for load, in case Blockly.Msg is not yet populated.
// runAfterPageLoad() does not run in a Node.js environment due to lack
// of document object, in which case skip the validation.
if (typeof document === 'object') {
// Relies on document.readyState
runAfterPageLoad(function () {
// Will print warnings if reference is missing.
parsing.checkMessageReferences(msgTemplate);
});
}

/** The actual extension. */
function extensionFn(this: Block) {
): (this: Block) => void {
return function (this: Block) {
this.setTooltip(
function (this: Block) {
const field = this.getField(fieldName);
Expand All @@ -527,8 +467,7 @@ export function buildTooltipWithFieldText(
.replace('%1', field ? field.getText() : '');
}.bind(this),
);
}
return extensionFn;
};
}

/**
Expand Down

0 comments on commit 12ac358

Please sign in to comment.