From 563a384219544ac90393dc9a1bf6f4671b973d4a Mon Sep 17 00:00:00 2001 From: Mert Akinc <7282195+m-akinc@users.noreply.github.com> Date: Wed, 1 May 2024 20:42:04 -0500 Subject: [PATCH 1/2] Fix regression in pseudo-element handling --- src/preview/rewriteStyleSheet.test.ts | 2 ++ src/preview/rewriteStyleSheet.ts | 26 +++++++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/preview/rewriteStyleSheet.test.ts b/src/preview/rewriteStyleSheet.test.ts index 95ec327..c746f43 100644 --- a/src/preview/rewriteStyleSheet.test.ts +++ b/src/preview/rewriteStyleSheet.test.ts @@ -141,6 +141,7 @@ describe("rewriteStyleSheet", () => { const sheet = new Sheet("::-webkit-scrollbar-thumb:hover { border-color: transparent; }") rewriteStyleSheet(sheet as any) expect(sheet.cssRules[0].getSelectors()).not.toContain("::-webkit-scrollbar-thumb.pseudo-hover") + expect(sheet.cssRules[0].getSelectors()).toContain(".pseudo-hover-all ::-webkit-scrollbar-thumb") }) it("adds alternative selector when ::-webkit-scrollbar-thumb follows :hover", () => { @@ -153,6 +154,7 @@ describe("rewriteStyleSheet", () => { const sheet = new Sheet("::part(foo bar):hover { border-color: transparent; }") rewriteStyleSheet(sheet as any) expect(sheet.cssRules[0].getSelectors()).not.toContain("::part(foo bar).pseudo-hover") + expect(sheet.cssRules[0].getSelectors()).toContain(".pseudo-hover-all ::part(foo bar)") }) it("adds alternative selector when ::part() follows :hover", () => { diff --git a/src/preview/rewriteStyleSheet.ts b/src/preview/rewriteStyleSheet.ts index 828ad84..3c6bfa9 100644 --- a/src/preview/rewriteStyleSheet.ts +++ b/src/preview/rewriteStyleSheet.ts @@ -1,13 +1,11 @@ import { PSEUDO_STATES, EXCLUDED_PSEUDO_ELEMENT_PATTERNS } from "../constants" import { splitSelectors } from "./splitSelectors" -const pseudoStateRegExp = (global: boolean, pseudoStates: string[]) => - new RegExp(`(? pseudoStateRegExp(true, [pseudoState]) - +const pseudoStatesPattern = `:(${pseudoStates.join("|")})` +const matchOne = new RegExp(pseudoStatesPattern) +const matchAll = new RegExp(pseudoStatesPattern, "g") + const warnings = new Set() const warnOnce = (message: string) => { if (warnings.has(message)) return @@ -17,7 +15,11 @@ const warnOnce = (message: string) => { } const replacePseudoStates = (selector: string, allClass?: boolean) => { - return pseudoStates.reduce((acc, state) => acc.replace(replacementRegExp(state), `.pseudo-${state}${allClass ? "-all" : ""}`), selector) + const negativeLookbehind = `(? acc.replace( + new RegExp(`${negativeLookbehind}:${state}`, "g"), + `.pseudo-${state}${allClass ? "-all" : ""}` + ), selector) } // Does not handle :host() or :not() containing pseudo-states. Need to call replaceNotSelectors on the input first. @@ -77,11 +79,16 @@ const rewriteRule = ({ cssText, selectorText }: CSSStyleRule, forShadowDOM: bool if (selector.includes(".pseudo-")) { return [] } + const replacementSelectors = [selector] if (!matchOne.test(selector)) { - return [selector] + return replacementSelectors } const classSelector = replacePseudoStates(selector) + if (classSelector !== selector) { + replacementSelectors.push(classSelector) + } + let ancestorSelector = "" if (selector.startsWith(":host(")) { @@ -108,8 +115,9 @@ const rewriteRule = ({ cssText, selectorText }: CSSStyleRule, forShadowDOM: bool const withNotsReplaced = rewriteNotSelectors(selector, forShadowDOM) ancestorSelector = replacePseudoStatesWithAncestorSelector(withNotsReplaced, forShadowDOM) } + replacementSelectors.push(ancestorSelector) - return [selector, classSelector, ancestorSelector] + return replacementSelectors }) .join(", ") ) From a8f043728f1978aaae922df13f6ffad8960e835e Mon Sep 17 00:00:00 2001 From: Mert Akinc <7282195+m-akinc@users.noreply.github.com> Date: Thu, 2 May 2024 15:05:00 -0500 Subject: [PATCH 2/2] Fix ShadowRootWithPart story --- stories/ShadowRootWithPart.css | 23 +++++++++++++++++++++++ stories/ShadowRootWithPart.js | 28 +--------------------------- 2 files changed, 24 insertions(+), 27 deletions(-) create mode 100644 stories/ShadowRootWithPart.css diff --git a/stories/ShadowRootWithPart.css b/stories/ShadowRootWithPart.css new file mode 100644 index 0000000..4bd2474 --- /dev/null +++ b/stories/ShadowRootWithPart.css @@ -0,0 +1,23 @@ +::part(foo) { + font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 700; + border: 0; + border-radius: 3em; + cursor: pointer; + display: inline-block; + line-height: 1; + color: white; + background-color: tomato; + font-size: 14px; + padding: 11px 20px; +} +::part(foo):hover { + text-decoration: underline; +} +::part(foo):focus { + box-shadow: inset 0 0 0 2px maroon; + outline: 0; +} +::part(foo):active { + background-color: firebrick; +} diff --git a/stories/ShadowRootWithPart.js b/stories/ShadowRootWithPart.js index 8c2c782..a835d8a 100644 --- a/stories/ShadowRootWithPart.js +++ b/stories/ShadowRootWithPart.js @@ -1,4 +1,5 @@ import React from "react" +import "./ShadowRootWithPart.css" export const ShadowRoot = ({ label = "Hello from shadow DOM" }) => { const ref = React.useRef() @@ -9,33 +10,6 @@ export const ShadowRoot = ({ label = "Hello from shadow DOM" }) => { ref.current.shadowRoot.innerHTML = ` ` - ref.current.innerHTML = ` - - ` }, []) return