Skip to content

Commit

Permalink
fix: don't 'content' values in quotes unnecessarily (#782)
Browse files Browse the repository at this point in the history
* Correctly parse functions like counter, url, etc, added a unit test
* Unit test for transform value features
  • Loading branch information
p0nch000 authored Nov 27, 2024
1 parent e099ab1 commit 42f70e4
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 11 deletions.
77 changes: 77 additions & 0 deletions packages/shared/__tests__/stylex-transform-value.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
*/
import transformValue from '../src/transform-value';

describe('transformValue content property tests', () => {
test('preserves CSS functions without quotes', () => {
const functions = [
'counters(div, ".")',
'counter(chapter)',
'counter(chapter, upper-roman)',
'attr(href)',
'url(image.jpg)',
'linear-gradient(#e66465, #9198e5)',
'image-set("image1x.png" 1x, "image2x.png" 2x)',
'"prefix"attr(href)',
'url(foo.jpg)attr(alt)',
];

functions.forEach((input) => {
expect(transformValue('content', input, {})).toBe(input);
});
});

test('preserves CSS keywords without quotes', () => {
const keywords = [
'normal',
'none',
'open-quote',
'close-quote',
'no-open-quote',
'no-close-quote',
'inherit',
'initial',
'revert',
'revert-layer',
'unset',
];

keywords.forEach((keyword) => {
expect(transformValue('content', keyword, {})).toBe(keyword);
});
});

test('handles mixed content values', () => {
const mixedValues = [
'open-quote counter(chapter)',
'"prefix"url(image.jpg)',
'url("test.png")/"Alt text"',
'open-quotecounter(chapter)close-quote',
'attr(href)normal',
'"text"attr(href)"more text"',
'counter(x)"text"counter(y)',
];

mixedValues.forEach((input) => {
expect(transformValue('content', input, {})).toBe(input);
});
});

test('adds quotes to plain strings', () => {
const strings = [
['Hello world', '"Hello world"'],
['Simple text', '"Simple text"'],
['123', '"123"'],
];

strings.forEach(([input, expected]) => {
expect(transformValue('content', input, {})).toBe(expected);
});
});
});
45 changes: 34 additions & 11 deletions packages/shared/src/transform-value.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,49 @@ export default function transformValue(
? String(Math.round(rawValue * 10000) / 10000) + getNumberSuffix(key)
: rawValue;

// content is one of the values that needs to wrapped in quotes.
// Users may write `''` without thinking about it, so we fix that.
if (
(key === 'content' ||
key === 'hyphenateCharacter' ||
key === 'hyphenate-character') &&
typeof value === 'string'
) {
const val = value.trim();
if (val.match(/^attr\([a-zA-Z0-9-]+\)$/)) {

const cssContentFunctions = [
'attr(',
'counter(',
'counters(',
'url(',
'linear-gradient(',
'image-set(',
];

const cssContentKeywords = new Set([
'normal',
'none',
'open-quote',
'close-quote',
'no-open-quote',
'no-close-quote',
'inherit',
'initial',
'revert',
'revert-layer',
'unset',
]);

const isCssFunction = cssContentFunctions.some((func) =>
val.includes(func),
);
const isKeyword = cssContentKeywords.has(val);
const hasMatchingQuotes =
(val.match(/"/g)?.length ?? 0) >= 2 ||
(val.match(/'/g)?.length ?? 0) >= 2;

if (isCssFunction || isKeyword || hasMatchingQuotes) {
return val;
}
if (
!(
(val.startsWith('"') && val.endsWith('"')) ||
(val.startsWith("'") && val.endsWith("'"))
)
) {
return `"${val}"`;
}
return `"${val}"`;
}

return normalizeValue(value, key, options);
Expand Down

0 comments on commit 42f70e4

Please sign in to comment.