From 935c340dd2d8b704f6833aff4d798160cdbfadea Mon Sep 17 00:00:00 2001 From: leaysgur <6259812+leaysgur@users.noreply.github.com> Date: Fri, 6 Mar 2026 04:00:19 +0000 Subject: [PATCH] test(oxfmt): Add css-in-js conformance (#20020) Add tests for updated css-in-js. --- .../edge-cases/css-in-js/styled-components.js | 263 ++++++++++++++++++ apps/oxfmt/conformance/run.ts | 20 ++ .../conformance/snapshots/conformance.snap.md | 22 ++ .../embedded_languages.test.ts.snap | 106 +++++++ .../cli/embedded_languages/fixtures/css.js | 12 + 5 files changed, 423 insertions(+) create mode 100644 apps/oxfmt/conformance/fixtures/edge-cases/css-in-js/styled-components.js diff --git a/apps/oxfmt/conformance/fixtures/edge-cases/css-in-js/styled-components.js b/apps/oxfmt/conformance/fixtures/edge-cases/css-in-js/styled-components.js new file mode 100644 index 0000000000000..30a62d68e30e5 --- /dev/null +++ b/apps/oxfmt/conformance/fixtures/edge-cases/css-in-js/styled-components.js @@ -0,0 +1,263 @@ +const ListItem1 = styled.li``; + +const ListItem2 = styled.li` `; + +const Dropdown = styled.div`position: relative;` + +const Button = styled.button` + color: palevioletred ; + + font-size : 1em ; +`; + +// NOTE: Copied from conformance/fixtures/prettier/js/multiparser-css/styled-components.js +// And comment out not supported `.extend` cases +// const TomatoButton = Button.extend` +// color : tomato ; + +// border-color : tomato +// ; + +// `; + +// Button.extend.attr({})` +// border-color : black; +// ` + +styled(ExistingComponent)` + color : papayawhip ; background-color: firebrick`; + + +styled.button.attr({})` +border : rebeccapurple`; + +styled(ExistingComponent).attr({})` +border : rebeccapurple`; + +styled.div` + color: ${props => props.theme.colors.paragraph}; + /* prettier-ignore */ + ${props => props.small ? 'font-size: 0.8em;' : ''}; +` + +styled.div` + color: ${props => props.theme.colors.paragraph}; + /* prettier-ignore */ + ${props => props.small ? 'font-size: 0.8em;' : ''} +` + +styled.div` + /* prettier-ignore */ + color: ${props => props.theme.colors.paragraph}; + ${props => props.small ? 'font-size: 0.8em;' : ''}; +` + +styled.div` + color: ${props => props.theme.colors.paragraph}; + /* prettier-ignore */ + ${props => props.small ? 'font-size: 0.8em;' : ''}; + /* prettier-ignore */ + ${props => props.red ? 'color: red;' : ''}; +` + +styled.div` + /* prettier-ignore */ + color: ${props => props.theme.colors.paragraph}; + /* prettier-ignore */ + ${props => props.small ? 'font-size: 0.8em;' : ''}; + /* prettier-ignore */ + ${props => props.red ? 'color: red;' : ''}; + /* prettier-ignore */ +` + +styled.div` + ${sanitize} ${fonts} + html { + margin: 0; + } +` + +styled.div` + ${bar} + baz +` + +styled.span` + foo + ${bar} + baz +` + +styled.div` + foo + ${bar} + ${baz} +` + +styled.span` + ${foo} + ${bar} +` + +styled.div` + ${foo} bar +` + +styled.span` + ${foo} ${bar} + baz: ${foo} +` + +styled.span` +${foo}; +${bar}; +` + +styled.span` +${foo}: ${bar}; +` + +styled.span` +${foo}: ${bar} +` + +styled.span` +${foo}: +${bar} +` + +styled.span` +${foo}: +${bar}; +` + +styled.a` + ${feedbackCountBlockCss} + text-decoration: none; + + ${FeedbackCount} { + margin: 0; + } +` + +const StyledComponent1 = styled.div` + ${anInterpolation} + /* a comment */ + + .aRule { + color: red + } +`; + +const StyledComponent2 = styled.div` + ${anInterpolation} + + /* a comment */ + + .aRule { + color: red + } +`; + +const Direction = styled.span` + ${({ up }) => up && `color: ${color.positive};`} + ${({ down }) => down && `color: ${color.negative};`} +`; + +const Direction2 = styled.span` + ${({ up }) => up && `color: ${color.positive}`}; + ${({ down }) => down && `color: ${color.negative}`}; +`; + +const mixin = css` + color: ${props => props.color}; + ${props => props.otherProperty}: ${props => props.otherValue}; +`; + +const foo = styled.div` + display: flex; + ${props => props.useMixin && mixin} +`; + +const Single1 = styled.div` + color: red +`; + +const Single2 = styled.div` + color: red; +`; + +const Dropdown2 = styled.div` + /* A comment to avoid the prettier issue: https://github.com/prettier/prettier/issues/2291 */ + position: relative; +`; + +const bar = styled.div` + border-radius: 50%; + border: 5px solid rgba(var(--green-rgb), 0); + display: inline-block; + height: 40px; + width: 40px; + + ${props => + (props.complete || props.inProgress) && + css` + border-color: rgba(var(--green-rgb), 0.15); + `} + + div { + background-color: var(--purpleTT); + border-radius: 50%; + border: 4px solid rgba(var(--purple-rgb), 0.2); + color: var(--purpleTT); + display: inline-flex; + + ${props => + props.complete && + css` + background-color: var(--green); + border-width: 7px; + `} + + ${props => + (props.complete || props.inProgress) && + css` + border-color: var(--green); + `} + } +`; + +const A = styled.a` + display: inline-block; + color: #fff; + ${props => props.a &&css` + display: none; + `} + height: 30px; +`; + +const Foo = styled.p` + max-width: 980px; + ${mediaBreakpointOnlyXs` + && { + font-size: 0.8rem; + } + `} + + &.bottom { + margin-top: 3rem; + } +`; + +styled(A)` + // prettier-ignore + @media (aaaaaaaaaaaaa) { + z-index: ${(props) => (props.isComplete ? '1' : '0')}; + } +`; + +const StyledDiv = styled.div` + ${props => getSize(props.$size.xs)} + ${props => getSize(props.$size.sm, 'sm')} + ${props => getSize(props.$size.md, 'md')} +`; diff --git a/apps/oxfmt/conformance/run.ts b/apps/oxfmt/conformance/run.ts index 1a2aff4ea1cbd..0dacf3545c3d2 100644 --- a/apps/oxfmt/conformance/run.ts +++ b/apps/oxfmt/conformance/run.ts @@ -56,6 +56,26 @@ const categories: Category[] = [ "comment-tag.js": "`/* GraphQL */` comment tag not yet supported", }, }, + { + name: "css-in-js", + sources: [ + { + dir: join(PRETTIER_FIXTURES_DIR, "js/multiparser-css"), + ext: ".js", + excludes: ["format.test.js"], + }, + { + dir: join(PRETTIER_FIXTURES_DIR, "jsx/embed"), + ext: ".js", + excludes: ["format.test.js"], + }, + { dir: join(EDGE_CASES_DIR, "css-in-js") }, + ], + optionSets: [{ printWidth: 80 }, { printWidth: 100 }], + notes: { + "styled-components.js": "Multiple issues: `Button.extend` not recognized as tag", + }, + }, ]; // --- diff --git a/apps/oxfmt/conformance/snapshots/conformance.snap.md b/apps/oxfmt/conformance/snapshots/conformance.snap.md index 99bcbe9c195b1..17975c53fad2b 100644 --- a/apps/oxfmt/conformance/snapshots/conformance.snap.md +++ b/apps/oxfmt/conformance/snapshots/conformance.snap.md @@ -41,3 +41,25 @@ | File | Note | | :--- | :--- | | comment-tag.js | `/* GraphQL */` comment tag not yet supported | + +## css-in-js + +### Option 1: 16/17 (94.12%) + +```json +{"printWidth":80} +``` + +| File | Note | +| :--- | :--- | +| styled-components.js | Multiple issues: `Button.extend` not recognized as tag | + +### Option 2: 16/17 (94.12%) + +```json +{"printWidth":100} +``` + +| File | Note | +| :--- | :--- | +| styled-components.js | Multiple issues: `Button.extend` not recognized as tag | diff --git a/apps/oxfmt/test/cli/embedded_languages/__snapshots__/embedded_languages.test.ts.snap b/apps/oxfmt/test/cli/embedded_languages/__snapshots__/embedded_languages.test.ts.snap index c0ebf5266be48..5bd6f3bde3925 100644 --- a/apps/oxfmt/test/cli/embedded_languages/__snapshots__/embedded_languages.test.ts.snap +++ b/apps/oxfmt/test/cli/embedded_languages/__snapshots__/embedded_languages.test.ts.snap @@ -336,6 +336,18 @@ const cssProp =
Hello
; const styledJsx = ; +// Template literals with \${} substitutions +const dynamic = css\`color:\${color};background:\${bg};\`; + +const styledWithExpr = styled.button\`font-size:\${size}px;color:\${theme.primary};\`; + +// printWidth-aware: long transition values should break across lines +const animated = styled.div\`transition:width \${duration},height \${duration},top \${duration},left \${duration};\`; + +const multiProp = css\` + .card {padding:\${spacing}px;margin:\${margin};border:1px solid \${borderColor};box-shadow:0 2px 4px \${shadowColor};} +\`; + // Multi-line templates with inherited indentation (dedent before formatting) const documented = styled.div\` /** @@ -403,6 +415,35 @@ const styledJsx = ( \`} ); +// Template literals with \${} substitutions +const dynamic = css\` + color: \${color}; + background: \${bg}; +\`; + +const styledWithExpr = styled.button\` + font-size: \${size}px; + color: \${theme.primary}; +\`; + +// printWidth-aware: long transition values should break across lines +const animated = styled.div\` + transition: + width \${duration}, + height \${duration}, + top \${duration}, + left \${duration}; +\`; + +const multiProp = css\` + .card { + padding: \${spacing}px; + margin: \${margin}; + border: 1px solid \${borderColor}; + box-shadow: 0 2px 4px \${shadowColor}; + } +\`; + // Multi-line templates with inherited indentation (dedent before formatting) const documented = styled.div\` /** @@ -578,6 +619,18 @@ const cssProp =
Hello
; const styledJsx = ; +// Template literals with \${} substitutions +const dynamic = css\`color:\${color};background:\${bg};\`; + +const styledWithExpr = styled.button\`font-size:\${size}px;color:\${theme.primary};\`; + +// printWidth-aware: long transition values should break across lines +const animated = styled.div\`transition:width \${duration},height \${duration},top \${duration},left \${duration};\`; + +const multiProp = css\` + .card {padding:\${spacing}px;margin:\${margin};border:1px solid \${borderColor};box-shadow:0 2px 4px \${shadowColor};} +\`; + // Multi-line templates with inherited indentation (dedent before formatting) const documented = styled.div\` /** @@ -645,6 +698,35 @@ const styledJsx = ( \`} ); +// Template literals with \${} substitutions +const dynamic = css\` + color: \${color}; + background: \${bg}; +\`; + +const styledWithExpr = styled.button\` + font-size: \${size}px; + color: \${theme.primary}; +\`; + +// printWidth-aware: long transition values should break across lines +const animated = styled.div\` + transition: + width \${duration}, + height \${duration}, + top \${duration}, + left \${duration}; +\`; + +const multiProp = css\` + .card { + padding: \${spacing}px; + margin: \${margin}; + border: 1px solid \${borderColor}; + box-shadow: 0 2px 4px \${shadowColor}; + } +\`; + // Multi-line templates with inherited indentation (dedent before formatting) const documented = styled.div\` /** @@ -918,6 +1000,18 @@ const cssProp =
Hello
; const styledJsx = ; +// Template literals with \${} substitutions +const dynamic = css\`color:\${color};background:\${bg};\`; + +const styledWithExpr = styled.button\`font-size:\${size}px;color:\${theme.primary};\`; + +// printWidth-aware: long transition values should break across lines +const animated = styled.div\`transition:width \${duration},height \${duration},top \${duration},left \${duration};\`; + +const multiProp = css\` + .card {padding:\${spacing}px;margin:\${margin};border:1px solid \${borderColor};box-shadow:0 2px 4px \${shadowColor};} +\`; + // Multi-line templates with inherited indentation (dedent before formatting) const documented = styled.div\` /** @@ -948,6 +1042,18 @@ const cssProp =
Hello
; const styledJsx = ; +// Template literals with \${} substitutions +const dynamic = css\`color:\${color};background:\${bg};\`; + +const styledWithExpr = styled.button\`font-size:\${size}px;color:\${theme.primary};\`; + +// printWidth-aware: long transition values should break across lines +const animated = styled.div\`transition:width \${duration},height \${duration},top \${duration},left \${duration};\`; + +const multiProp = css\` + .card {padding:\${spacing}px;margin:\${margin};border:1px solid \${borderColor};box-shadow:0 2px 4px \${shadowColor};} +\`; + // Multi-line templates with inherited indentation (dedent before formatting) const documented = styled.div\` /** diff --git a/apps/oxfmt/test/cli/embedded_languages/fixtures/css.js b/apps/oxfmt/test/cli/embedded_languages/fixtures/css.js index b639ceb16721b..64661b8244f97 100644 --- a/apps/oxfmt/test/cli/embedded_languages/fixtures/css.js +++ b/apps/oxfmt/test/cli/embedded_languages/fixtures/css.js @@ -17,6 +17,18 @@ const cssProp =
Hello
; const styledJsx = ; +// Template literals with ${} substitutions +const dynamic = css`color:${color};background:${bg};`; + +const styledWithExpr = styled.button`font-size:${size}px;color:${theme.primary};`; + +// printWidth-aware: long transition values should break across lines +const animated = styled.div`transition:width ${duration},height ${duration},top ${duration},left ${duration};`; + +const multiProp = css` + .card {padding:${spacing}px;margin:${margin};border:1px solid ${borderColor};box-shadow:0 2px 4px ${shadowColor};} +`; + // Multi-line templates with inherited indentation (dedent before formatting) const documented = styled.div` /**