0

WebUI: Add syntax highlighting to docs/webui/ code snippets.

Bug: None
Change-Id: Ib88ad238b6a2e756e9050876b37a984dd88d6c04
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6388416
Auto-Submit: Demetrios Papadopoulos <dpapad@chromium.org>
Commit-Queue: Demetrios Papadopoulos <dpapad@chromium.org>
Reviewed-by: Teresa Mao <temao@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1437215}
This commit is contained in:
dpapad
2025-03-24 17:21:43 -07:00
committed by Chromium LUCI CQ
parent ede58b1274
commit 994a93969c
8 changed files with 76 additions and 78 deletions

@ -5,7 +5,7 @@
In order to build with a fast configuration, try setting these options in your In order to build with a fast configuration, try setting these options in your
GN args: GN args:
``` ```python
optimize_webui = true optimize_webui = true
is_debug = false is_debug = false
``` ```

@ -71,11 +71,10 @@ class FooBarBrowserTest : public WebUIMochaBrowserTest {
// Necessary if you have features (or the UI itself) behind a feature flag. // Necessary if you have features (or the UI itself) behind a feature flag.
base::test::ScopedFeatureList scoped_feature_list_{foo_bar::kSomeNewFeature}; base::test::ScopedFeatureList scoped_feature_list_{foo_bar::kSomeNewFeature};
}; };
``` ```
Note: If testing a `chrome-untrusted://` UI, also call: Note: If testing a `chrome-untrusted://` UI, also call:
``` ```c++
set_test_loader_scheme(content::kChromeUIUntrustedScheme); set_test_loader_scheme(content::kChromeUIUntrustedScheme);
``` ```
in the constructor. in the constructor.
@ -89,7 +88,6 @@ IN_PROC_BROWSER_TEST_F(FooBarAppTest, All) {
// Run all Mocha test suites found in app_test // Run all Mocha test suites found in app_test
RunTest("foo_bar/app_test.js", "mocha.run();"); RunTest("foo_bar/app_test.js", "mocha.run();");
} }
``` ```
Should you write a large number of tests in a single file (e.g., for Should you write a large number of tests in a single file (e.g., for
one particular custom element), it may be necessary to run different suites or one particular custom element), it may be necessary to run different suites or
@ -205,7 +203,7 @@ suite('SearchEmpty', function() {
To build your tests as part of the `browser_tests` target, add a BUILD.gn file To build your tests as part of the `browser_tests` target, add a BUILD.gn file
in your folder: in your folder:
``` ```python
# chrome/test/data/webui/foo_bar/BUILD.gn # chrome/test/data/webui/foo_bar/BUILD.gn
import("../build_webui_tests.gni") import("../build_webui_tests.gni")
@ -239,7 +237,7 @@ importing shared test files from `chrome-untrusted://webui-test/` paths.
You then need to hook up the targets generated by this file, and list your You then need to hook up the targets generated by this file, and list your
browsertest.cc source file, in `chrome/test/data/webui/BUILD.gn`: browsertest.cc source file, in `chrome/test/data/webui/BUILD.gn`:
``` ```python
# chrome/test/data/webui/BUILD.gn # chrome/test/data/webui/BUILD.gn
source_set("browser_tests") { source_set("browser_tests") {
@ -282,8 +280,8 @@ generate_grd("build_grd") {
# Conditionally added (e.g. platform-specific) grdps and deps go here # Conditionally added (e.g. platform-specific) grdps and deps go here
} }
``` ```
### Unusual Test Setups ### Unusual Test Setups
Some UIs may need to rely on loading a particular URL, such that using the Some UIs may need to rely on loading a particular URL, such that using the
default `chrome://foo-bar/test_loader.html` URL that is used to dynamically default `chrome://foo-bar/test_loader.html` URL that is used to dynamically

@ -25,7 +25,7 @@ in the WebUI renderer such as memory corruption bugs.
should have an if statement to check the Trusted Types support before using should have an if statement to check the Trusted Types support before using
methods/properties under `window.trustedTypes` :** methods/properties under `window.trustedTypes` :**
``` ```ts
if (window.trustedTypes) { if (window.trustedTypes) {
// Trusted Types is supported, let's use Trusted Types 😎 // Trusted Types is supported, let's use Trusted Types 😎
elem.innerHTML = trustedTypes.emptyHTML; elem.innerHTML = trustedTypes.emptyHTML;
@ -39,7 +39,7 @@ if (window.trustedTypes) {
Example code: Example code:
``` ```ts
document.body.innerHTML = ''; document.body.innerHTML = '';
``` ```
@ -47,7 +47,7 @@ This will be a Trusted Types violation because the value we are assigning to
a dangerous sink is not a Trusted Type. a dangerous sink is not a Trusted Type.
This can be converted to: This can be converted to:
``` ```ts
document.body.innerHTML = trustedTypes.emptyHTML; document.body.innerHTML = trustedTypes.emptyHTML;
``` ```
@ -57,13 +57,13 @@ There is also `trustedTypes.emptyScript` to clear script contents.
Example code: Example code:
``` ```ts
document.body.innerHTML = 'Hello Guest!'; document.body.innerHTML = 'Hello Guest!';
``` ```
Because this is just a text assignment, this can be converted to: Because this is just a text assignment, this can be converted to:
``` ```ts
document.body.textContent = 'Hello Guest!'; document.body.textContent = 'Hello Guest!';
``` ```
@ -73,14 +73,14 @@ document.body.textContent = 'Hello Guest!';
Example code: Example code:
``` ```ts
document.body.innerHTML = '<div><p>' + loadTimeData.getString('foo') + '</p> document.body.innerHTML = '<div><p>' + loadTimeData.getString('foo') + '</p>
</div>'; </div>';
``` ```
This can be converted by adding _template_ element to HTML file: This can be converted by adding _template_ element to HTML file:
``` ```html
<template id="foo-template"> <template id="foo-template">
<div> <div>
<p></p> <p></p>
@ -90,7 +90,7 @@ This can be converted by adding _template_ element to HTML file:
And then adding following JS code to JS file: And then adding following JS code to JS file:
``` ```ts
// body might already have some contents, so let's clear those first 😊 // body might already have some contents, so let's clear those first 😊
document.body.innerHTML = trustedTypes.emptyHTML; document.body.innerHTML = trustedTypes.emptyHTML;
@ -105,13 +105,13 @@ In cases where you don't have control over the HTML file (e.g. converting common
JS libraries), you can use DOM APIs. JS libraries), you can use DOM APIs.
Example code: Example code:
``` ```ts
document.body.innerHTML += '<p>' + loadTimeData.getString('foo') + '</p>'; document.body.innerHTML += '<p>' + loadTimeData.getString('foo') + '</p>';
``` ```
And then adding following JS code to JS file: And then adding following JS code to JS file:
``` ```ts
const p = document.createElement('p'); const p = document.createElement('p');
p.textContent = loadTimeData.getString('foo'); p.textContent = loadTimeData.getString('foo');
document.body.appendChild(p); document.body.appendChild(p);
@ -124,7 +124,7 @@ readability, you can [use `trustedTypes.createPolicy`]
(https://web.dev/trusted-types/#create-a-trusted-type-policy). (https://web.dev/trusted-types/#create-a-trusted-type-policy).
Example code: Example code:
``` ```ts
document.body.innerHTML = '<div class="tree-row">' + document.body.innerHTML = '<div class="tree-row">' +
'<span class="expand-icon"></span>' + '<span class="expand-icon"></span>' +
'<span class="tree-label-icon"></span>' + '<span class="tree-label-icon"></span>' +
@ -135,7 +135,7 @@ document.body.innerHTML = '<div class="tree-row">' +
This can be converted to: This can be converted to:
``` ```ts
const htmlString = '<div class="tree-row">' + const htmlString = '<div class="tree-row">' +
'<span class="expand-icon"></span>' + '<span class="expand-icon"></span>' +
'<span class="tree-label-icon"></span>' + '<span class="tree-label-icon"></span>' +
@ -155,7 +155,7 @@ This case also requires changes in C++, as we need to allow the `foo-static`
Trusted Type policy (created above in `trustedTypes.createPolicy`) in the CSP Trusted Type policy (created above in `trustedTypes.createPolicy`) in the CSP
header. header.
``` ```c++
source->OverrideContentSecurityPolicy( source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::TrustedTypes, network::mojom::CSPDirectiveName::TrustedTypes,
"trusted-types foo-static;"); "trusted-types foo-static;");
@ -165,7 +165,7 @@ source->OverrideContentSecurityPolicy(
Example code: Example code:
``` ```ts
const script = document.createElement('script'); const script = document.createElement('script');
script.src = 'chrome://resources/foo.js'; script.src = 'chrome://resources/foo.js';
document.body.appendChild(script); document.body.appendChild(script);
@ -173,7 +173,7 @@ document.body.appendChild(script);
This can be converted to: This can be converted to:
``` ```ts
const staticUrlPolicy = trustedTypes.createPolicy( const staticUrlPolicy = trustedTypes.createPolicy(
'foo-js-static', 'foo-js-static',
{createScriptURL: () => 'chrome://resources/foo.js'}); {createScriptURL: () => 'chrome://resources/foo.js'});
@ -189,7 +189,7 @@ This case also requires changes in C++, as we need to allow the `foo-js-static`
Trusted Type policy (created above in `trustedTypes.createPolicy`) in the CSP Trusted Type policy (created above in `trustedTypes.createPolicy`) in the CSP
header. header.
``` ```c++
source->OverrideContentSecurityPolicy( source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::TrustedTypes, network::mojom::CSPDirectiveName::TrustedTypes,
"trusted-types foo-js-static;"); "trusted-types foo-js-static;");
@ -200,7 +200,7 @@ source->OverrideContentSecurityPolicy(
In case there is no way to support Trusted Types in a WebUI page, you can In case there is no way to support Trusted Types in a WebUI page, you can
disable Trusted Types with following code: disable Trusted Types with following code:
``` ```c++
source->DisableTrustedTypesCSP(); source->DisableTrustedTypesCSP();
``` ```

@ -72,7 +72,7 @@ scheme: One of ['chrome', 'relative']. Defaults to 'relative'. Specifies whether
``` ```
#### **Example** #### **Example**
``` ```python
import("//tools/polymer/html_to_wrapper.gni") import("//tools/polymer/html_to_wrapper.gni")
import("//tools/polymer/css_to_wrapper.gni") import("//tools/polymer/css_to_wrapper.gni")
@ -115,7 +115,7 @@ defines: Optional parameter. Specifies additional variables that can be used in
conditional expressions. conditional expressions.
``` ```
#### **Example:** #### **Example:**
``` ```python
import("//tools/grit/preprocess_if_expr.gni") import("//tools/grit/preprocess_if_expr.gni")
# Preprocesses "my_web_component.html.ts" and my_style_module.css.ts in # Preprocesses "my_web_component.html.ts" and my_style_module.css.ts in
@ -223,7 +223,7 @@ enable_source_maps: Defaults to the value of the enable_webui_inline_sourcemaps
#### **Example** #### **Example**
``` ```python
import("//tools/typescript/ts_library.gni") import("//tools/typescript/ts_library.gni")
# Compiles and outputs my_webui.js, my_web_component.js and # Compiles and outputs my_webui.js, my_web_component.js and
@ -362,7 +362,7 @@ out_folder: The location where bundled files will be placed in. Defaults to
``` ```
#### **Example** #### **Example**
``` ```python
import("//ui/webui/resources/tools/bundle_js.gni") import("//ui/webui/resources/tools/bundle_js.gni")
import ("//chrome/common/features.gni") import ("//chrome/common/features.gni")
@ -406,7 +406,7 @@ deps: Targets generating any files being minified.
``` ```
#### **Example** #### **Example**
``` ```python
import("//ui/webui/resources/tools/minify_js.gni") import("//ui/webui/resources/tools/minify_js.gni")
import ("//chrome/common/features.gni") import ("//chrome/common/features.gni")
@ -444,7 +444,7 @@ deps: Targets generating any files being minified.
``` ```
#### **Example** #### **Example**
``` ```python
import("//ui/webui/resources/tools/generate_code_cache.gni") import("//ui/webui/resources/tools/generate_code_cache.gni")
import ("//chrome/common/features.gni") import ("//chrome/common/features.gni")
@ -497,7 +497,7 @@ resource_path_rewrites: Paths to rewrite. In general, the path in input_files,
``` ```
#### **Example** #### **Example**
``` ```python
import("//ui/webui/resources/tools/generate_grd.gni") import("//ui/webui/resources/tools/generate_grd.gni")
generate_grd("build_grd") { generate_grd("build_grd") {
@ -539,7 +539,7 @@ deps: Names of any targets generating the grd file, and names of any targets
``` ```
#### **Example** #### **Example**
``` ```python
import("//tools/grit/grit_rule.gni") import("//tools/grit/grit_rule.gni")
grit("resources") { grit("resources") {
@ -715,7 +715,7 @@ enable_type_aware_eslint_checks: Defaults to "true". Turns on additional
``` ```
#### **Example** #### **Example**
``` ```python
import("//ui/webui/resources/tools/build_webui.gni") import("//ui/webui/resources/tools/build_webui.gni")
build_webui("build") { build_webui("build") {
@ -821,7 +821,7 @@ enable_type_aware_eslint_checks: Defaults to "true". Turns on additional
``` ```
#### **Example** #### **Example**
``` ```python
import("//chrome/test/data/webui/settings/tools/build_webui_tests.gni") import("//chrome/test/data/webui/settings/tools/build_webui_tests.gni")
build_webui_tests("build") { build_webui_tests("build") {
@ -862,7 +862,7 @@ use any `<if expr>`.
``` ```
#### **BUILD.gn file** #### **BUILD.gn file**
``` ```python
import("//chrome/common/features.gni") import("//chrome/common/features.gni")
import("//tools/grit/grit_rule.gni") import("//tools/grit/grit_rule.gni")
import("//tools/typescript/ts_library.gni") import("//tools/typescript/ts_library.gni")
@ -915,7 +915,7 @@ for the top level index. None of these files use any `<if expr>`.
``` ```
#### **BUILD.gn file** #### **BUILD.gn file**
``` ```python
import("//chrome/common/features.gni") import("//chrome/common/features.gni")
import("//tools/grit/grit_rule.gni") import("//tools/grit/grit_rule.gni")
import("//tools/typescript/ts_library.gni") import("//tools/typescript/ts_library.gni")

@ -124,7 +124,7 @@ Third, **add dependencies and corresponding path mappings** in the build
targets that depend on the new shared library. This is best demonstrated with targets that depend on the new shared library. This is best demonstrated with
an example: an example:
``` ```python
build_webui("build_my_webui") { build_webui("build_my_webui") {
grd_prefix = "my_webui" grd_prefix = "my_webui"
# Other params here # Other params here

@ -512,7 +512,7 @@ mojom() is the build rule used to generate mojo bindings. It can be set up as
follows: follows:
**chrome/browser/ui/webui/donuts/BUILD.gn** **chrome/browser/ui/webui/donuts/BUILD.gn**
``` ```python
import("//mojo/public/tools/bindings/mojom.gni") import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojo_bindings") { mojom("mojo_bindings") {
@ -673,7 +673,7 @@ added to the build and served from the root (e.g.
`build_webui()`: `build_webui()`:
**chrome/browser/resources/donuts/BUILD.gn** **chrome/browser/resources/donuts/BUILD.gn**
``` ```python
import("//ui/webui/resources/tools/build_webui.gni") import("//ui/webui/resources/tools/build_webui.gni")
build_webui("build") { build_webui("build") {
@ -743,7 +743,7 @@ calling the method returns a promise. The promise will be resolved with the
response from the browser. response from the browser.
**chrome/browser/resources/donuts/donuts.ts** **chrome/browser/resources/donuts/donuts.ts**
```js ```ts
import {BrowserProxyImpl} from './browser_proxy.js'; import {BrowserProxyImpl} from './browser_proxy.js';
let numDonutsBaked: number = 0; let numDonutsBaked: number = 0;
@ -864,7 +864,7 @@ void OvenHandler::OnPilotLightExtinguished() {
This works by crafting a string to be evaluated in the renderer. Any arguments This works by crafting a string to be evaluated in the renderer. Any arguments
to the call are serialized to JSON and the parameter list is wrapped with to the call are serialized to JSON and the parameter list is wrapped with
``` ```c++
// See WebUI::GetJavascriptCall() for specifics: // See WebUI::GetJavascriptCall() for specifics:
"functionCallName(" + argumentsAsJson + ")" "functionCallName(" + argumentsAsJson + ")"
``` ```

@ -73,7 +73,7 @@ body {
``` ```
`chrome/browser/resources/hello_world/app.html.ts` `chrome/browser/resources/hello_world/app.html.ts`
```js ```ts
// Copyright 2024 The Chromium Authors // Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
@ -89,7 +89,7 @@ export function getHtml(this: HelloWorldAppElement) {
``` ```
`chrome/browser/resources/hello_world/app.ts` `chrome/browser/resources/hello_world/app.ts`
```js ```ts
import './strings.m.js'; import './strings.m.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
@ -517,7 +517,7 @@ HelloWorldUI::HelloWorldUI(content::WebUI* web_ui) {
It is suggested to turn off any optimizations during WebUI development by adding It is suggested to turn off any optimizations during WebUI development by adding
the following in the args.gn file: the following in the args.gn file:
``` ```py
optimize_webui = false optimize_webui = false
``` ```
@ -541,7 +541,7 @@ iterate faster by **not having to do the above steps**. In order to use this
flow, follow the steps below: flow, follow the steps below:
**Step 1:** Add the following in your args.gn file and build the `chrome` target. **Step 1:** Add the following in your args.gn file and build the `chrome` target.
``` ```py
optimize_webui = false # explained in previous section optimize_webui = false # explained in previous section
load_webui_from_disk = true load_webui_from_disk = true
``` ```

@ -84,7 +84,7 @@ will be necessary to check for changes to the property in `changedProperties`.
This is also demonstrated in the example below. This is also demonstrated in the example below.
Suppose the Lit child has a property with `notify: true` as follows: Suppose the Lit child has a property with `notify: true` as follows:
``` ```ts
static override get properties() { static override get properties() {
return { return {
foo: { foo: {
@ -97,14 +97,14 @@ static override get properties() {
This property is also bound to a parent element that listens for the This property is also bound to a parent element that listens for the
`-changed` event as follows: `-changed` event as follows:
``` ```html
<foo-child ?foo="${this.foo_}" on-foo-changed="${this.onFooChanged_}"> <foo-child ?foo="${this.foo_}" on-foo-changed="${this.onFooChanged_}">
</foo-child> </foo-child>
<demo-child id="demo"></demo-child> <demo-child id="demo"></demo-child>
``` ```
The parent TypeScript code could look like this: The parent TypeScript code could look like this:
``` ```ts
static override get properties() { static override get properties() {
return { return {
foo_: {type: Boolean}, foo_: {type: Boolean},
@ -148,7 +148,7 @@ empty at startup. The following example would reproduce this bug and
have an empty `<select>` displayed at startup. have an empty `<select>` displayed at startup.
`.html.ts` file with `<select>` bug: `.html.ts` file with `<select>` bug:
``` ```html
<select .value="${this.mySelectValue}" @change="${this.onSelectChange_}"> <select .value="${this.mySelectValue}" @change="${this.onSelectChange_}">
<option value="${MyEnum.FIRST}">Option 1</option> <option value="${MyEnum.FIRST}">Option 1</option>
<option value="${MyEnum.SECOND}">Option 2</option> <option value="${MyEnum.SECOND}">Option 2</option>
@ -157,7 +157,7 @@ have an empty `<select>` displayed at startup.
Corresponding `.ts`. Note that the bug manifests even though `mySelectValue` Corresponding `.ts`. Note that the bug manifests even though `mySelectValue`
is being initialized to a valid option. is being initialized to a valid option.
``` ```ts
static get properties() { static get properties() {
return { return {
mySelectValue: {type: String}, mySelectValue: {type: String},
@ -175,7 +175,7 @@ The current recommended workaround is to instead bind to the `selected`
attribute on each `<option>`, i.e.: attribute on each `<option>`, i.e.:
`.html.ts` file: `.html.ts` file:
``` ```html
<select @change="${this.onSelectChange_}"> <select @change="${this.onSelectChange_}">
<option value="${MyEnum.FIRST}" <option value="${MyEnum.FIRST}"
?selected="${this.isSelected_(MyEnum.FIRST)}"> ?selected="${this.isSelected_(MyEnum.FIRST)}">
@ -189,7 +189,7 @@ attribute on each `<option>`, i.e.:
``` ```
Corresponding `.ts` file: Corresponding `.ts` file:
``` ```ts
static get properties() { static get properties() {
return { return {
mySelectValue: {type: String}, mySelectValue: {type: String},
@ -313,7 +313,7 @@ template and its styling, and a `.ts` file containing the element definition.
*** ***
Example `.ts` file: Example `.ts` file:
``` ```ts
// Copyright 2024 The Chromium Authors // Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
@ -371,7 +371,7 @@ customElements.define(MyExampleElement.is, MyExampleElement);
``` ```
Example CSS file: Example CSS file:
``` ```css
/* Copyright 2024 The Chromium Authors /* Copyright 2024 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be * Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */ * found in the LICENSE file. */
@ -406,7 +406,7 @@ generate the wrapper `.css.ts` file.
*** ***
Example `.html.ts `file: Example `.html.ts `file:
``` ```ts
// Copyright 2024 The Chromium Authors // Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
@ -425,7 +425,7 @@ export function getHtml(this: MyExampleElement) {
``` ```
`BUILD.gn` file configuration: `BUILD.gn` file configuration:
``` ```python
build_webui("build") { build_webui("build") {
# Use non_web_component_files since the .html.ts file is checked in. # Use non_web_component_files since the .html.ts file is checked in.
@ -493,12 +493,12 @@ in these cases the computation method can be used directly in the template
without specifying parameters. An example of this follows. without specifying parameters. An example of this follows.
Polymer HTML template snippet: Polymer HTML template snippet:
``` ```html
<cr-button hidden="[[hideButton_]]">Click Me</cr-button> <cr-button hidden="[[hideButton_]]">Click Me</cr-button>
``` ```
In the Polymer element definition: In the Polymer element definition:
``` ```ts
static get properties() { static get properties() {
return { return {
loading: Boolean, loading: Boolean,
@ -519,12 +519,12 @@ private computeHideButton_(): boolean {
This could be rewritten in Lit, omitting the `hideButton_` property entirely. This could be rewritten in Lit, omitting the `hideButton_` property entirely.
Equivalent Lit HTML template snippet: Equivalent Lit HTML template snippet:
``` ```html
<cr-button ?hidden="${this.computeHideButton_()}">Click Me</cr-button> <cr-button ?hidden="${this.computeHideButton_()}">Click Me</cr-button>
``` ```
Equivalent Lit element definition: Equivalent Lit element definition:
``` ```ts
static get properties() { static get properties() {
return { return {
loading: {type: Boolean}, loading: {type: Boolean},
@ -543,7 +543,7 @@ In other cases, where computed properties are bound to other elements, used as
attributes, or are needed for other internal logic, they can be computed in the attributes, or are needed for other internal logic, they can be computed in the
`willUpdate()` lifecycle callback when the properties that they depend on change `willUpdate()` lifecycle callback when the properties that they depend on change
as in the following example: as in the following example:
``` ```ts
override willUpdate(changedProperties: PropertyValues<this>) { override willUpdate(changedProperties: PropertyValues<this>) {
super.willUpdate(changedProperties); super.willUpdate(changedProperties);
@ -567,7 +567,7 @@ internal logic or requires accessing the elements DOM:
than triggering a second round of updates. than triggering a second round of updates.
Consider the following Polymer code, with a complex observer: Consider the following Polymer code, with a complex observer:
``` ```ts
static get properties() { static get properties() {
return { return {
max: Number, max: Number,
@ -591,7 +591,7 @@ private onValueSet_() {
The Lit migrated code would look as follows, with the observer code split The Lit migrated code would look as follows, with the observer code split
into `willUpdate()` and `updated()` based on whether it accesses the DOM: into `willUpdate()` and `updated()` based on whether it accesses the DOM:
``` ```ts
static override get properties() { static override get properties() {
return { return {
max: {type: Number}, max: {type: Number},
@ -630,7 +630,7 @@ statements in the `.html.ts` file of the form
`cr-toolbar`: `cr-toolbar`:
Polymer `cr_toolbar.html`: Polymer `cr_toolbar.html`:
``` ```html
<div id="content"> <div id="content">
<template is="dom-if" if="[[showMenu]]" restamp> <template is="dom-if" if="[[showMenu]]" restamp>
<cr-icon-button id="menuButton" class="no-overlap" <cr-icon-button id="menuButton" class="no-overlap"
@ -642,7 +642,7 @@ Polymer `cr_toolbar.html`:
``` ```
Lit `cr_toolbar.html.ts`: Lit `cr_toolbar.html.ts`:
``` ```html
<div id="content"> <div id="content">
${this.showMenu ? html` ${this.showMenu ? html`
<cr-icon-button id="menuButton" class="no-overlap" <cr-icon-button id="menuButton" class="no-overlap"
@ -677,7 +677,7 @@ One possibility is to set the index or item as data attributes on elements that
fire events, as seen in the example that follows. fire events, as seen in the example that follows.
From the Polymer element template: From the Polymer element template:
``` ```html
<template is="dom-repeat" items="[[listItems]]"> <template is="dom-repeat" items="[[listItems]]">
<div class="item-container [[getSelectedClass_(item, selectedItem)]]"> <div class="item-container [[getSelectedClass_(item, selectedItem)]]">
<cr-button id="[[getItemId_(index)]]" on-click="onItemClick_"> <cr-button id="[[getItemId_(index)]]" on-click="onItemClick_">
@ -688,7 +688,7 @@ From the Polymer element template:
``` ```
From the Polymer element definition: From the Polymer element definition:
``` ```ts
private getItemId_(index: number): string { private getItemId_(index: number): string {
return 'listItemId' + index; return 'listItemId' + index;
} }
@ -709,7 +709,7 @@ private onItemClick_(e: DomRepeatEvent<ListItemType>) {
``` ```
Lit template: Lit template:
``` ```ts
${this.listItems.map((item, index) => html` ${this.listItems.map((item, index) => html`
<div class="item-container ${this.getSelectedClass_(item)}"> <div class="item-container ${this.getSelectedClass_(item)}">
<cr-button id="${this.getItemId_(index)}" <cr-button id="${this.getItemId_(index)}"
@ -725,7 +725,7 @@ Note the `data-index` setting the `data` attribute on the
*** ***
From the Lit element definition file: From the Lit element definition file:
``` ```ts
protected getItemId_(index: number): string { protected getItemId_(index: number): string {
return 'listItemId' + index; return 'listItemId' + index;
} }
@ -763,7 +763,7 @@ An example based on a simplified form of `cr-url-list-item`, which uses
composition, follows. composition, follows.
From the Polymer `.html` template: From the Polymer `.html` template:
``` ```html
<div class="folder-and-count"> <div class="folder-and-count">
<template is="dom-if" if="[[shouldShowFolderImages_(size)]]" restamp> <template is="dom-if" if="[[shouldShowFolderImages_(size)]]" restamp>
<template is="dom-repeat" items="[[imageUrls]]" <template is="dom-repeat" items="[[imageUrls]]"
@ -778,7 +778,7 @@ From the Polymer `.html` template:
``` ```
From the Polymer element definition: From the Polymer element definition:
``` ```ts
private shouldShowImageUrl_(_url: string, index: number) { private shouldShowImageUrl_(_url: string, index: number) {
return index <= 1; return index <= 1;
} }
@ -798,7 +798,7 @@ private getDisplayedCount_() {
``` ```
From the Lit `.html.ts` template file: From the Lit `.html.ts` template file:
``` ```ts
import {html} from '//resources/lit/v3_0/lit.rollup.js'; import {html} from '//resources/lit/v3_0/lit.rollup.js';
import type {CrUrlListItemElement} from './cr_url_list_item.js'; import type {CrUrlListItemElement} from './cr_url_list_item.js';
@ -842,7 +842,7 @@ export function getHtml(this: CrUrlListItemElement) {
``` ```
From the Lit element definition: From the Lit element definition:
``` ```ts
protected getDisplayedCount_(): string { protected getDisplayedCount_(): string {
if (this.count && this.count > 999) { if (this.count && this.count > 999) {
// The square to display the count only fits 3 characters. // The square to display the count only fits 3 characters.
@ -891,7 +891,7 @@ their `updated()` lifecycle callback whenever any property that may impact
their height has changed. See example below: their height has changed. See example below:
From the `list_parent.html` template (`iron-list` client so must be Polymer) From the `list_parent.html` template (`iron-list` client so must be Polymer)
``` ```html
<iron-list id="list" items="[[listItems_]]" as="item"> <iron-list id="list" items="[[listItems_]]" as="item">
<template> <template>
<custom-item description="[[item.description]]" name="[[item.name]]" <custom-item description="[[item.description]]" name="[[item.name]]"
@ -902,7 +902,7 @@ From the `list_parent.html` template (`iron-list` client so must be Polymer)
``` ```
From the child `custom_item.html.ts` template: From the child `custom_item.html.ts` template:
``` ```html
<div class="name">${this.name}</div> <div class="name">${this.name}</div>
<div class="description" ?hidden="${!this.description}"> <div class="description" ?hidden="${!this.description}">
${this.description} ${this.description}
@ -916,7 +916,7 @@ display gaps or overlap in the list. To prevent this, the child item should
fire `iron-resize` in `updated()` if its `description` property changes. fire `iron-resize` in `updated()` if its `description` property changes.
From `custom_item.ts`: From `custom_item.ts`:
``` ```ts
override updated(changedProperties: PropertyValues<this>) { override updated(changedProperties: PropertyValues<this>) {
super.updated(changedProperties); super.updated(changedProperties);
if (changedProperties.has('description')) { if (changedProperties.has('description')) {
@ -929,7 +929,7 @@ override updated(changedProperties: PropertyValues<this>) {
### Testing ### Testing
A large number of unit tests do something like the following: A large number of unit tests do something like the following:
``` ```ts
// Validate that the input is disabled when invalid is set. // Validate that the input is disabled when invalid is set.
myTestElement.invalid = true; myTestElement.invalid = true;
assertTrue(myTestElement.$.input.disabled); assertTrue(myTestElement.$.input.disabled);
@ -948,7 +948,7 @@ to do this:
render cycle). render cycle).
Updated example: Updated example:
``` ```ts
// Validate that the input is disabled when invalid is set. // Validate that the input is disabled when invalid is set.
myTestElement.invalid = true; myTestElement.invalid = true;
await microtasksFinished(); await microtasksFinished();