fix: handle nesting in comma separated selectors#207
Conversation
6799698 to
058744f
Compare
📊 Bundle size reportUnchanged fixtures
|
058744f to
27c7266
Compare
| expect( | ||
| compileCSS({ | ||
| ...defaultOptions, | ||
| pseudo: ':global(body) &', |
There was a problem hiding this comment.
The original selector is invalid, replaced it with a proper test case.
| // Issues are reported: | ||
| // https://github.com/thysultan/stylis.js/issues/253 | ||
| // https://github.com/thysultan/stylis.js/issues/252 | ||
| if (selector.indexOf(':global(') === 0) { | ||
| // 👇 :global(GROUP_1)GROUP_2 | ||
| const GLOBAL_PSEUDO_REGEX = /global\((.+)\)(.+)?/; | ||
| const result = GLOBAL_PSEUDO_REGEX.exec(selector)!; | ||
|
|
||
| let rtlClassNameSelector: string | null = null; | ||
| let rtlCSSDeclaration: string | null = null; | ||
| globalSelector = result[1] + ' '; | ||
| const restPseudo = result[2] || ''; | ||
|
|
||
| if (rtlProperty && rtlClassName) { | ||
| rtlClassNameSelector = `.${rtlClassName}`; | ||
| rtlCSSDeclaration = Array.isArray(rtlValue) | ||
| ? `{ ${rtlValue.map(v => `${hyphenateProperty(rtlProperty)}: ${v}`).join(';')}; }` | ||
| : `{ ${hyphenateProperty(rtlProperty)}: ${rtlValue}; }`; | ||
| } | ||
| // should be normalized to handle ":global(SELECTOR) &" | ||
| const normalizedPseudoSelector = normalizePseudoSelector(restPseudo); | ||
|
|
||
| let cssRule = ''; | ||
| if (normalizedPseudoSelector === '') { | ||
| return acc; | ||
| } | ||
|
|
||
| // Should be handled by namespace plugin of Stylis, is buggy now | ||
| // Issues are reported: | ||
| // https://github.com/thysultan/stylis.js/issues/253 | ||
| // https://github.com/thysultan/stylis.js/issues/252 | ||
| if (pseudo.indexOf(':global(') === 0) { | ||
| // 👇 :global(GROUP_1)GROUP_2 | ||
| const GLOBAL_PSEUDO_REGEX = /global\((.+)\)(.+)?/; | ||
| const [, globalSelector, restPseudo = ''] = GLOBAL_PSEUDO_REGEX.exec(pseudo)!; | ||
| return `${normalizedPseudoSelector} { ${acc} }`; | ||
| } |
There was a problem hiding this comment.
I moved the hack with small changes, will replace it with a Stylis plugin in a follow up PR.
| expect(getStyleBucketName([], '', '', '')).toBe('d'); | ||
| expect(getStyleBucketName(['.foo'], '', '', '')).toBe('d'); |
| if (selectors.length > 0) { | ||
| const normalizedPseudo = selectors[0].trim(); |
There was a problem hiding this comment.
We take into account only the first pseudo selector, so it's find to use the first element.
| expect( | ||
| resolveStyleRules({ | ||
| ':hover,:focus-within': { | ||
| '::before': { | ||
| color: 'orange', | ||
| }, | ||
| }, | ||
| }), | ||
| ).toMatchInlineSnapshot(` | ||
| .fij4gri:hover::before, | ||
| .fij4gri:focus-within::before { | ||
| color: orange; | ||
| } | ||
| `); |
| it('handles :global selector', () => { | ||
| expect( | ||
| resolveStyleRules({ | ||
| ':global(body) &': { color: 'green' }, | ||
| }), | ||
| ).toMatchInlineSnapshot(` | ||
| body .fm1e7ra { | ||
| color: green; | ||
| } | ||
| `); | ||
| }); |
There was a problem hiding this comment.
Again a test that does not have any sense.
| `); | ||
| expect( | ||
| resolveStyleRules({ | ||
| ':global(body):focus': { color: 'pink' }, |
| it('supports :global as a nested selector', () => { | ||
| expect( | ||
| resolveStyleRules({ | ||
| ':focus': { ':global(body)': { color: 'green' } }, | ||
| }), | ||
| ).toMatchInlineSnapshot(` | ||
| body .fz7er5p:focus { | ||
| color: green; | ||
| } | ||
| `); | ||
| }); |
There was a problem hiding this comment.
As now we have scopes this test surprisingly works 🐱
27c7266 to
737a2a6
Compare
737a2a6 to
280d7fb
Compare
| // https://github.com/thysultan/stylis.js/issues/252 | ||
| if (selector.indexOf(':global(') === 0) { | ||
| // 👇 :global(GROUP_1)GROUP_2 | ||
| const GLOBAL_PSEUDO_REGEX = /global\((.+)\)(.+)?/; |
There was a problem hiding this comment.
maybe hoist the regex so it doesn't get created on every iteration ?
There was a problem hiding this comment.
Makes sense, but in a followup I will get it of this part anyway (I will replace this part with a stylis plugin).
| className: string; | ||
|
|
||
| pseudo: string; | ||
| selectors: string[]; |
There was a problem hiding this comment.
would the length of selectors ever exceed 2 ? It seems that the only case we are handling here is pseudo selector + element. It shouldn't be possible to go further since it would be invalid css ?
There was a problem hiding this comment.
Can you please provide an example?

Fixes #204.
Fixes #206.
Changes in selectors handling
In
resolveStyleRules()&compileCSS()I renamedpseudotoselectorsand changed their type to bestring[]:To reflect what it contains as
selectorscan contain not only pseudo selectors.To handle selectors properly as it was a problem without a solution:
Before we were concatenating inputs with selectors and getting
":hover,:focus-within::before"that will be compiled by Stylis to:And without proper wrapping with curly braces we cannot handle it. With selectors as an array with can send as input
:hover,:focus-within { ::before { } }that will be handled properly.Changes in
:global()handlingBecause before we didn't know scopes of selectors we were doing
.trim()and were losing" "in rules (#206):griffel/packages/core/src/runtime/compileCSS.ts
Lines 92 to 93 in 0e358cb
Currently I kept existing hack with
:global()in place to avoid more changes in this PR, but in a follow up - I will replace it with a Stylis plugin.