diff --git a/packages/react-stately/src/color/Color.ts b/packages/react-stately/src/color/Color.ts index 497227ce174..7ceba66e3d4 100644 --- a/packages/react-stately/src/color/Color.ts +++ b/packages/react-stately/src/color/Color.ts @@ -282,9 +282,13 @@ class RGBColor extends Color { // matching rgb(rrr, ggg, bbb), rgba(rrr, ggg, bbb, 0.a) const match = value.match(/^rgba?\((.*)\)$/); if (match?.[1]) { - colors = match[1].split(',').map(value => Number(value.trim())); - colors = colors.map((num, i) => { - return clamp(num ?? 0, 0, i < 3 ? 255 : 1); + const parts = match[1].split(',').map(value => value.trim()); + // Number('') is 0 rather than NaN, so empty components need their own check. + if (parts.some(part => part === '' || !Number.isFinite(Number(part)))) { + return undefined; + } + colors = parts.map((part, i) => { + return clamp(Number(part), 0, i < 3 ? 255 : 1); }); } if (colors[0] === undefined || colors[1] === undefined || colors[2] === undefined) { diff --git a/packages/react-stately/test/color/Color.test.tsx b/packages/react-stately/test/color/Color.test.tsx index 675b48e1526..680e4c7960a 100644 --- a/packages/react-stately/test/color/Color.test.tsx +++ b/packages/react-stately/test/color/Color.test.tsx @@ -107,6 +107,20 @@ describe('Color', function () { expect(color.getChannelValue('alpha')).toBe(1); expect(color.toString('rgba')).toBe('rgba(255, 0, 0, 1)'); }); + + it('should throw on non-numeric rgb channels', function () { + expect(() => parseColor('rgb(a, b, c)')).toThrow('Invalid color value: rgb(a, b, c)'); + }); + + it('should throw on a non-numeric rgba alpha', function () { + expect(() => parseColor('rgba(0, 0, 0, abc)')).toThrow( + 'Invalid color value: rgba(0, 0, 0, abc)' + ); + }); + + it('should throw on empty rgb channels', function () { + expect(() => parseColor('rgb(, , )')).toThrow('Invalid color value: rgb(, , )'); + }); }); describe('hsl', function () {