Merge "UI: Replace paper-toggle-button with md-switch"
diff --git a/Documentation/js_licenses.txt b/Documentation/js_licenses.txt
index e7840bc..0d6b48e 100644
--- a/Documentation/js_licenses.txt
+++ b/Documentation/js_licenses.txt
@@ -831,9 +831,9 @@
* @polymer/iron-resizable-behavior
* @polymer/iron-selector
* @polymer/iron-validatable-behavior
-* @polymer/marked-element
* @polymer/neon-animation
* @polymer/paper-behaviors
+* @polymer/paper-button
* @polymer/paper-card
* @polymer/paper-dropdown-menu
* @polymer/paper-fab
diff --git a/Documentation/licenses.txt b/Documentation/licenses.txt
index a5aa473..d37aae0 100644
--- a/Documentation/licenses.txt
+++ b/Documentation/licenses.txt
@@ -3436,9 +3436,9 @@
* @polymer/iron-resizable-behavior
* @polymer/iron-selector
* @polymer/iron-validatable-behavior
-* @polymer/marked-element
* @polymer/neon-animation
* @polymer/paper-behaviors
+* @polymer/paper-button
* @polymer/paper-card
* @polymer/paper-dropdown-menu
* @polymer/paper-fab
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 1c77e4a..dab63f6 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -198,7 +198,7 @@
--
Deletes the given account if config `enableDelete` under
-link:#config-gerrit.html#accounts[accounts] section is enbaled.
+link:#config-gerrit.html#accounts[accounts] section is enabled.
Currently only supporting self deletion (regardless of the way
link:#account-id[\{account-id\}] is provided).
diff --git a/java/com/google/gerrit/server/config/PreferencesParserUtil.java b/java/com/google/gerrit/server/config/PreferencesParserUtil.java
index 3e39e25..d7f19b0 100644
--- a/java/com/google/gerrit/server/config/PreferencesParserUtil.java
+++ b/java/com/google/gerrit/server/config/PreferencesParserUtil.java
@@ -231,13 +231,13 @@
my = new ArrayList<>();
}
if (my.isEmpty()) {
- my.add(new MenuItem("Dashboard", "#/dashboard/self", null));
- my.add(new MenuItem("Draft Comments", "#/q/has:draft", null));
- my.add(new MenuItem("Edits", "#/q/has:edit", null));
- my.add(new MenuItem("Watched Changes", "#/q/is:watched+is:open", null));
- my.add(new MenuItem("Starred Changes", "#/q/is:starred", null));
- my.add(new MenuItem("All Visible Changes", "#/q/is:visible", null));
- my.add(new MenuItem("Groups", "#/settings/#Groups", null));
+ my.add(new MenuItem("Dashboard", "/dashboard/self", null));
+ my.add(new MenuItem("Draft Comments", "/q/has:draft", null));
+ my.add(new MenuItem("Edits", "/q/has:edit", null));
+ my.add(new MenuItem("Watched Changes", "/q/is:watched+is:open", null));
+ my.add(new MenuItem("Starred Changes", "/q/is:starred", null));
+ my.add(new MenuItem("All Visible Changes", "/q/is:visible", null));
+ my.add(new MenuItem("Groups", "/settings/#Groups", null));
}
return my;
}
@@ -307,8 +307,9 @@
private static List<MenuItem> my(Config cfg) {
List<MenuItem> my = new ArrayList<>();
for (String subsection : cfg.getSubsections(UserConfigSections.MY)) {
- String url = my(cfg, subsection, KEY_URL, "#/");
- String target = my(cfg, subsection, KEY_TARGET, url.startsWith("#") ? null : "_blank");
+ String url = my(cfg, subsection, KEY_URL, "/");
+ boolean isRelative = url.startsWith("#") || url.startsWith("/");
+ String target = my(cfg, subsection, KEY_TARGET, isRelative ? null : "_blank");
my.add(new MenuItem(subsection, url, target, my(cfg, subsection, KEY_ID, null)));
}
return my;
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
index db038d1..20d42fb 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
@@ -57,13 +57,13 @@
assertPrefs(o, GeneralPreferencesInfo.defaults(), "my", "changeTable");
assertThat(o.my)
.containsExactly(
- new MenuItem("Dashboard", "#/dashboard/self", null),
- new MenuItem("Draft Comments", "#/q/has:draft", null),
- new MenuItem("Edits", "#/q/has:edit", null),
- new MenuItem("Watched Changes", "#/q/is:watched+is:open", null),
- new MenuItem("Starred Changes", "#/q/is:starred", null),
- new MenuItem("All Visible Changes", "#/q/is:visible", null),
- new MenuItem("Groups", "#/settings/#Groups", null));
+ new MenuItem("Dashboard", "/dashboard/self", null),
+ new MenuItem("Draft Comments", "/q/has:draft", null),
+ new MenuItem("Edits", "/q/has:edit", null),
+ new MenuItem("Watched Changes", "/q/is:watched+is:open", null),
+ new MenuItem("Starred Changes", "/q/is:starred", null),
+ new MenuItem("All Visible Changes", "/q/is:visible", null),
+ new MenuItem("Groups", "/settings/#Groups", null));
assertThat(o.changeTable).isEmpty();
GeneralPreferencesInfo i = GeneralPreferencesInfo.defaults();
diff --git a/polygerrit-ui/app/api/suggestions.ts b/polygerrit-ui/app/api/suggestions.ts
index c0e8d27..8ea6302 100644
--- a/polygerrit-ui/app/api/suggestions.ts
+++ b/polygerrit-ui/app/api/suggestions.ts
@@ -128,4 +128,9 @@
NO_SUGGESTION = 'NO_SUGGESTION',
OUT_OF_RANGE = 'OUT_OF_RANGE',
ERROR = 'ERROR',
+ FILE_TOO_LARGE = 'FILE_TOO_LARGE',
+ INVALID_RANGE = 'INVALID_RANGE',
+ UNSUPPORTED_FILE_TYPE = 'UNSUPPORTED_FILE_TYPE',
+ USER_QUOTA_EXCEEDED = 'USER_QUOTA_EXCEEDED',
+ OK_LOW_CONFIDENCE = 'OK_LOW_CONFIDENCE',
}
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.ts b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.ts
index 3524d2f..079ff7d 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.ts
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.ts
@@ -988,17 +988,9 @@
// private but used in test
createHeaderLink(linkObj: TopMenuItemInfo): MainHeaderLink {
- // Delete target property due to complications of
- // https://issues.gerritcodereview.com/issues/40006107
- //
- // The server tries to guess whether URL is a view within the UI.
- // If not, it sets target='_blank' on the menu item. The server
- // makes assumptions that work for the GWT UI, but not PolyGerrit,
- // so we'll just disable it altogether for now.
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const {target, ...headerLink} = {...linkObj};
+ const headerLink = {...linkObj};
- // Normalize all urls to PolyGerrit style.
+ // Normalize all GWT style URLs to PolyGerrit style.
if (headerLink.url.startsWith('#')) {
headerLink.url = linkObj.url.slice(1);
}
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.ts b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.ts
index b5e28be..dc4902a 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.ts
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.ts
@@ -309,11 +309,11 @@
assert.deepEqual(
[
{url: 'https://awesometown.com/#hashyhash', name: '', target: ''},
- {url: 'url', name: '', target: '_blank'},
+ {url: '#/q/is:nice', name: '', target: '_blank'},
].map(element.createHeaderLink),
[
- {url: 'https://awesometown.com/#hashyhash', name: ''},
- {url: 'url', name: ''},
+ {url: 'https://awesometown.com/#hashyhash', name: '', target: ''},
+ {url: '/q/is:nice', name: '', target: '_blank'},
]
);
});
@@ -428,6 +428,7 @@
links: [
{
name: 'Manage',
+ target: '_blank',
url: 'https://gerrit/plugins/plugin-manager/static/index.html',
},
],
@@ -449,12 +450,10 @@
items: [
{
name: 'Project Settings',
- target: '_blank',
url: '/plugins/myplugin/${projectName}',
},
{
name: 'Project List',
- target: '_blank',
url: '/plugins/myplugin/index.html',
},
],
@@ -493,7 +492,6 @@
items: [
{
name: 'Manage',
- target: '_blank',
url: 'https://gerrit/plugins/plugin-manager/static/index.html',
},
],
@@ -503,7 +501,6 @@
items: [
{
name: 'Create',
- target: '_blank',
url: 'https://gerrit/plugins/plugin-manager/static/create.html',
},
],
@@ -550,7 +547,6 @@
items: [
{
name: 'Manage',
- target: '_blank',
url: 'https://gerrit/plugins/plugin-manager/static/index.html',
},
],
@@ -589,7 +585,6 @@
items: [
{
name: 'Manage',
- target: '_blank',
url: 'https://gerrit/plugins/plugin-manager/static/index.html',
},
],
@@ -652,6 +647,7 @@
adminLinks[0],
{
name: 'Manage',
+ target: '_blank',
url: 'https://gerrit/plugins/plugin-manager/static/index.html',
},
],
diff --git a/polygerrit-ui/app/elements/settings/gr-preferences/gr-preferences.ts b/polygerrit-ui/app/elements/settings/gr-preferences/gr-preferences.ts
index 17c9f6d..cea96cf 100644
--- a/polygerrit-ui/app/elements/settings/gr-preferences/gr-preferences.ts
+++ b/polygerrit-ui/app/elements/settings/gr-preferences/gr-preferences.ts
@@ -124,13 +124,15 @@
() => this.getConfigModel().docsBaseUrl$,
docsBaseUrl => (this.docsBaseUrl = docsBaseUrl)
);
- subscribe(
- this,
- () => this.getPluginLoader().pluginsModel.suggestionsPlugins$,
- // We currently support results from only 1 provider.
- suggestionsPlugins =>
- (this.suggestionsProvider = suggestionsPlugins?.[0]?.provider)
- );
+ if (this.flagsService.isEnabled(KnownExperimentId.ML_SUGGESTED_EDIT_V2)) {
+ subscribe(
+ this,
+ () => this.getPluginLoader().pluginsModel.suggestionsPlugins$,
+ // We currently support results from only 1 provider.
+ suggestionsPlugins =>
+ (this.suggestionsProvider = suggestionsPlugins?.[0]?.provider)
+ );
+ }
}
static override get styles() {
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button.ts b/polygerrit-ui/app/elements/shared/gr-button/gr-button.ts
index 3a585dd..e01fdf4 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.ts
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.ts
@@ -122,6 +122,9 @@
/* Brings back the round corners we had with paper-button */
--md-text-button-container-shape: 4px;
--md-elevated-button-container-shape: 4px;
+ /* We have a variable for setting the text colour when it is disabled */
+ --md-elevated-button-disabled-label-text-color: var(--text-color);
+ --md-text-button-disabled-label-text-color: var(--text-color);
align-items: center;
background-color: var(--background-color);
color: var(--text-color);
@@ -187,6 +190,9 @@
min-width: 3em;
--md-sys-color-primary: var(--vote-text-color);
}
+ .loadingSpin {
+ display: inline-block;
+ }
`,
];
}
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
index 7236aa2..36b5eda 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
@@ -446,16 +446,18 @@
this.autocompleteComment();
}
);
- subscribe(
- this,
- () =>
- this.generateSuggestionTrigger$.pipe(
- debounceTime(GENERATE_SUGGESTION_DEBOUNCE_DELAY_MS)
- ),
- () => {
- this.generateSuggestEdit();
- }
- );
+ if (this.flagsService.isEnabled(KnownExperimentId.ML_SUGGESTED_EDIT_V2)) {
+ subscribe(
+ this,
+ () =>
+ this.generateSuggestionTrigger$.pipe(
+ debounceTime(GENERATE_SUGGESTION_DEBOUNCE_DELAY_MS)
+ ),
+ () => {
+ this.generateSuggestEdit();
+ }
+ );
+ }
subscribe(
this,
() => this.getUserModel().preferences$,
@@ -1119,6 +1121,7 @@
// private but used in test
showGeneratedSuggestion() {
return (
+ this.flagsService.isEnabled(KnownExperimentId.ML_SUGGESTED_EDIT_V2) &&
this.getSuggestionsService().isGeneratedSuggestedFixEnabledForComment(
this.comment
) &&
@@ -1226,6 +1229,7 @@
private async generateSuggestEdit() {
if (
+ !this.flagsService.isEnabled(KnownExperimentId.ML_SUGGESTED_EDIT_V2) ||
!this.showGeneratedSuggestion() ||
!this.generateSuggestion ||
this.messageText.length === 0
@@ -1635,6 +1639,8 @@
}
getFixSuggestions(): FixSuggestionInfo[] | undefined {
+ if (!this.flagsService.isEnabled(KnownExperimentId.ML_SUGGESTED_EDIT_V2))
+ return undefined;
if (!this.generateSuggestion) return undefined;
if (!this.generatedFixSuggestion) return undefined;
// Disable fix suggestions when the comment already has a user suggestion
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.ts b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.ts
index ebd0788..d4537fa 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.ts
@@ -46,6 +46,7 @@
CommentsModel,
commentsModelToken,
} from '../../../models/comments/comments-model';
+import {KnownExperimentId} from '../../../services/flags/flags';
import {GrSuggestionDiffPreview} from '../gr-suggestion-diff-preview/gr-suggestion-diff-preview';
suite('gr-comment tests', () => {
@@ -891,6 +892,11 @@
},
],
};
+ setup(async () => {
+ stubFlags('isEnabled')
+ .withArgs(KnownExperimentId.ML_SUGGESTED_EDIT_V2)
+ .returns(true);
+ });
test('renders suggestions in comment', async () => {
const comment = {
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.ts b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.ts
index 87b9f72..2bf84fe7 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.ts
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.ts
@@ -11,7 +11,6 @@
sanitizeHtmlToFragment,
} from '../../../utils/inner-html-util';
import {unescapeHTML} from '../../../utils/syntax-util';
-import '@polymer/marked-element';
import {resolve} from '../../../models/dependency';
import {subscribe} from '../../lit/subscription-controller';
import {configModelToken} from '../../../models/config/config-model';
@@ -24,6 +23,7 @@
USER_SUGGESTION_INFO_STRING,
} from '../../../utils/comment-util';
import {sameOrigin} from '../../../utils/url-util';
+import '../gr-marked-element/gr-marked-element';
// MIME types for images we allow showing. Do not include SVG, it can contain
// arbitrary JavaScript.
@@ -244,11 +244,11 @@
const allowMarkdownBase64ImagesInComments =
this.allowMarkdownBase64ImagesInComments;
- // We are overriding some marked-element renderers for a few reasons:
+ // We are overriding some gr-marked-element renderers for a few reasons:
// 1. Disable inline images as a design/policy choice.
// 2. Inline code blocks ("codespan") do not unescape HTML characters when
// rendering without <pre> and so we must do this manually.
- // <marked-element> is already escaping these internally. See test
+ // <gr-marked-element> is already escaping these internally. See test
// covering this.
// 3. Multiline code blocks ("code") is similarly handling escaped
// characters using <pre>. The convention is to only use <pre> for multi-
@@ -304,7 +304,7 @@
return `<pre><code>${text}</code></pre>`;
}
};
- // <marked-element> internals will be in charge of calling our custom
+ // <gr-marked-element> internals will be in charge of calling our custom
// renderer so we write these functions separately so that 'this' is
// preserved via closure.
renderer['paragraph'] = boundRewriteAsterisks;
@@ -313,10 +313,10 @@
// The child with slot is optional but allows us control over the styling.
// The `callback` property lets us do a final sanitization of the output
- // HTML string before it is rendered by `<marked-element>` in case any
+ // HTML string before it is rendered by `<gr-marked-element>` in case any
// rewrites have been abused to attempt an XSS attack.
return html`
- <marked-element
+ <gr-marked-element
.markdown=${this.escapeAllButBlockQuotes(this.content)}
.breaks=${true}
.renderer=${customRenderer}
@@ -324,7 +324,7 @@
sanitizeHtml(contents)}
>
<div class="markdown-html" slot="markdown-html"></div>
- </marked-element>
+ </gr-marked-element>
`;
}
@@ -349,10 +349,21 @@
}
override updated() {
+ this.removeEventListener(
+ 'marked-render-complete',
+ this.markedRenderComplete
+ );
+ // When masked-element was using Polymer, it was rendered synchronously,
+ // compared to the lit version of the element. updated() ran before the markdown
+ // was inserted into the slot.
+ this.addEventListener('marked-render-complete', this.markedRenderComplete);
+ }
+
+ readonly markedRenderComplete = () => {
// Look for @mentions and replace them with an account-label chip.
this.convertEmailsToAccountChips();
this.convertCodeToSuggestions();
- }
+ };
private convertEmailsToAccountChips() {
for (const emailLink of this.renderRoot.querySelectorAll(
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.ts b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.ts
index 1f751e5..113a6cd 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.ts
@@ -307,7 +307,7 @@
element,
/* HTML */ `
<gr-endpoint-decorator name="formatted-text-endpoint">
- <marked-element>
+ <gr-marked-element>
<div slot="markdown-html" class="markdown-html">
<p>text</p>
<p>
@@ -342,7 +342,7 @@
</a>
</p>
</div>
- </marked-element>
+ </gr-marked-element>
</gr-endpoint-decorator>
`
);
@@ -409,7 +409,7 @@
element,
/* HTML */ `
<gr-endpoint-decorator name="formatted-text-endpoint">
- <marked-element>
+ <gr-marked-element>
<div slot="markdown-html" class="markdown-html">
<h1>h1-heading</h1>
<h2>h2-heading</h2>
@@ -438,7 +438,7 @@
</a>
</h1>
</div>
- </marked-element>
+ </gr-marked-element>
</gr-endpoint-decorator>
`
);
@@ -454,7 +454,7 @@
element,
/* HTML */ `
<gr-endpoint-decorator name="formatted-text-endpoint">
- <marked-element>
+ <gr-marked-element>
<div slot="markdown-html" class="markdown-html">
<p>
<code>inline code</code>
@@ -466,7 +466,7 @@
<code>inline code with config link: LinkRewriteMe</code>
</p>
</div>
- </marked-element>
+ </gr-marked-element>
</gr-endpoint-decorator>
`
);
@@ -482,7 +482,7 @@
element,
/* HTML */ `
<gr-endpoint-decorator name="formatted-text-endpoint">
- <marked-element>
+ <gr-marked-element>
<div slot="markdown-html" class="markdown-html">
<pre>
<code>multiline code</code>
@@ -494,7 +494,7 @@
<code>multiline code with config link: LinkRewriteMe</code>
</pre>
</div>
- </marked-element>
+ </gr-marked-element>
</gr-endpoint-decorator>
`
);
@@ -508,11 +508,11 @@
element,
/* HTML */ `
<gr-endpoint-decorator name="formatted-text-endpoint">
- <marked-element>
+ <gr-marked-element>
<div slot="markdown-html" class="markdown-html">
<p></p>
</div>
- </marked-element>
+ </gr-marked-element>
</gr-endpoint-decorator>
`
);
@@ -526,13 +526,13 @@
element,
/* HTML */ `
<gr-endpoint-decorator name="formatted-text-endpoint">
- <marked-element>
+ <gr-marked-element>
<div slot="markdown-html" class="markdown-html">
<p>
<gr-account-chip></gr-account-chip>
</p>
</div>
- </marked-element>
+ </gr-marked-element>
</gr-endpoint-decorator>
`
);
@@ -554,7 +554,7 @@
element,
/* HTML */ `
<gr-endpoint-decorator name="formatted-text-endpoint">
- <marked-element>
+ <gr-marked-element>
<div slot="markdown-html" class="markdown-html">
<p>
<code>@</code>
@@ -567,7 +567,7 @@
</a>
</p>
</div>
- </marked-element>
+ </gr-marked-element>
</gr-endpoint-decorator>
`
);
@@ -588,7 +588,7 @@
element,
/* HTML */ `
<gr-endpoint-decorator name="formatted-text-endpoint">
- <marked-element>
+ <gr-marked-element>
<div slot="markdown-html" class="markdown-html">
<p>
<a
@@ -624,7 +624,7 @@
>
</p>
</div>
- </marked-element>
+ </gr-marked-element>
</gr-endpoint-decorator>
`
);
@@ -640,7 +640,7 @@
element,
/* HTML */ `
<gr-endpoint-decorator name="formatted-text-endpoint">
- <marked-element>
+ <gr-marked-element>
<div slot="markdown-html" class="markdown-html">
<blockquote>
<p>block quote</p>
@@ -670,7 +670,7 @@
</p>
</blockquote>
</div>
- </marked-element>
+ </gr-marked-element>
</gr-endpoint-decorator>
`
);
@@ -689,7 +689,7 @@
element,
/* HTML */ `
<gr-endpoint-decorator name="formatted-text-endpoint">
- <marked-element>
+ <gr-marked-element>
<div slot="markdown-html" class="markdown-html">
<p>plain text ${escapedDiv}</p>
<p>
@@ -712,7 +712,7 @@
>
</p>
</div>
- </marked-element>
+ </gr-marked-element>
</gr-endpoint-decorator>
`
);
@@ -726,7 +726,7 @@
element,
/* HTML */ `
<gr-endpoint-decorator name="formatted-text-endpoint">
- <marked-element>
+ <gr-marked-element>
<div slot="markdown-html" class="markdown-html">
<blockquote>
<blockquote>
@@ -736,7 +736,7 @@
</blockquote>
</blockquote>
</div>
- </marked-element>
+ </gr-marked-element>
</gr-endpoint-decorator>
`
);
@@ -757,7 +757,7 @@
element,
/* HTML */ `
<gr-endpoint-decorator name="formatted-text-endpoint">
- <marked-element>
+ <gr-marked-element>
<div slot="markdown-html" class="markdown-html">
<p>
I think
@@ -769,7 +769,7 @@
>
</p>
</div>
- </marked-element>
+ </gr-marked-element>
</gr-endpoint-decorator>
`
);
@@ -836,11 +836,11 @@
/* HTML */
`
<gr-endpoint-decorator name="formatted-text-endpoint">
- <marked-element>
+ <gr-marked-element>
<div class="markdown-html" slot="markdown-html">
<gr-user-suggestion-fix>Hello World</gr-user-suggestion-fix>
</div>
- </marked-element>
+ </gr-marked-element>
</gr-endpoint-decorator>
`
);
diff --git a/polygerrit-ui/app/elements/shared/gr-marked-element/gr-marked-element.ts b/polygerrit-ui/app/elements/shared/gr-marked-element/gr-marked-element.ts
new file mode 100644
index 0000000..5aeb2cb
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-marked-element/gr-marked-element.ts
@@ -0,0 +1,141 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {css, html, LitElement, PropertyValues} from 'lit';
+import {
+ customElement,
+ property,
+ queryAssignedElements,
+} from 'lit/decorators.js';
+// @ts-ignore
+import * as marked from 'marked/lib/marked';
+
+if (!window.marked) {
+ window.marked = marked;
+}
+
+declare global {
+ interface Window {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ marked: any;
+ }
+
+ interface HTMLElementTagNameMap {
+ 'gr-marked-element': GrMarkedElement;
+ }
+}
+
+/**
+ * This is based on [marked-element](https://github.com/PolymerElements/marked-element) by Polymer
+ * but converted to use Lit. It uses the [marked](https://github.com/markedjs/marked) library.
+ */
+@customElement('gr-marked-element')
+export class GrMarkedElement extends LitElement {
+ @property({type: String}) markdown: string | null = null;
+
+ @property({type: Boolean}) breaks = false;
+
+ @property({type: Boolean}) pedantic = false;
+
+ @property({type: Function}) renderer: Function | null = null;
+
+ @property({type: Boolean}) sanitize = false;
+
+ @property({type: Function}) sanitizer:
+ | ((html: string) => string)
+ | undefined = undefined;
+
+ @property({type: Boolean}) smartypants = false;
+
+ @property({type: Function}) callback: Function | null = null;
+
+ @queryAssignedElements({
+ flatten: true,
+ slot: 'markdown-html',
+ })
+ private outputElement!: Array<HTMLElement>;
+
+ static override styles = css`
+ :host {
+ display: block;
+ }
+ `;
+
+ override render() {
+ return html`
+ <slot name="markdown-html">
+ <div id="content" slot="markdown-html"></div>
+ </slot>
+ `;
+ }
+
+ override connectedCallback() {
+ super.connectedCallback();
+ this.renderMarkdown();
+ }
+
+ protected override updated(changedProps: PropertyValues) {
+ const propsToWatch = [
+ 'markdown',
+ 'breaks',
+ 'pedantic',
+ 'renderer',
+ 'sanitize',
+ 'sanitizer',
+ 'smartypants',
+ 'callback',
+ ];
+
+ if (propsToWatch.some(prop => changedProps.has(prop))) {
+ this.renderMarkdown();
+ }
+ }
+
+ override firstUpdated() {
+ this.renderMarkdown();
+ }
+
+ private renderMarkdown() {
+ if (!this.isConnected || !this.outputElement.length) {
+ return;
+ }
+
+ if (!this.markdown) {
+ this.outputElement[0].innerHTML = '';
+ return;
+ }
+
+ const renderer = new window.marked.Renderer();
+ if (this.renderer) this.renderer(renderer);
+
+ const options = {
+ renderer,
+ highlight: this.highlight.bind(this),
+ breaks: this.breaks,
+ sanitize: this.sanitize,
+ sanitizer: this.sanitizer,
+ pedantic: this.pedantic,
+ smartypants: this.smartypants,
+ };
+
+ const output = window.marked(this.markdown, options, this.callback);
+
+ this.outputElement[0].innerHTML = output;
+ this.dispatchEvent(
+ new CustomEvent('marked-render-complete', {bubbles: true, composed: true})
+ );
+ }
+
+ private highlight(code: string, lang: string): string {
+ const event = new CustomEvent('syntax-highlight', {
+ detail: {code, lang},
+ bubbles: true,
+ composed: true,
+ });
+ this.dispatchEvent(event);
+ return event.detail.code || code;
+ }
+}
diff --git a/polygerrit-ui/app/elements/shared/gr-marked-element/gr-marked-element_test.ts b/polygerrit-ui/app/elements/shared/gr-marked-element/gr-marked-element_test.ts
new file mode 100644
index 0000000..01b2d0f
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-marked-element/gr-marked-element_test.ts
@@ -0,0 +1,31 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+import '../../../test/common-test-setup';
+import './gr-marked-element';
+import {assert, fixture, html} from '@open-wc/testing';
+import {GrMarkedElement} from './gr-marked-element';
+
+suite('gr-app-element tests', () => {
+ let element: GrMarkedElement;
+
+ setup(async () => {
+ element = await fixture<GrMarkedElement>(
+ html`<gr-marked-element></gr-marked-element>`
+ );
+ await element.updateComplete;
+ });
+
+ test('renders', () => {
+ assert.shadowDom.equal(
+ element,
+ /* HTML */ `
+ <slot name="markdown-html">
+ <div id="content" slot="markdown-html"></div>
+ </slot>
+ `
+ );
+ });
+});
diff --git a/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls.ts b/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls.ts
index cfb850d..82465ad 100644
--- a/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls.ts
+++ b/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls.ts
@@ -3,7 +3,7 @@
* Copyright 2021 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
-import '@material/web/button/text-button';
+import '@polymer/paper-button/paper-button';
import '@polymer/paper-tooltip/paper-tooltip';
import {EMPTY, of, Subject} from 'rxjs';
import {delay, switchMap} from 'rxjs/operators';
@@ -200,12 +200,10 @@
font: var(--context-control-button-font, inherit);
}
- md-text-button {
+ paper-button {
text-transform: none;
align-items: center;
background-color: var(--background-color);
- --md-text-button-label-text-font: inherit;
- /* This is also set in the button-label-font css var above. We keep this incase it is also needed. */
font-family: inherit;
margin: var(--margin, 0);
min-width: var(--border, 0);
@@ -214,26 +212,13 @@
border-width: 1px;
border-radius: var(--border-radius);
padding: var(--spacing-s) var(--spacing-l);
- --md-text-button-container-color: var(--background-color);
- --md-sys-color-primary: var(--diff-context-control-color);
- /* We set this to 0 which defaults to 12px under text-button.
- This is because for some reason it makes the size of it bigger
- which makes some of the buttons look wrong. E.g. attention set has
- a bigger width and thus a lot of space. This fixes it. */
- --md-text-button-leading-space: 0;
- --md-text-button-trailing-space: 0;
- /* Brings back the round corners we had with paper-button */
- --md-text-button-container-shape: 4px;
- /* Needed to resize properly */
- min-height: auto;
- height: auto;
}
- md-text-button:hover {
+ paper-button:hover {
/* same as defined in gr-button */
background: rgba(0, 0, 0, 0.12);
}
- md-text-button:focus-visible {
+ paper-button:focus-visible {
/* paper-button sets this to 0, thus preventing focus-based styling. */
outline-width: 1px;
}
@@ -457,7 +442,7 @@
});
};
- const button = html` <md-text-button
+ const button = html` <paper-button
class=${classes}
aria-label=${ariaLabel}
@click=${expandHandler}
@@ -466,7 +451,7 @@
>
<span class="showContext">${text}</span>
${tooltip}
- </md-text-button>`;
+ </paper-button>`;
return button;
}
diff --git a/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls_test.ts b/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls_test.ts
index a65a11e..b08a2e8 100644
--- a/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls_test.ts
+++ b/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls_test.ts
@@ -33,7 +33,7 @@
await waitEventLoop();
const buttons = element.shadowRoot!.querySelectorAll(
- 'md-text-button.showContext'
+ 'paper-button.showContext'
);
assert.equal(buttons.length, 1);
assert.equal(buttons[0].textContent!.trim(), '+10 common lines');
@@ -45,7 +45,7 @@
await waitEventLoop();
const buttons = element.shadowRoot!.querySelectorAll(
- 'md-text-button.showContext'
+ 'paper-button.showContext'
);
assert.equal(buttons.length, 2);
@@ -62,7 +62,7 @@
await waitEventLoop();
const buttons = element.shadowRoot!.querySelectorAll(
- 'md-text-button.showContext'
+ 'paper-button.showContext'
);
assert.equal(buttons.length, 3);
@@ -81,7 +81,7 @@
await waitEventLoop();
const buttons = element.shadowRoot!.querySelectorAll(
- 'md-text-button.showContext'
+ 'paper-button.showContext'
);
assert.equal(buttons.length, 2);
@@ -104,13 +104,13 @@
await waitEventLoop();
const fullExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.fullExpansion md-text-button'
+ '.fullExpansion paper-button'
);
const partialExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.partialExpansion md-text-button'
+ '.partialExpansion paper-button'
);
const blockExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.blockExpansion md-text-button'
+ '.blockExpansion paper-button'
);
assert.equal(fullExpansionButtons.length, 1);
assert.equal(partialExpansionButtons.length, 1);
@@ -132,13 +132,13 @@
await waitEventLoop();
const fullExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.fullExpansion md-text-button'
+ '.fullExpansion paper-button'
);
const partialExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.partialExpansion md-text-button'
+ '.partialExpansion paper-button'
);
const blockExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.blockExpansion md-text-button'
+ '.blockExpansion paper-button'
);
assert.equal(fullExpansionButtons.length, 1);
assert.equal(partialExpansionButtons.length, 2);
@@ -168,13 +168,13 @@
await waitEventLoop();
const fullExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.fullExpansion md-text-button'
+ '.fullExpansion paper-button'
);
const partialExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.partialExpansion md-text-button'
+ '.partialExpansion paper-button'
);
const blockExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.blockExpansion md-text-button'
+ '.blockExpansion paper-button'
);
assert.equal(fullExpansionButtons.length, 1);
assert.equal(partialExpansionButtons.length, 1);
@@ -207,7 +207,7 @@
await waitEventLoop();
const blockExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.blockExpansion md-text-button'
+ '.blockExpansion paper-button'
);
assert.equal(
blockExpansionButtons[0]
@@ -258,7 +258,7 @@
await waitEventLoop();
const blockExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.blockExpansion md-text-button'
+ '.blockExpansion paper-button'
);
assert.equal(
blockExpansionButtons[0]
@@ -302,7 +302,7 @@
await waitEventLoop();
const blockExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.blockExpansion md-text-button'
+ '.blockExpansion paper-button'
);
assert.equal(
blockExpansionButtons[0]
@@ -319,7 +319,7 @@
await waitEventLoop();
const blockExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.blockExpansion md-text-button'
+ '.blockExpansion paper-button'
);
const tooltipAbove =
blockExpansionButtons[0].querySelector('paper-tooltip')!;
@@ -342,7 +342,7 @@
await waitEventLoop();
const buttons = element.shadowRoot!.querySelectorAll(
- 'md-text-button.showContext'
+ 'paper-button.showContext'
);
assert.equal(buttons.length, 1);
assert.equal(buttons[0].textContent!.trim(), '+ Unrelated changes');
diff --git a/polygerrit-ui/app/node_modules_licenses/licenses.ts b/polygerrit-ui/app/node_modules_licenses/licenses.ts
index 7b99808..e7bcdb79 100644
--- a/polygerrit-ui/app/node_modules_licenses/licenses.ts
+++ b/polygerrit-ui/app/node_modules_licenses/licenses.ts
@@ -213,10 +213,6 @@
license: SharedLicenses.Polymer2015,
},
{
- name: '@polymer/marked-element',
- license: SharedLicenses.Polymer2015,
- },
- {
name: '@polymer/neon-animation',
license: SharedLicenses.Polymer2015,
},
@@ -225,6 +221,10 @@
license: SharedLicenses.Polymer2015,
},
{
+ name: '@polymer/paper-button',
+ license: SharedLicenses.Polymer2015,
+ },
+ {
name: '@polymer/paper-card',
license: SharedLicenses.Polymer2015,
},
diff --git a/polygerrit-ui/app/package.json b/polygerrit-ui/app/package.json
index 55504dc..81a98cb 100644
--- a/polygerrit-ui/app/package.json
+++ b/polygerrit-ui/app/package.json
@@ -13,7 +13,7 @@
"@polymer/iron-iconset-svg": "^3.0.1",
"@polymer/iron-input": "^3.0.1",
"@polymer/iron-selector": "^3.0.1",
- "@polymer/marked-element": "^3.0.1",
+ "@polymer/paper-button": "^3.0.1",
"@polymer/paper-card": "^3.0.1",
"@polymer/paper-checkbox": "^3.1.0",
"@polymer/paper-dropdown-menu": "^3.2.0",
@@ -36,6 +36,7 @@
"highlightjs-vue": "https://github.com/paladox/highlightjs-vue#44eed074ea0110d1ad03d2cbd77d27027cf7bb04",
"immer": "^9.0.21",
"lit": "^3.2.1",
+ "marked": "^0.5.0",
"polymer-bridges": "file:../../polymer-bridges",
"polymer-resin": "^2.0.1",
"resemblejs": "^5.0.0",
diff --git a/polygerrit-ui/app/services/flags/flags.ts b/polygerrit-ui/app/services/flags/flags.ts
index 144b260..86d8a39 100644
--- a/polygerrit-ui/app/services/flags/flags.ts
+++ b/polygerrit-ui/app/services/flags/flags.ts
@@ -18,6 +18,7 @@
NEW_IMAGE_DIFF_UI = 'UiFeature__new_image_diff_ui',
CHECKS_DEVELOPER = 'UiFeature__checks_developer',
PUSH_NOTIFICATIONS_DEVELOPER = 'UiFeature__push_notifications_developer',
+ ML_SUGGESTED_EDIT_V2 = 'UiFeature__ml_suggested_edit_v2',
REVISION_PARENTS_DATA = 'UiFeature__revision_parents_data',
COMMENT_AUTOCOMPLETION = 'UiFeature__comment_autocompletion_enabled',
SAVE_PROJECT_CONFIG_FOR_REVIEW = 'UiFeature__save_project_config_for_review',
diff --git a/polygerrit-ui/app/types/common.ts b/polygerrit-ui/app/types/common.ts
index 536ebdf..6c577c1 100644
--- a/polygerrit-ui/app/types/common.ts
+++ b/polygerrit-ui/app/types/common.ts
@@ -518,7 +518,7 @@
export interface TopMenuItemInfo {
url: string;
name: string;
- target: string;
+ target?: string;
id?: string;
}
diff --git a/polygerrit-ui/app/yarn.lock b/polygerrit-ui/app/yarn.lock
index ab5b397..4ea578f 100644
--- a/polygerrit-ui/app/yarn.lock
+++ b/polygerrit-ui/app/yarn.lock
@@ -208,14 +208,6 @@
"@polymer/iron-meta" "^3.0.0-pre.26"
"@polymer/polymer" "^3.0.0"
-"@polymer/marked-element@^3.0.1":
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/@polymer/marked-element/-/marked-element-3.0.1.tgz#56add62080404dea142c055977807ae9ca773a89"
- integrity sha512-WJQzQetxdStVGQbyTBUBgd+hSI0Rl39uJg7b2zL3r6EfMnibzmA/YNT06M8jVZdxPF+B4SumrFWRtasVtGQRUQ==
- dependencies:
- "@polymer/polymer" "^3.0.0"
- marked "~0.3.9"
-
"@polymer/neon-animation@^3.0.0-pre.26":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@polymer/neon-animation/-/neon-animation-3.0.1.tgz#6658e4b524abc057477772a7473292493d366c24"
@@ -235,6 +227,16 @@
"@polymer/paper-ripple" "^3.0.0-pre.26"
"@polymer/polymer" "^3.0.0"
+"@polymer/paper-button@^3.0.1":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@polymer/paper-button/-/paper-button-3.0.1.tgz#f13b019137e3f6ccc4d04d0b1f27f4830ea5774d"
+ integrity sha512-JRNBc+Oj9EWnmyLr7FcCr8T1KAnEHPh6mosln9BUdkM+qYaYsudSICh3cjTIbnj6AuF5OJidoLkM1dlyj0j6Zg==
+ dependencies:
+ "@polymer/iron-flex-layout" "^3.0.0-pre.26"
+ "@polymer/paper-behaviors" "^3.0.0-pre.27"
+ "@polymer/paper-styles" "^3.0.0-pre.26"
+ "@polymer/polymer" "^3.0.0"
+
"@polymer/paper-card@^3.0.1":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@polymer/paper-card/-/paper-card-3.0.1.tgz#fb5960b3e55fab56d20b7c1c3dee08f0d052ff2a"
@@ -659,10 +661,10 @@
dependencies:
semver "^6.0.0"
-marked@~0.3.9:
- version "0.3.19"
- resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.19.tgz#5d47f709c4c9fc3c216b6d46127280f40b39d790"
- integrity sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==
+marked@^0.5.0:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/marked/-/marked-0.5.2.tgz#3efdb27b1fd0ecec4f5aba362bddcd18120e5ba9"
+ integrity sha512-fdZvBa7/vSQIZCi4uuwo2N3q+7jJURpMVCcbaX0S1Mg65WZ5ilXvC67MviJAsdjqqgD+CEq4RKo5AYGgINkVAA==
mimic-response@^2.0.0:
version "2.1.0"