зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1762488 - Remove text configuration nesting with raw unlocalized text r=emcminn
Also allow configuring styles like zap and color. Clean up some unnecessary conditional rendering as Localized already handles that. Differential Revision: https://phabricator.services.mozilla.com/D142673
This commit is contained in:
Родитель
8b26a85901
Коммит
352b921b1e
|
@ -355,10 +355,12 @@ __webpack_require__.r(__webpack_exports__);
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const MS_STRING_PROP = "string_id";
|
||||
const CONFIGURABLE_STYLES = ["color", "fontSize"];
|
||||
const ZAP_SIZE_THRESHOLD = 160;
|
||||
/**
|
||||
* Based on the .text prop, localizes an inner element if a string_id
|
||||
* is provided, OR renders plain text, OR hides it if nothing is provided.
|
||||
* Allows configuring of some styles including zap underline and color.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
|
@ -373,6 +375,7 @@ const MS_STRING_PROP = "string_id";
|
|||
* Unlocalized text
|
||||
* jsx:
|
||||
* <Localized text="Welcome"><h1 /></Localized>
|
||||
* <Localized text={{raw: "Welcome"}}><h1 /></Localized>
|
||||
* output:
|
||||
* <h1>Welcome</h1>
|
||||
*/
|
||||
|
@ -381,29 +384,55 @@ const Localized = ({
|
|||
text,
|
||||
children
|
||||
}) => {
|
||||
// Dynamically determine the size of the zap style.
|
||||
const zapRef = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createRef();
|
||||
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
||||
const {
|
||||
current
|
||||
} = zapRef;
|
||||
if (current) requestAnimationFrame(() => current === null || current === void 0 ? void 0 : current.classList.replace("short", current.getBoundingClientRect().width > ZAP_SIZE_THRESHOLD ? "long" : "short"));
|
||||
}); // Skip rendering of children with no text.
|
||||
|
||||
if (!text) {
|
||||
return null;
|
||||
}
|
||||
} // Allow augmenting existing child container properties.
|
||||
|
||||
let props = children ? children.props : {};
|
||||
let textNode;
|
||||
|
||||
if (typeof text === "object" && text[MS_STRING_PROP]) {
|
||||
props = { ...props
|
||||
};
|
||||
props["data-l10n-id"] = text[MS_STRING_PROP];
|
||||
const props = {
|
||||
children: [],
|
||||
className: "",
|
||||
style: {},
|
||||
...(children === null || children === void 0 ? void 0 : children.props)
|
||||
}; // Support nested Localized by starting with their children.
|
||||
|
||||
const textNodes = props.children; // Pick desired fluent or raw/plain text to render.
|
||||
|
||||
if (text.string_id) {
|
||||
props["data-l10n-id"] = text.string_id;
|
||||
if (text.args) props["data-l10n-args"] = JSON.stringify(text.args);
|
||||
} else if (text.raw) {
|
||||
textNodes.push(text.raw);
|
||||
} else if (typeof text === "string") {
|
||||
textNode = text;
|
||||
}
|
||||
textNodes.push(text);
|
||||
} // Add zap style and content in a way that allows fluent to insert too.
|
||||
|
||||
if (!children) {
|
||||
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", props, textNode);
|
||||
} else if (textNode) {
|
||||
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().cloneElement(children, props, textNode);
|
||||
}
|
||||
|
||||
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().cloneElement(children, props);
|
||||
if (text.zap) {
|
||||
props.className += " welcomeZap";
|
||||
textNodes.push( /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
||||
className: "short zap",
|
||||
"data-l10n-name": "zap",
|
||||
ref: zapRef
|
||||
}, text.zap));
|
||||
} // Apply certain configurable styles.
|
||||
|
||||
|
||||
CONFIGURABLE_STYLES.forEach(style => {
|
||||
if (text[style]) props.style[style] = text[style];
|
||||
});
|
||||
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().cloneElement( // Provide a default container for the text if necessary.
|
||||
children ?? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null), props, // Conditionally pass in as void elements can't accept empty array.
|
||||
textNodes.length ? textNodes : null);
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
|
@ -679,7 +708,7 @@ class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
|
|||
}
|
||||
|
||||
render() {
|
||||
var _this$props$appAndSys, _content$primary_butt;
|
||||
var _this$props$appAndSys, _content$primary_butt, _content$primary_butt2;
|
||||
|
||||
const {
|
||||
autoAdvance,
|
||||
|
@ -716,11 +745,11 @@ class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
|
|||
text: content.hero_text
|
||||
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h1", null)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "spacer-bottom"
|
||||
})), content.help_text && content.help_text.text ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
||||
text: content.help_text.text
|
||||
})), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
||||
text: content.help_text
|
||||
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
||||
className: "attrib-text"
|
||||
})) : null) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
}))) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "section-main"
|
||||
}, content.secondary_button_top ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_5__.SecondaryCTA, {
|
||||
content: content,
|
||||
|
@ -749,19 +778,19 @@ class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
|
|||
text: content.title
|
||||
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h1", {
|
||||
id: "mainContentHeader"
|
||||
})), content.subtitle ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
||||
})), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
||||
text: content.subtitle
|
||||
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h2", {
|
||||
"data-l10n-args": JSON.stringify({
|
||||
"addon-name": this.props.addonName,
|
||||
...((_this$props$appAndSys = this.props.appAndSystemLocaleInfo) === null || _this$props$appAndSys === void 0 ? void 0 : _this$props$appAndSys.displayNames)
|
||||
})
|
||||
})) : null), this.renderContentTiles(), this.renderLanguageSwitcher(), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
||||
text: content.primary_button ? content.primary_button.label : null
|
||||
}))), this.renderContentTiles(), this.renderLanguageSwitcher(), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
||||
text: (_content$primary_butt = content.primary_button) === null || _content$primary_butt === void 0 ? void 0 : _content$primary_butt.label
|
||||
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
||||
className: "primary",
|
||||
value: "primary_button",
|
||||
disabled: ((_content$primary_butt = content.primary_button) === null || _content$primary_butt === void 0 ? void 0 : _content$primary_butt.disabled) === true,
|
||||
disabled: ((_content$primary_butt2 = content.primary_button) === null || _content$primary_butt2 === void 0 ? void 0 : _content$primary_butt2.disabled) === true,
|
||||
onClick: this.props.handleAction
|
||||
})), content.secondary_button ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_5__.SecondaryCTA, {
|
||||
content: content,
|
||||
|
@ -1077,7 +1106,7 @@ const Themes = props => {
|
|||
onClick: props.handleAction
|
||||
})), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: `icon ${theme === props.activeTheme ? " selected" : ""} ${theme}`
|
||||
}), label && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
||||
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
||||
text: label
|
||||
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "text"
|
||||
|
|
|
@ -38,9 +38,7 @@ const DEFAULT_WELCOME_CONTENT = {
|
|||
string_id: "mr1-welcome-screen-hero-text",
|
||||
},
|
||||
help_text: {
|
||||
text: {
|
||||
string_id: "mr1-onboarding-welcome-image-caption",
|
||||
},
|
||||
string_id: "mr1-onboarding-welcome-image-caption",
|
||||
},
|
||||
has_noodles: true,
|
||||
primary_button: {
|
||||
|
@ -443,7 +441,7 @@ async function prepareContentForReact(content) {
|
|||
if (Services.locale.appLocaleAsBCP47.split("-")[0] !== "en") {
|
||||
delete content.screens?.find(
|
||||
screen => screen.content?.help_text?.deleteIfNotEn
|
||||
)?.content.help_text.text;
|
||||
)?.content.help_text;
|
||||
}
|
||||
|
||||
let shouldRemoveLanguageMismatchScreen = true;
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import React from "react";
|
||||
const MS_STRING_PROP = "string_id";
|
||||
import React, { useEffect } from "react";
|
||||
const CONFIGURABLE_STYLES = ["color", "fontSize"];
|
||||
const ZAP_SIZE_THRESHOLD = 160;
|
||||
|
||||
/**
|
||||
* Based on the .text prop, localizes an inner element if a string_id
|
||||
* is provided, OR renders plain text, OR hides it if nothing is provided.
|
||||
* Allows configuring of some styles including zap underline and color.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
|
@ -22,30 +24,67 @@ const MS_STRING_PROP = "string_id";
|
|||
* Unlocalized text
|
||||
* jsx:
|
||||
* <Localized text="Welcome"><h1 /></Localized>
|
||||
* <Localized text={{raw: "Welcome"}}><h1 /></Localized>
|
||||
* output:
|
||||
* <h1>Welcome</h1>
|
||||
*/
|
||||
|
||||
export const Localized = ({ text, children }) => {
|
||||
// Dynamically determine the size of the zap style.
|
||||
const zapRef = React.createRef();
|
||||
useEffect(() => {
|
||||
const { current } = zapRef;
|
||||
if (current)
|
||||
requestAnimationFrame(() =>
|
||||
current?.classList.replace(
|
||||
"short",
|
||||
current.getBoundingClientRect().width > ZAP_SIZE_THRESHOLD
|
||||
? "long"
|
||||
: "short"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
// Skip rendering of children with no text.
|
||||
if (!text) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let props = children ? children.props : {};
|
||||
let textNode;
|
||||
// Allow augmenting existing child container properties.
|
||||
const props = { children: [], className: "", style: {}, ...children?.props };
|
||||
// Support nested Localized by starting with their children.
|
||||
const textNodes = props.children;
|
||||
|
||||
if (typeof text === "object" && text[MS_STRING_PROP]) {
|
||||
props = { ...props };
|
||||
props["data-l10n-id"] = text[MS_STRING_PROP];
|
||||
// Pick desired fluent or raw/plain text to render.
|
||||
if (text.string_id) {
|
||||
props["data-l10n-id"] = text.string_id;
|
||||
if (text.args) props["data-l10n-args"] = JSON.stringify(text.args);
|
||||
} else if (text.raw) {
|
||||
textNodes.push(text.raw);
|
||||
} else if (typeof text === "string") {
|
||||
textNode = text;
|
||||
textNodes.push(text);
|
||||
}
|
||||
|
||||
if (!children) {
|
||||
return React.createElement("span", props, textNode);
|
||||
} else if (textNode) {
|
||||
return React.cloneElement(children, props, textNode);
|
||||
// Add zap style and content in a way that allows fluent to insert too.
|
||||
if (text.zap) {
|
||||
props.className += " welcomeZap";
|
||||
textNodes.push(
|
||||
<span className="short zap" data-l10n-name="zap" ref={zapRef}>
|
||||
{text.zap}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return React.cloneElement(children, props);
|
||||
|
||||
// Apply certain configurable styles.
|
||||
CONFIGURABLE_STYLES.forEach(style => {
|
||||
if (text[style]) props.style[style] = text[style];
|
||||
});
|
||||
|
||||
return React.cloneElement(
|
||||
// Provide a default container for the text if necessary.
|
||||
children ?? <span />,
|
||||
props,
|
||||
// Conditionally pass in as void elements can't accept empty array.
|
||||
textNodes.length ? textNodes : null
|
||||
);
|
||||
};
|
||||
|
|
|
@ -197,11 +197,9 @@ export class ProtonScreen extends React.PureComponent {
|
|||
</Localized>
|
||||
<div className="spacer-bottom" />
|
||||
</div>
|
||||
{content.help_text && content.help_text.text ? (
|
||||
<Localized text={content.help_text.text}>
|
||||
<span className="attrib-text" />
|
||||
</Localized>
|
||||
) : null}
|
||||
<Localized text={content.help_text}>
|
||||
<span className="attrib-text" />
|
||||
</Localized>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="section-main">
|
||||
|
@ -237,25 +235,19 @@ export class ProtonScreen extends React.PureComponent {
|
|||
<Localized text={content.title}>
|
||||
<h1 id="mainContentHeader" />
|
||||
</Localized>
|
||||
{content.subtitle ? (
|
||||
<Localized text={content.subtitle}>
|
||||
<h2
|
||||
data-l10n-args={JSON.stringify({
|
||||
"addon-name": this.props.addonName,
|
||||
...this.props.appAndSystemLocaleInfo?.displayNames,
|
||||
})}
|
||||
/>
|
||||
</Localized>
|
||||
) : null}
|
||||
<Localized text={content.subtitle}>
|
||||
<h2
|
||||
data-l10n-args={JSON.stringify({
|
||||
"addon-name": this.props.addonName,
|
||||
...this.props.appAndSystemLocaleInfo?.displayNames,
|
||||
})}
|
||||
/>
|
||||
</Localized>
|
||||
</div>
|
||||
{this.renderContentTiles()}
|
||||
{this.renderLanguageSwitcher()}
|
||||
<div>
|
||||
<Localized
|
||||
text={
|
||||
content.primary_button ? content.primary_button.label : null
|
||||
}
|
||||
>
|
||||
<Localized text={content.primary_button?.label}>
|
||||
<button
|
||||
className="primary"
|
||||
value="primary_button"
|
||||
|
|
|
@ -37,11 +37,9 @@ export const Themes = props => {
|
|||
theme === props.activeTheme ? " selected" : ""
|
||||
} ${theme}`}
|
||||
/>
|
||||
{label && (
|
||||
<Localized text={label}>
|
||||
<div className="text" />
|
||||
</Localized>
|
||||
)}
|
||||
<Localized text={label}>
|
||||
<div className="text" />
|
||||
</Localized>
|
||||
</label>
|
||||
</Localized>
|
||||
)
|
||||
|
|
|
@ -114,7 +114,7 @@ describe("MultiStageAboutWelcomeProton module", () => {
|
|||
it("should have an image caption", async () => {
|
||||
const data = await prepConfig();
|
||||
|
||||
assert.property(data.screens[0].content.help_text, "text");
|
||||
assert.property(data.screens[0].content, "help_text");
|
||||
});
|
||||
it("should remove the caption if deleteIfNotEn is true", async () => {
|
||||
sandbox.stub(global.Services.locale, "appLocaleAsBCP47").value("de");
|
||||
|
@ -132,16 +132,14 @@ describe("MultiStageAboutWelcomeProton module", () => {
|
|||
position: "corner",
|
||||
help_text: {
|
||||
deleteIfNotEn: true,
|
||||
text: {
|
||||
string_id: "mr1-onboarding-welcome-image-caption",
|
||||
},
|
||||
string_id: "mr1-onboarding-welcome-image-caption",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
assert.notProperty(data.screens[0].content.help_text, "text");
|
||||
assert.notProperty(data.screens[0].content, "help_text");
|
||||
});
|
||||
});
|
||||
describe("AboutWelcomeDefaults prepareContentForReact", () => {
|
||||
|
|
Загрузка…
Ссылка в новой задаче