Skip to content

Commit 1adb75e

Browse files
YevheniiKotyrlometa-codesync[bot]
authored andcommitted
Match functional colors against the whole string in normalize-colors (#59103)
Summary: `react-native/normalize-colors` anchors its hex matchers but not `rgb`, `rgba`, `hsl`, `hsla` and `hwb`, so a string that only contains one of those calls normalizes to that color: `normalizeColor('xxrgb(1, 2, 3)yy')` is `0x010203ff` (#58495). Animated pays for it. `AnimatedInterpolation` treats an output range that normalizes as a color as a color, so a JS-driven interpolation of a box shadow such as `inset 0 0 0 1px rgba(124, 58, 237, 1)` returns the bare `rgba(…)`, which `processBoxShadow` parses to no shadow at all. This anchors the five functional matchers, as the hex ones already are. It is the change #58496 proposed before its author closed it. ## Changelog: [GENERAL] [FIXED] - An Animated interpolation of a string that contains an `rgb()`, `hsl()` or `hwb()` color, such as a box shadow, interpolates the whole string instead of collapsing to the color Pull Request resolved: #59103 Test Plan: `normalizeColor-test.js` gains a case for strings that only contain a functional color, which returned `16909311` for `'xxrgb(1, 2, 3)yy'` before the change. `Interpolation-itest.js` gains a box-shadow range, which returned `'rgba(124, 58, 237, 1)'` before the change and now answers `'inset 0 0 0 2px rgba(124, 58, 237, 0.75)'` halfway. - `yarn jest packages/normalize-color packages/react-native/Libraries/Animated packages/react-native/Libraries/StyleSheet`: 80 passed - `yarn flow check`: no errors - `eslint` and `prettier --check` on the three files: clean - RNTester release builds on a Galaxy S23 (Android 14), [before and after](#59103 (comment)): 24 views animating an inset ring through a JS-driven interpolation draw no ring without the change and the ring at its interpolated alpha with it Under Fantom, this PR's CI run passes `Interpolation-itest.js` and `normalizeColor-itest.js`. Reviewed By: christophpurrer Differential Revision: D126415662 Pulled By: javache fbshipit-source-id: 6ea989cd8709b847ae8cf4d04d9d46d0dff9d3d4
1 parent 7612536 commit 1adb75e

3 files changed

Lines changed: 30 additions & 7 deletions

File tree

‎packages/normalize-color/__tests__/normalizeColor-test.js‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ it('refuses non-spec compliant colors', () => {
5353
expect(normalizeColor('rgb(1%, 2%, 3%)')).toBe(null);
5454
});
5555

56+
it('refuses a string that only contains a functional color', () => {
57+
expect(normalizeColor('xxrgb(1, 2, 3)yy')).toBe(null);
58+
expect(normalizeColor('rgba(1, 2, 3, 0.5) extra')).toBe(null);
59+
expect(normalizeColor('0 0 0 1px rgba(124, 58, 237, 1)')).toBe(null);
60+
expect(normalizeColor('drop-shadow(0 0 4px hsl(0, 100%, 50%))')).toBe(null);
61+
expect(normalizeColor('hsla(0, 100%, 50%, 1);')).toBe(null);
62+
expect(normalizeColor('x hwb(0 0% 0%)')).toBe(null);
63+
});
64+
5665
it('handles hex6 properly', () => {
5766
expect(normalizeColor('#000000')).toBe(0x000000ff);
5867
expect(normalizeColor('#ffffff')).toBe(0xffffffff);

‎packages/normalize-color/index.js‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -277,22 +277,22 @@ function getMatchers() {
277277
callWithSlashSeparator(NUMBER, NUMBER, NUMBER, NUMBER);
278278

279279
cachedMatchers = {
280-
rgb: new RegExp('rgb(' + rgbRegexPattern + ')'),
281-
rgba: new RegExp('rgba(' + rgbRegexPattern + ')'),
282-
hsl: new RegExp('hsl' + call(NUMBER, PERCENTAGE, PERCENTAGE)),
280+
rgb: new RegExp('^rgb(' + rgbRegexPattern + ')$'),
281+
rgba: new RegExp('^rgba(' + rgbRegexPattern + ')$'),
282+
hsl: new RegExp('^hsl' + call(NUMBER, PERCENTAGE, PERCENTAGE) + '$'),
283283
hsla: new RegExp(
284-
'hsla(' +
284+
'^hsla(' +
285285
commaSeparatedCall(NUMBER, PERCENTAGE, PERCENTAGE, NUMBER) +
286286
'|' +
287287
callWithSlashSeparator(NUMBER, PERCENTAGE, PERCENTAGE, NUMBER) +
288-
')',
288+
')$',
289289
),
290290
hwb: new RegExp(
291-
'hwb(' +
291+
'^hwb(' +
292292
callModern(NUMBER, PERCENTAGE, PERCENTAGE) +
293293
'|' +
294294
callWithSlashSeparator(NUMBER, PERCENTAGE, PERCENTAGE, NUMBER) +
295-
')',
295+
')$',
296296
),
297297
hex3: /^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
298298
hex4: /^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,

‎packages/react-native/Libraries/Animated/__tests__/Interpolation-itest.js‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,20 @@ describe('Interpolation', () => {
333333
expect(interpolation(1)).toBe('translate(20px, 10px)');
334334
});
335335

336+
it('should keep the rest of a string range that contains a color', () => {
337+
const interpolation = createInterpolation({
338+
inputRange: [0, 1],
339+
outputRange: [
340+
'inset 0 0 0 1px rgba(124, 58, 237, 1)',
341+
'inset 0 0 0 3px rgba(124, 58, 237, 0.5)',
342+
],
343+
});
344+
345+
expect(interpolation(0)).toBe('inset 0 0 0 1px rgba(124, 58, 237, 1)');
346+
expect(interpolation(0.5)).toBe('inset 0 0 0 2px rgba(124, 58, 237, 0.75)');
347+
expect(interpolation(1)).toBe('inset 0 0 0 3px rgba(124, 58, 237, 0.5)');
348+
});
349+
336350
it('should crash when chaining an interpolation that returns a string', () => {
337351
const interpolation = createInterpolation({
338352
inputRange: [0, 1],

0 commit comments

Comments
 (0)