Add link from `position-fallback` property to `@position-fallback` rule
Renamed `AnimationNameSwatch` to be `LinkSwatch` and reused it for linking from `position-fallback` property declaration.
See the screenshot: https://bugs.chromium.org/p/chromium/issues/attachment?aid=593023&signed_aid=KIDmOt1aBxuH53HF29QrTg==&inline=1
Bug: 1412227
Change-Id: If1a1e32cff6d07426f35085e0d1c38f5c7c98822
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/4402908
Commit-Queue: Ergün Erdoğmuş <ergunsh@chromium.org>
Reviewed-by: Changhao Han <changhaohan@chromium.org>
Auto-Submit: Ergün Erdoğmuş <ergunsh@chromium.org>
diff --git a/front_end/panels/elements/StylePropertyTreeElement.ts b/front_end/panels/elements/StylePropertyTreeElement.ts
index f38a4a6..eea5862 100644
--- a/front_end/panels/elements/StylePropertyTreeElement.ts
+++ b/front_end/panels/elements/StylePropertyTreeElement.ts
@@ -281,13 +281,13 @@
const contentChild = document.createElement('span');
for (let i = 0; i < animationNames.length; i++) {
const animationName = animationNames[i];
- const swatch = new InlineEditor.LinkSwatch.AnimationNameSwatch();
+ const swatch = new InlineEditor.LinkSwatch.LinkSwatch();
UI.UIUtils.createTextChild(swatch, animationName);
const isDefined = Boolean(this.matchedStylesInternal.keyframes().find(kf => kf.name().text === animationName));
swatch.data = {
text: animationName,
isDefined,
- onLinkActivate: this.handleAnimationNameDefinitionActivate.bind(this),
+ onLinkActivate: () => this.parentPaneInternal.jumpToSectionBlock(`@keyframes ${animationName}`),
};
contentChild.appendChild(swatch);
@@ -335,6 +335,22 @@
return contentChild;
}
+ private processPositionFallback(propertyText: string): Node {
+ const contentChild = document.createElement('span');
+ const swatch = new InlineEditor.LinkSwatch.LinkSwatch();
+ UI.UIUtils.createTextChild(swatch, propertyText);
+ const isDefined =
+ Boolean(this.matchedStylesInternal.positionFallbackRules().find(pf => pf.name().text === propertyText));
+ swatch.data = {
+ text: propertyText,
+ isDefined,
+ onLinkActivate: () => this.parentPaneInternal.jumpToSectionBlock(`@position-fallback ${propertyText}`),
+ };
+ contentChild.appendChild(swatch);
+
+ return contentChild;
+ }
+
private processColor(text: string, valueChild?: Node|null): Node {
return this.renderColorSwatch(text, valueChild);
}
@@ -475,10 +491,6 @@
return this.processColor(computedValue, varSwatch);
}
- private handleAnimationNameDefinitionActivate(animationName: string): void {
- this.parentPaneInternal.jumpToSectionBlock(`@keyframes ${animationName}`);
- }
-
private handleVarDefinitionActivate(variableName: string): void {
Host.userMetrics.actionTaken(Host.UserMetrics.Action.CustomPropertyLinkClicked);
this.parentPaneInternal.jumpToProperty(variableName);
@@ -862,6 +874,7 @@
propertyRenderer.setGridHandler(this.processGrid.bind(this));
propertyRenderer.setAngleHandler(this.processAngle.bind(this));
propertyRenderer.setLengthHandler(this.processLength.bind(this));
+ propertyRenderer.setPositionFallbackHandler(this.processPositionFallback.bind(this));
}
this.listItemElement.removeChildren();
diff --git a/front_end/panels/elements/StylesSidebarPane.ts b/front_end/panels/elements/StylesSidebarPane.ts
index c83a4f6..600fc0e 100644
--- a/front_end/panels/elements/StylesSidebarPane.ts
+++ b/front_end/panels/elements/StylesSidebarPane.ts
@@ -2135,6 +2135,7 @@
private lengthHandler: ((arg0: string) => Node)|null;
private animationNameHandler: ((data: string) => Node)|null;
private animationHandler: ((data: string) => Node)|null;
+ private positionFallbackHandler: ((data: string) => Node)|null;
constructor(rule: SDK.CSSRule.CSSRule|null, node: SDK.DOMModel.DOMNode|null, name: string, value: string) {
this.rule = rule;
@@ -2152,6 +2153,7 @@
this.angleHandler = null;
this.lengthHandler = null;
this.animationHandler = null;
+ this.positionFallbackHandler = null;
}
setColorHandler(handler: (arg0: string) => Node): void {
@@ -2198,6 +2200,10 @@
this.lengthHandler = handler;
}
+ setPositionFallbackHandler(handler: (arg0: string) => Node): void {
+ this.positionFallbackHandler = handler;
+ }
+
renderName(): Element {
const nameElement = document.createElement('span');
UI.ARIAUtils.setAccessibleName(nameElement, i18nString(UIStrings.cssPropertyName, {PH1: this.propertyName}));
@@ -2292,6 +2298,12 @@
regexes.push(/^.*$/g);
processors.push(this.animationNameHandler);
}
+
+ if (this.positionFallbackHandler && this.propertyName === 'position-fallback') {
+ regexes.push(/^.*$/g);
+ processors.push(this.positionFallbackHandler);
+ }
+
const results = TextUtils.TextUtils.Utils.splitStringByRegexes(this.propertyValue, regexes);
for (let i = 0; i < results.length; i++) {
const result = results[i];
diff --git a/front_end/ui/legacy/components/inline_editor/LinkSwatch.ts b/front_end/ui/legacy/components/inline_editor/LinkSwatch.ts
index cf54b14..857c794 100644
--- a/front_end/ui/legacy/components/inline_editor/LinkSwatch.ts
+++ b/front_end/ui/legacy/components/inline_editor/LinkSwatch.ts
@@ -20,16 +20,15 @@
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
const {render, html, Directives} = LitHtml;
-interface LinkSwatchRenderData {
+interface BaseLinkSwatchRenderData {
text: string;
title: string;
isDefined: boolean;
onLinkActivate: (linkText: string) => void;
}
-/* eslint-disable-next-line rulesdir/check_component_naming */
-class LinkSwatch extends HTMLElement {
- static readonly litTagName = LitHtml.literal`devtools-link-swatch`;
+class BaseLinkSwatch extends HTMLElement {
+ static readonly litTagName = LitHtml.literal`devtools-base-link-swatch`;
protected readonly shadow = this.attachShadow({mode: 'open'});
protected onLinkActivate: (linkText: string, event: MouseEvent|KeyboardEvent) => void = () => undefined;
@@ -37,7 +36,7 @@
this.shadow.adoptedStyleSheets = [linkSwatchStyles];
}
- set data(data: LinkSwatchRenderData) {
+ set data(data: BaseLinkSwatchRenderData) {
this. string, event: MouseEvent|KeyboardEvent): void => {
if (event instanceof MouseEvent && event.button !== 0) {
return;
@@ -53,7 +52,7 @@
this.render(data);
}
- private render(data: LinkSwatchRenderData): void {
+ private render(data: BaseLinkSwatchRenderData): void {
const {isDefined, text, title} = data;
const classes = Directives.classMap({
'link-swatch-link': true,
@@ -153,50 +152,50 @@
const fallbackIncludeComma = functionParts.fallbackIncludeComma ? functionParts.fallbackIncludeComma : '';
render(
- html`<span title=${data.computedValue || ''}>${functionParts.pre}<${LinkSwatch.litTagName} .data=${
+ html`<span title=${data.computedValue || ''}>${functionParts.pre}<${BaseLinkSwatch.litTagName} .data=${
{title, text: functionParts.variableName, isDefined, onLinkActivate} as
- LinkSwatchRenderData}></${LinkSwatch.litTagName}>${fallbackIncludeComma}${functionParts.post}</span>`,
+ LinkSwatchRenderData}></${BaseLinkSwatch.litTagName}>${fallbackIncludeComma}${functionParts.post}</span>`,
this.shadow, {host: this});
}
}
-interface AnimationNameSwatchRenderData {
+interface LinkSwatchRenderData {
isDefined: boolean;
text: string;
onLinkActivate: (linkText: string) => void;
}
-export class AnimationNameSwatch extends HTMLElement {
- static readonly litTagName = LitHtml.literal`devtools-animation-name-swatch`;
+export class LinkSwatch extends HTMLElement {
+ static readonly litTagName = LitHtml.literal`devtools-link-swatch`;
protected readonly shadow = this.attachShadow({mode: 'open'});
- set data(data: AnimationNameSwatchRenderData) {
+ set data(data: LinkSwatchRenderData) {
this.render(data);
}
- protected render(data: AnimationNameSwatchRenderData): void {
+ protected render(data: LinkSwatchRenderData): void {
const {text, isDefined, onLinkActivate} = data;
const title = isDefined ? text : i18nString(UIStrings.sIsNotDefined, {PH1: text});
render(
- html`<span title=${data.text}><${LinkSwatch.litTagName} .data=${{
+ html`<span title=${data.text}><${BaseLinkSwatch.litTagName} .data=${{
text,
isDefined,
title,
onLinkActivate,
- } as LinkSwatchRenderData}></${LinkSwatch.litTagName}></span>`,
+ } as LinkSwatchRenderData}></${BaseLinkSwatch.litTagName}></span>`,
this.shadow, {host: this});
}
}
-ComponentHelpers.CustomElements.defineComponent('devtools-animation-name-swatch', AnimationNameSwatch);
-ComponentHelpers.CustomElements.defineComponent('devtools-css-var-swatch', CSSVarSwatch);
+ComponentHelpers.CustomElements.defineComponent('devtools-base-link-swatch', BaseLinkSwatch);
ComponentHelpers.CustomElements.defineComponent('devtools-link-swatch', LinkSwatch);
+ComponentHelpers.CustomElements.defineComponent('devtools-css-var-swatch', CSSVarSwatch);
declare global {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface HTMLElementTagNameMap {
+ 'devtools-base-link-swatch': BaseLinkSwatch;
'devtools-link-swatch': LinkSwatch;
- 'devtools-animation-name-swatch': AnimationNameSwatch;
'devtools-css-var-swatch': CSSVarSwatch;
}
}
diff --git a/test/unittests/front_end/panels/elements/StylePropertyTreeElement_test.ts b/test/unittests/front_end/panels/elements/StylePropertyTreeElement_test.ts
index 96faa4d..6d22771 100644
--- a/test/unittests/front_end/panels/elements/StylePropertyTreeElement_test.ts
+++ b/test/unittests/front_end/panels/elements/StylePropertyTreeElement_test.ts
@@ -143,8 +143,8 @@
});
});
- describe('animation-name swatch', () => {
- it('should be rendered for animation-name declaration', () => {
+ describe('animation-name', () => {
+ it('should link-swatch be rendered for animation-name declaration', () => {
const cssAnimationNameProperty = new SDK.CSSProperty.CSSProperty(
mockCssStyleDeclaration, 0, 'animation-name', 'first-keyframe', true, false, true, false, '', undefined);
const stylePropertyTreeElement = new Elements.StylePropertyTreeElement.StylePropertyTreeElement(
@@ -152,24 +152,24 @@
stylePropertyTreeElement.updateTitle();
- const animationNameSwatch =
- stylePropertyTreeElement.valueElement?.querySelector('devtools-animation-name-swatch');
+ const animationNameSwatch = stylePropertyTreeElement.valueElement?.querySelector('devtools-link-swatch');
assert.isNotNull(animationNameSwatch);
});
- it('should two swatches be rendered for animation-name declaration that contains two keyframe references', () => {
- const cssAnimationNameProperty = new SDK.CSSProperty.CSSProperty(
- mockCssStyleDeclaration, 0, 'animation-name', 'first-keyframe, second-keyframe', true, false, true, false,
- '', undefined);
- const stylePropertyTreeElement = new Elements.StylePropertyTreeElement.StylePropertyTreeElement(
- mockStylesSidebarPane, mockMatchedStyles, cssAnimationNameProperty, false, false, false, true);
+ it('should two link-swatches be rendered for animation-name declaration that contains two keyframe references',
+ () => {
+ const cssAnimationNameProperty = new SDK.CSSProperty.CSSProperty(
+ mockCssStyleDeclaration, 0, 'animation-name', 'first-keyframe, second-keyframe', true, false, true,
+ false, '', undefined);
+ const stylePropertyTreeElement = new Elements.StylePropertyTreeElement.StylePropertyTreeElement(
+ mockStylesSidebarPane, mockMatchedStyles, cssAnimationNameProperty, false, false, false, true);
- stylePropertyTreeElement.updateTitle();
+ stylePropertyTreeElement.updateTitle();
- const animationNameSwatches =
- stylePropertyTreeElement.valueElement?.querySelectorAll('devtools-animation-name-swatch');
- assert.strictEqual(animationNameSwatches?.length, 2);
- });
+ const animationNameSwatches =
+ stylePropertyTreeElement.valueElement?.querySelectorAll('devtools-link-swatch');
+ assert.strictEqual(animationNameSwatches?.length, 2);
+ });
});
});
diff --git a/test/unittests/front_end/panels/elements/StylesSidebarPane_test.ts b/test/unittests/front_end/panels/elements/StylesSidebarPane_test.ts
index 7deb13e..6513c83 100644
--- a/test/unittests/front_end/panels/elements/StylesSidebarPane_test.ts
+++ b/test/unittests/front_end/panels/elements/StylesSidebarPane_test.ts
@@ -205,6 +205,17 @@
const node = renderer.renderValue();
assert.deepEqual(node.textContent, nodeContents);
});
+
+ it('runs positionFallbackHandler for position-fallback property', () => {
+ const nodeContents = 'nodeContents';
+ const renderer =
+ new Elements.StylesSidebarPane.StylesSidebarPropertyRenderer(null, null, 'position-fallback', '--compass');
+ renderer.setPositionFallbackHandler(() => document.createTextNode(nodeContents));
+
+ const node = renderer.renderValue();
+
+ assert.deepEqual(node.textContent, nodeContents);
+ });
});
describe('IdleCallbackManager', () => {
diff --git a/test/unittests/front_end/ui/legacy/components/inline_editor/LinkSwatch_test.ts b/test/unittests/front_end/ui/legacy/components/inline_editor/LinkSwatch_test.ts
index cccfc2b..f9e6b09 100644
--- a/test/unittests/front_end/ui/legacy/components/inline_editor/LinkSwatch_test.ts
+++ b/test/unittests/front_end/ui/legacy/components/inline_editor/LinkSwatch_test.ts
@@ -9,7 +9,7 @@
const {assert} = chai;
-function assertSwatch(swatch: InlineEditor.LinkSwatch.CSSVarSwatch, expected: {
+function assertVarSwatch(swatch: InlineEditor.LinkSwatch.CSSVarSwatch, expected: {
valueTooltip: string|null,
linkTooltip: string,
isDefined: boolean,
@@ -19,7 +19,7 @@
assertShadowRoot(swatch.shadowRoot);
const container = swatch.shadowRoot.querySelector('span');
assertNotNullOrUndefined(container);
- const linkSwatch = container.querySelector('devtools-link-swatch');
+ const linkSwatch = container.querySelector('devtools-base-link-swatch');
assertNotNullOrUndefined(linkSwatch);
assertShadowRoot(linkSwatch.shadowRoot);
@@ -53,7 +53,7 @@
onLinkActivate: () => {},
};
- assertSwatch(component, {
+ assertVarSwatch(component, {
valueTooltip: '2px',
linkTooltip: '2px',
isDefined: true,
@@ -71,7 +71,7 @@
onLinkActivate: () => {},
};
- assertSwatch(component, {
+ assertVarSwatch(component, {
valueTooltip: null,
linkTooltip: '--undefined is not defined',
isDefined: false,
@@ -89,7 +89,7 @@
onLinkActivate: () => {},
};
- assertSwatch(component, {
+ assertVarSwatch(component, {
valueTooltip: '3px',
linkTooltip: '--undefined is not defined',
isDefined: false,
@@ -107,7 +107,7 @@
onLinkActivate: () => {},
};
- assertSwatch(component, {
+ assertVarSwatch(component, {
valueTooltip: 'green',
linkTooltip: '--undefined-color is not defined',
isDefined: false,
@@ -125,7 +125,7 @@
onLinkActivate: () => {},
};
- assertSwatch(component, {
+ assertVarSwatch(component, {
valueTooltip: 'green',
linkTooltip: '--undefined-color is not defined',
isDefined: false,
@@ -143,7 +143,7 @@
onLinkActivate: () => {},
};
- assertSwatch(component, {
+ assertVarSwatch(component, {
valueTooltip: 'red',
linkTooltip: 'red',
isDefined: true,
@@ -161,7 +161,7 @@
onLinkActivate: () => {},
};
- assertSwatch(component, {
+ assertVarSwatch(component, {
valueTooltip: 'red',
linkTooltip: 'red',
isDefined: true,
@@ -179,7 +179,7 @@
onLinkActivate: () => {},
};
- assertSwatch(component, {
+ assertVarSwatch(component, {
valueTooltip: 'red',
linkTooltip: 'red',
isDefined: true,
@@ -188,40 +188,39 @@
});
});
-function assertAnimationNameSwatch(swatch: InlineEditor.LinkSwatch.AnimationNameSwatch, expected: {
- animationName: string|null,
+function assertLinkSwatch(swatch: InlineEditor.LinkSwatch.LinkSwatch, expected: {
+ text: string|null,
title: string|null,
isDefined: boolean,
}) {
assertShadowRoot(swatch.shadowRoot);
const container = swatch.shadowRoot.querySelector('span');
assertNotNullOrUndefined(container);
- const linkSwatch = container.querySelector('devtools-link-swatch');
+ const linkSwatch = container.querySelector('devtools-base-link-swatch');
assertNotNullOrUndefined(linkSwatch);
assertShadowRoot(linkSwatch.shadowRoot);
const link = linkSwatch.shadowRoot.querySelector('.link-swatch-link');
assertNotNullOrUndefined(link);
- assert.strictEqual(
- container.getAttribute('title'), expected.animationName, 'The animation name appears as a tooltip');
+ assert.strictEqual(container.getAttribute('title'), expected.text, 'The text appears as a tooltip');
assert.strictEqual(
link.classList.contains('undefined'), !expected.isDefined,
'The link only has the class undefined when the property is undefined');
assert.strictEqual(link.getAttribute('title'), expected.title, 'The link has the right tooltip');
- assert.strictEqual(link.textContent, expected.animationName, 'The link has the right text content');
+ assert.strictEqual(link.textContent, expected.text, 'The link has the right text content');
}
-describeWithLocale('AnimationNameSwatch', () => {
+describeWithLocale('LinkSwatch', () => {
it('can be instantiated successfully', () => {
- const component = new InlineEditor.LinkSwatch.AnimationNameSwatch();
+ const component = new InlineEditor.LinkSwatch.LinkSwatch();
renderElementIntoDOM(component);
assert.instanceOf(component, HTMLElement, 'The swatch is an instance of HTMLElement');
});
- it('renders a simple animation name', () => {
- const component = new InlineEditor.LinkSwatch.AnimationNameSwatch();
+ it('renders a simple text', () => {
+ const component = new InlineEditor.LinkSwatch.LinkSwatch();
component.data = {
text: 'test',
isDefined: true,
@@ -229,15 +228,15 @@
};
renderElementIntoDOM(component);
- assertAnimationNameSwatch(component, {
- animationName: 'test',
+ assertLinkSwatch(component, {
+ text: 'test',
title: 'test',
isDefined: true,
});
});
- it('renders a missing animation name', () => {
- const component = new InlineEditor.LinkSwatch.AnimationNameSwatch();
+ it('renders a missing test', () => {
+ const component = new InlineEditor.LinkSwatch.LinkSwatch();
component.data = {
text: 'test',
isDefined: false,
@@ -245,15 +244,15 @@
};
renderElementIntoDOM(component);
- assertAnimationNameSwatch(component, {
- animationName: 'test',
+ assertLinkSwatch(component, {
+ text: 'test',
title: 'test is not defined',
isDefined: false,
});
});
it('calls the onLinkActivate callback', async () => {
- const component = new InlineEditor.LinkSwatch.AnimationNameSwatch();
+ const component = new InlineEditor.LinkSwatch.LinkSwatch();
let callbackCalled = false;
component.data = {
text: 'testHandler',
@@ -264,7 +263,7 @@
};
const element = renderElementIntoDOM(component)
- ?.shadowRoot?.querySelector('devtools-link-swatch')
+ ?.shadowRoot?.querySelector('devtools-base-link-swatch')
?.shadowRoot?.querySelector('.link-swatch-link');
assertNotNullOrUndefined(element);
element.dispatchEvent(new MouseEvent('mousedown'));