templates: Convert module to TypeScript.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg
2024-11-27 13:16:39 -08:00
committed by Tim Abbott
parent 19f5ea8832
commit cf7e420847
6 changed files with 48 additions and 25 deletions

View File

@@ -9,7 +9,7 @@ import "jquery-validation";
// Import app JS
import "../setup.ts";
import "../reload.ts";
import "../templates.js";
import "../templates.ts";
import "../zulip_test.ts";
// Import styles

View File

@@ -20,7 +20,7 @@ Handlebars.registerHelper({
eq(a, b) {
return a === b;
},
and(...args) {
and(...args: unknown[]) {
args.pop(); // Handlebars options
if (args.length === 0) {
return true;
@@ -33,7 +33,7 @@ Handlebars.registerHelper({
}
return last;
},
or(...args) {
or(...args: unknown[]) {
args.pop(); // Handlebars options
if (args.length === 0) {
return false;
@@ -51,7 +51,9 @@ Handlebars.registerHelper({
},
});
Handlebars.registerHelper("t", function (message) {
type Context = Record<string, unknown>;
Handlebars.registerHelper("t", function (this: Context, message: string) {
// Marks a string for translation.
// Example usage 1:
// {{t "some English text"}}
@@ -68,10 +70,19 @@ Handlebars.registerHelper("t", function (message) {
.map((s) => s.trim())
.join(" ");
const descriptor = {id: message, defaultMessage: message};
return intl.formatMessage(descriptor, this);
return intl.formatMessage(
descriptor,
Object.fromEntries(
Object.entries(this).flatMap(([key, value]) =>
typeof value === "string" || typeof value === "number" || value instanceof Date
? [[key, value]]
: [],
),
),
);
});
Handlebars.registerHelper("tr", function (options) {
Handlebars.registerHelper("tr", function (this: Context, options: Handlebars.HelperOptions) {
// Marks a block for translation.
// Example usage 1:
// {{#tr}}
@@ -92,19 +103,29 @@ Handlebars.registerHelper("tr", function (options) {
.map((s) => s.trim())
.join(" ");
const descriptor = {id: message, defaultMessage: message};
const partials: Partial<Record<string, (context: Context, options: unknown) => string>> =
"partials" in options.fn &&
typeof options.fn.partials === "object" &&
options.fn.partials !== null
? options.fn.partials
: {};
const result = intl.formatMessage(descriptor, {
...default_html_elements,
...Object.fromEntries(
Object.entries(options.fn.partials ?? {}).map(([name, value]) => [
Object.entries(partials).map(([name, value]) => [
name,
(s) => value(this, {data: {"partial-block": () => s.join("")}}),
(content_html: string[]) =>
value!(this, {data: {"partial-block": () => content_html.join("")}}),
]),
),
...Object.fromEntries(
Object.entries(this).map(([key, value]) => [
key,
Handlebars.Utils.escapeExpression(value),
]),
Object.entries(this).flatMap(([key, value]): [string, string | number | Date][] =>
typeof value === "string"
? [[key, Handlebars.Utils.escapeExpression(value)]]
: typeof value === "number" || value instanceof Date
? [[key, value]]
: [],
),
),
});
return new Handlebars.SafeString(result);
@@ -112,13 +133,14 @@ Handlebars.registerHelper("tr", function (options) {
Handlebars.registerHelper(
"rendered_markdown",
(content) => new Handlebars.SafeString(postprocess_content(content)),
(content: string) => new Handlebars.SafeString(postprocess_content(content)),
);
Handlebars.registerHelper("numberFormat", (number) => number.toLocaleString());
Handlebars.registerHelper("numberFormat", (number: number) => number.toLocaleString());
Handlebars.registerHelper("tooltip_hotkey_hints", (...hotkeys) => {
hotkeys.pop(); // Handlebars options
Handlebars.registerHelper("tooltip_hotkey_hints", (...args) => {
args.pop(); // Handlebars options
const hotkeys: string[] = args;
let hotkey_hints = "";
common.adjust_mac_hotkey_hints(hotkeys);
for (const hotkey of hotkeys) {
@@ -128,8 +150,9 @@ Handlebars.registerHelper("tooltip_hotkey_hints", (...hotkeys) => {
return new Handlebars.SafeString(result);
});
Handlebars.registerHelper("popover_hotkey_hints", (...hotkeys) => {
hotkeys.pop(); // Handlebars options
Handlebars.registerHelper("popover_hotkey_hints", (...args) => {
args.pop(); // Handlebars options
const hotkeys: string[] = args;
let hotkey_hints = "";
common.adjust_mac_hotkey_hints(hotkeys);
const shift_hotkey_exists = common.adjust_shift_hotkey(hotkeys);
@@ -138,7 +161,7 @@ Handlebars.registerHelper("popover_hotkey_hints", (...hotkeys) => {
}
if (shift_hotkey_exists) {
return new Handlebars.SafeString(
`<span class="popover-menu-hotkey-hints popover-contains-shift-hotkey" data-hotkey-hints="${hotkeys}">${hotkey_hints}</span>`,
`<span class="popover-menu-hotkey-hints popover-contains-shift-hotkey" data-hotkey-hints="${hotkeys.join(",")}">${hotkey_hints}</span>`,
);
}
return new Handlebars.SafeString(

View File

@@ -22,12 +22,12 @@ page_params.translation_data = {
// Re-register Zulip extensions so extensions registered previously with
// mocked i18n.ts do not interfere with following tests.
require("../src/templates.js");
require("../src/templates.ts");
// All of our other tests stub out i18n activity;
// here we do a quick sanity check on the engine itself.
// `i18n.ts` initializes FormatJS and is imported by
// `templates.js`.
// `templates.ts`.
unmock_module("../src/i18n");
const {$t, $t_html, get_language_name, get_language_list_columns, initialize} = zrequire("i18n");
@@ -102,7 +102,7 @@ run_test("{{#tr}} to tag for translation", ({mock_template}) => {
// We're actually testing `notification_settings.hbs` here which
// is imported as a partial in the file below. We want to test
// the partial handling logic in `templates.js`, that's why we
// the partial handling logic in `templates.ts`, that's why we
// test the file below instead of directly testing
// `notification_settings.hbs`.
mock_template("settings/user_notification_settings.hbs", true, (data, html) => {

View File

@@ -83,7 +83,7 @@ handlebars.hook_require();
const noop = function () {};
require("../../src/templates.js"); // register Zulip extensions
require("../../src/templates.ts"); // register Zulip extensions
async function run_one_module(file) {
zjquery.clear_initialize_function();

View File

@@ -209,7 +209,7 @@ exports.zrequire = function (short_fn) {
short_fn,
"templates",
`
There is no need to zrequire templates.js.
There is no need to zrequire templates.ts.
The test runner automatically registers the
Handlebars extensions.

View File

@@ -167,7 +167,7 @@ const config = (
ignoreHelpers: true,
// Tell webpack not to explicitly require these.
knownHelpers: [
// The ones below are defined in web/src/templates.js
// The ones below are defined in web/src/templates.ts
"eq",
"and",
"or",