diff --git a/packages/@react-aria/color/test/useColorWheel.test.tsx b/packages/@react-aria/color/test/useColorWheel.test.tsx index b336c7828c6..e0ede0a76e4 100644 --- a/packages/@react-aria/color/test/useColorWheel.test.tsx +++ b/packages/@react-aria/color/test/useColorWheel.test.tsx @@ -11,9 +11,9 @@ */ import {act, fireEvent, render} from '@testing-library/react'; -import {Color, useColorWheelState} from '@react-stately/color'; import {ColorWheelProps} from '@react-types/color'; import {installMouseEvent, installPointerEvent} from '@react-spectrum/test-utils'; +import {parseColor, useColorWheelState} from '@react-stately/color'; import React, {useRef} from 'react'; import {useColorWheel} from '../'; import userEvent from '@testing-library/user-event'; @@ -124,7 +124,7 @@ describe('useColorWheel', () => { describe('keyboard events', () => { it('left/right works', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); act(() => {slider.focus();}); @@ -138,7 +138,7 @@ describe('useColorWheel', () => { }); it('up/down works', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); act(() => {slider.focus();}); @@ -152,7 +152,7 @@ describe('useColorWheel', () => { }); it('doesn\'t work when disabled', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); act(() => {slider.focus();}); @@ -164,7 +164,7 @@ describe('useColorWheel', () => { }); it('wraps around', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); act(() => {slider.focus();}); @@ -175,7 +175,7 @@ describe('useColorWheel', () => { }); it('respects step', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); act(() => {slider.focus();}); @@ -189,7 +189,7 @@ describe('useColorWheel', () => { }); it('can always get back to 0 even with step', () => { - let defaultColor = new Color('hsl(330, 100%, 50%)'); + let defaultColor = parseColor('hsl(330, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); act(() => {slider.focus();}); @@ -203,7 +203,7 @@ describe('useColorWheel', () => { }); it('steps with page up/down', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); act(() => {slider.focus();}); @@ -238,7 +238,7 @@ describe('useColorWheel', () => { prepare(); it('dragging the thumb works', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole, getByTestId} = render(); let thumb = getByTestId('thumb'); let slider = getByRole('slider'); @@ -261,7 +261,7 @@ describe('useColorWheel', () => { }); it('dragging the thumb doesn\'t work when disabled', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole, getByTestId} = render(); let thumb = getByTestId('thumb'); let slider = getByRole('slider'); @@ -283,7 +283,7 @@ describe('useColorWheel', () => { }); it('dragging the thumb respects the step', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByTestId} = render(); let thumb = getByTestId('thumb'); let container = getByTestId('container'); @@ -299,7 +299,7 @@ describe('useColorWheel', () => { }); it('clicking and dragging on the track works', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole, getByTestId} = render(); let thumb = getByTestId('thumb'); let slider = getByRole('slider'); @@ -323,7 +323,7 @@ describe('useColorWheel', () => { }); it('clicking and dragging on the track doesn\'t work when disabled', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole, getByTestId} = render(); let slider = getByRole('slider'); let container = getByTestId('container'); @@ -344,7 +344,7 @@ describe('useColorWheel', () => { }); it('clicking and dragging on the track respects the step', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByTestId} = render(); let thumb = getByTestId('thumb'); let container = getByTestId('container'); diff --git a/packages/@react-aria/color/test/useHexColorField.test.js b/packages/@react-aria/color/test/useHexColorField.test.js index b0db940a3a9..4e6fd1cef06 100644 --- a/packages/@react-aria/color/test/useHexColorField.test.js +++ b/packages/@react-aria/color/test/useHexColorField.test.js @@ -10,7 +10,7 @@ * governing permissions and limitations under the License. */ -import {Color} from '@react-stately/color'; +import {parseColor} from '@react-stately/color'; import React from 'react'; import {renderHook} from '@testing-library/react-hooks'; import {useHexColorField} from '../'; @@ -50,7 +50,7 @@ describe('useHexColorField', function () { }); it('should return props for colorValue provided', function () { - let colorValue = new Color('#ff88a0'); + let colorValue = parseColor('#ff88a0'); let {inputFieldProps} = renderHexColorFieldHook({}, {colorValue}); expect(inputFieldProps['aria-valuenow']).toBe(colorValue.toHexInt()); expect(inputFieldProps['aria-valuetext']).toBe('#FF88A0'); @@ -77,12 +77,12 @@ describe('useHexColorField', function () { let {inputFieldProps} = renderHexColorFieldHook({validationState: 'invalid'}); expect(inputFieldProps['aria-invalid']).toBe(true); }); - + it('should return prop for required', function () { let {inputFieldProps} = renderHexColorFieldHook({isRequired: true}); expect(inputFieldProps['aria-required']).toBe(true); }); - + it('should return prop for readonly', function () { let {inputFieldProps} = renderHexColorFieldHook({isReadOnly: true}); expect(inputFieldProps['aria-readonly']).toBe(true); diff --git a/packages/@react-spectrum/color/src/ColorThumb.tsx b/packages/@react-spectrum/color/src/ColorThumb.tsx index 481c1ef806b..14581d265d4 100644 --- a/packages/@react-spectrum/color/src/ColorThumb.tsx +++ b/packages/@react-spectrum/color/src/ColorThumb.tsx @@ -11,7 +11,7 @@ */ import {classNames} from '@react-spectrum/utils'; -import {Color} from '@react-stately/color'; +import {Color} from '@react-types/color'; import {DOMProps} from '@react-types/shared'; import React, {ReactElement} from 'react'; import stylesHandle from '@adobe/spectrum-css-temp/components/colorhandle/vars.css'; diff --git a/packages/@react-spectrum/color/stories/ColorSlider.stories.tsx b/packages/@react-spectrum/color/stories/ColorSlider.stories.tsx index 3a0dd497822..794fc17c5d1 100644 --- a/packages/@react-spectrum/color/stories/ColorSlider.stories.tsx +++ b/packages/@react-spectrum/color/stories/ColorSlider.stories.tsx @@ -10,9 +10,9 @@ * governing permissions and limitations under the License. */ -import {Color} from '@react-stately/color'; import {ColorSlider} from '../'; import {Flex} from '@react-spectrum/layout'; +import {parseColor} from '@react-stately/color'; import React, {useState} from 'react'; import {storiesOf} from '@storybook/react'; import {Text} from '@react-spectrum/text'; @@ -20,44 +20,44 @@ import {Text} from '@react-spectrum/text'; storiesOf('ColorSlider', module) .add( 'default', - () => + () => ) .add( 'no label', - () => + () => ) .add( 'no value label', - () => + () => ) .add( 'no label, no value label', - () => + () => ) .add( 'step', - () => + () => ) .add( 'disabled', - () => + () => ) .add( 'vertical', - () => + () => ) .add( 'controlled', - () => + () => ) .add( 'custom width', - () => + () => ) .add( 'rgba', () => { - let [color, setColor] = useState(new Color('#ff00ff')); + let [color, setColor] = useState(parseColor('#ff00ff')); return ( @@ -75,7 +75,7 @@ storiesOf('ColorSlider', module) .add( 'hsla', () => { - let [color, setColor] = useState(new Color('hsla(0, 100%, 50%, 0.5)')); + let [color, setColor] = useState(parseColor('hsla(0, 100%, 50%, 0.5)')); return ( @@ -92,7 +92,7 @@ storiesOf('ColorSlider', module) .add( 'hsba', () => { - let [color, setColor] = useState(new Color('hsba(0, 100%, 50%, 0.5)')); + let [color, setColor] = useState(parseColor('hsba(0, 100%, 50%, 0.5)')); return ( diff --git a/packages/@react-spectrum/color/stories/ColorThumb.stories.tsx b/packages/@react-spectrum/color/stories/ColorThumb.stories.tsx index 6297eaaeceb..dd4c584342a 100644 --- a/packages/@react-spectrum/color/stories/ColorThumb.stories.tsx +++ b/packages/@react-spectrum/color/stories/ColorThumb.stories.tsx @@ -10,29 +10,29 @@ * governing permissions and limitations under the License. */ -import {Color} from '@react-stately/color'; import {ColorThumb} from '../src/ColorThumb'; +import {parseColor} from '@react-stately/color'; import React from 'react'; import {storiesOf} from '@storybook/react'; storiesOf('ColorThumb', module) .add( 'default', - () => + () => ) .add( 'focused', - () => + () => ) .add( 'focused, dragging', - () => + () => ) .add( 'focused, dragging, alpha', - () => + () => ) .add( 'disabled', - () => + () => ); diff --git a/packages/@react-spectrum/color/stories/ColorWheel.stories.tsx b/packages/@react-spectrum/color/stories/ColorWheel.stories.tsx index c850f8a165c..5a2a66ecc4d 100644 --- a/packages/@react-spectrum/color/stories/ColorWheel.stories.tsx +++ b/packages/@react-spectrum/color/stories/ColorWheel.stories.tsx @@ -11,24 +11,24 @@ */ import {action} from '@storybook/addon-actions'; -import {Color} from '@react-stately/color'; import {ColorWheel} from '../'; import {Flex} from '@adobe/react-spectrum'; +import {parseColor} from '@react-stately/color'; import React, {useState} from 'react'; import {storiesOf} from '@storybook/react'; storiesOf('ColorWheel', module) .add( 'default', - () => + () => ) .add( 'disabled', - () => + () => ) .add( 'step', - () => + () => ) .add( 'custom size', @@ -40,14 +40,14 @@ storiesOf('ColorWheel', module) - + ); } ) .add( 'controlled', () => { - let [color, setColor] = useState(new Color('hsl(0, 100%, 50%)')); + let [color, setColor] = useState(parseColor('hsl(0, 100%, 50%)')); let colorCSS = color.toString('css'); return ( diff --git a/packages/@react-spectrum/color/stories/HexColorField.stories.tsx b/packages/@react-spectrum/color/stories/HexColorField.stories.tsx index 35120e85544..2a966f15976 100644 --- a/packages/@react-spectrum/color/stories/HexColorField.stories.tsx +++ b/packages/@react-spectrum/color/stories/HexColorField.stories.tsx @@ -12,7 +12,7 @@ import {action} from '@storybook/addon-actions'; import {ActionButton} from '@react-spectrum/button'; -import {Color} from '@react-stately/color'; +import {Color} from '@react-types/color'; import {Dialog, DialogTrigger} from '@react-spectrum/dialog'; import {Flex} from '@react-spectrum/layout'; import {HexColorField} from '../'; @@ -61,7 +61,7 @@ storiesOf('HexColorField', module) 'controlled value', () => ( ) ) @@ -91,7 +91,7 @@ function HexColorFieldPopover(props: any = {}) { UNSAFE_style={{ background: colorString }} >{colorString} - diff --git a/packages/@react-spectrum/color/test/ColorSlider.test.tsx b/packages/@react-spectrum/color/test/ColorSlider.test.tsx index 400c5876780..e9f4b9a633b 100644 --- a/packages/@react-spectrum/color/test/ColorSlider.test.tsx +++ b/packages/@react-spectrum/color/test/ColorSlider.test.tsx @@ -11,9 +11,9 @@ */ import {act, fireEvent, render} from '@testing-library/react'; -import {Color} from '@react-stately/color'; import {ColorSlider} from '../'; import {installMouseEvent, installPointerEvent} from '@react-spectrum/test-utils'; +import {parseColor} from '@react-stately/color'; import React from 'react'; import userEvent from '@testing-library/user-event'; @@ -47,7 +47,7 @@ describe('ColorSlider', () => { }); it('sets input props', () => { - let {getByRole} = render(); + let {getByRole} = render(); let slider = getByRole('slider'); expect(slider).toHaveAttribute('type', 'range'); @@ -57,7 +57,7 @@ describe('ColorSlider', () => { }); it('sets aria-label when label is disabled', () => { - let {getByRole} = render(); + let {getByRole} = render(); let slider = getByRole('slider'); expect(slider).toHaveAttribute('aria-label', 'Red'); @@ -66,7 +66,7 @@ describe('ColorSlider', () => { it('the slider is focusable', () => { let {getAllByRole, getByRole} = render(
- +
); let slider = getByRole('slider'); @@ -85,7 +85,7 @@ describe('ColorSlider', () => { it('disabled', () => { let {getAllByRole, getByRole} = render(
- +
); let slider = getByRole('slider'); @@ -102,7 +102,7 @@ describe('ColorSlider', () => { describe('keyboard events', () => { it('works', () => { - let defaultColor = new Color('#000000'); + let defaultColor = parseColor('#000000'); let {getByRole} = render(); let slider = getByRole('slider'); act(() => {slider.focus();}); @@ -116,7 +116,7 @@ describe('ColorSlider', () => { }); it('doesn\'t work when disabled', () => { - let defaultColor = new Color('#000000'); + let defaultColor = parseColor('#000000'); let {getByRole} = render(); let slider = getByRole('slider'); act(() => {slider.focus();}); @@ -128,7 +128,7 @@ describe('ColorSlider', () => { }); it('respects step', () => { - let defaultColor = new Color('#000000'); + let defaultColor = parseColor('#000000'); let {getByRole} = render(); let slider = getByRole('slider'); act(() => {slider.focus();}); @@ -163,7 +163,7 @@ describe('ColorSlider', () => { prepare(); it('dragging the thumb works', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); let thumb = slider.parentElement; @@ -184,7 +184,7 @@ describe('ColorSlider', () => { }); it('dragging the thumb works when vertical', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); let thumb = slider.parentElement; @@ -205,7 +205,7 @@ describe('ColorSlider', () => { }); it('dragging the thumb doesn\'t works when disabled', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); let thumb = slider.parentElement; @@ -225,7 +225,7 @@ describe('ColorSlider', () => { }); it('dragging the thumb respects the step', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); let thumb = slider.parentElement; @@ -240,7 +240,7 @@ describe('ColorSlider', () => { }); it('clicking and dragging on the track works', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); let thumb = slider.parentElement; @@ -263,7 +263,7 @@ describe('ColorSlider', () => { }); it('clicking and dragging on the track works when vertical', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); let thumb = slider.parentElement; @@ -286,7 +286,7 @@ describe('ColorSlider', () => { }); it('clicking and dragging on the track doesn\'t work when disabled', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); let container = getByRole('group').firstChild; @@ -306,7 +306,7 @@ describe('ColorSlider', () => { }); it('clicking and dragging on the track respects the step', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); let thumb = slider.parentElement; diff --git a/packages/@react-spectrum/color/test/ColorWheel.test.tsx b/packages/@react-spectrum/color/test/ColorWheel.test.tsx index 4a965a4ff38..55d1f201e82 100644 --- a/packages/@react-spectrum/color/test/ColorWheel.test.tsx +++ b/packages/@react-spectrum/color/test/ColorWheel.test.tsx @@ -11,9 +11,9 @@ */ import {act, fireEvent, render} from '@testing-library/react'; -import {Color} from '@react-stately/color'; import {ColorWheel} from '../'; import {installMouseEvent, installPointerEvent} from '@react-spectrum/test-utils'; +import {parseColor} from '@react-stately/color'; import React from 'react'; import userEvent from '@testing-library/user-event'; @@ -105,7 +105,7 @@ describe('ColorWheel', () => { describe('keyboard events', () => { it('works', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); act(() => {slider.focus();}); @@ -119,7 +119,7 @@ describe('ColorWheel', () => { }); it('doesn\'t work when disabled', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); act(() => {slider.focus();}); @@ -131,7 +131,7 @@ describe('ColorWheel', () => { }); it('wraps around', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); act(() => {slider.focus();}); @@ -142,7 +142,7 @@ describe('ColorWheel', () => { }); it('respects step', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {getByRole} = render(); let slider = getByRole('slider'); act(() => {slider.focus();}); @@ -177,7 +177,7 @@ describe('ColorWheel', () => { prepare(); it('dragging the thumb works', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {container: _container, getByRole} = render(); let slider = getByRole('slider'); let thumb = slider.parentElement; @@ -200,7 +200,7 @@ describe('ColorWheel', () => { }); it('dragging the thumb doesn\'t works when disabled', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {container: _container, getByRole} = render(); let slider = getByRole('slider'); let container = _container.firstChild as HTMLElement; @@ -222,7 +222,7 @@ describe('ColorWheel', () => { }); it('dragging the thumb respects the step', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {container: _container, getByRole} = render(); let slider = getByRole('slider'); let container = _container.firstChild as HTMLElement; @@ -239,7 +239,7 @@ describe('ColorWheel', () => { }); it('clicking and dragging on the track works', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {container: _container, getByRole} = render(); let slider = getByRole('slider'); let thumb = slider.parentElement; @@ -263,7 +263,7 @@ describe('ColorWheel', () => { }); it('clicking and dragging on the track doesn\'t work when disabled', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {container: _container, getByRole} = render(); let slider = getByRole('slider'); let container = _container.firstChild as HTMLElement; @@ -284,7 +284,7 @@ describe('ColorWheel', () => { }); it('clicking and dragging on the track respects the step', () => { - let defaultColor = new Color('hsl(0, 100%, 50%)'); + let defaultColor = parseColor('hsl(0, 100%, 50%)'); let {container: _container, getByRole} = render(); let slider = getByRole('slider'); let thumb = slider.parentElement; diff --git a/packages/@react-spectrum/color/test/HexColorField.test.js b/packages/@react-spectrum/color/test/HexColorField.test.js index d16bf2177c0..c5cb3a6b1d6 100644 --- a/packages/@react-spectrum/color/test/HexColorField.test.js +++ b/packages/@react-spectrum/color/test/HexColorField.test.js @@ -12,8 +12,8 @@ import {act, fireEvent, render} from '@testing-library/react'; import {chain} from '@react-aria/utils'; -import {Color} from '@react-stately/color'; import {HexColorField} from '../'; +import {parseColor} from '@react-stately/color'; import {Provider} from '@react-spectrum/provider'; import React, {useState} from 'react'; import {theme} from '@react-spectrum/theme-default'; @@ -33,7 +33,7 @@ function renderComponent(props) { describe('HexColorField', function () { it('should handle defaults', function () { let { - getByLabelText, + getByLabelText, getByRole, getByText } = renderComponent({}); @@ -112,10 +112,10 @@ describe('HexColorField', function () { Name | props ${'3-length hex string'} | ${{defaultValue: '#abc'}} ${'6-length hex string'} | ${{defaultValue: '#aabbcc'}} - ${'Color object'} | ${{defaultValue: new Color('#abc')}} + ${'Color object'} | ${{defaultValue: parseColor('#abc')}} ${'3-length hex string controlled'} | ${{value: '#abc'}} ${'6-length hex string controlled'} | ${{value: '#aabbcc'}} - ${'Color object controlled'} | ${{value: new Color('#abc')}} + ${'Color object controlled'} | ${{value: parseColor('#abc')}} `('should accept $Name as value', function ({props}) { let {getByLabelText} = renderComponent(props); let hexColorField = getByLabelText('Primary Color'); @@ -141,12 +141,12 @@ describe('HexColorField', function () { typeText(hexColorField, 'cba'); expect(hexColorField.value).toBe('cba'); expect(onChangeSpy).toHaveBeenCalledTimes(2); - expect(onChangeSpy).toHaveBeenCalledWith(new Color('#cba')); + expect(onChangeSpy).toHaveBeenCalledWith(parseColor('#cba')); typeText(hexColorField, 'cba'); expect(hexColorField.value).toBe('cbacba'); expect(onChangeSpy).toHaveBeenCalledTimes(3); - expect(onChangeSpy).toHaveBeenCalledWith(new Color('#cbacba')); + expect(onChangeSpy).toHaveBeenCalledWith(parseColor('#cbacba')); act(() => {hexColorField.blur();}); expect(hexColorField.value).toBe('#CBACBA'); @@ -175,7 +175,7 @@ describe('HexColorField', function () { }); typeText(hexColorField, 'cba'); expect(hexColorField.value).toBe('#AABBCC'); - expect(onChangeSpy).toHaveBeenCalledWith(new Color('#cba')); + expect(onChangeSpy).toHaveBeenCalledWith(parseColor('#cba')); expect(onChangeSpy).toHaveBeenCalledTimes(2); act(() => {hexColorField.blur();}); @@ -193,10 +193,10 @@ describe('HexColorField', function () { label="Primary Color" value={color} onChange={chain(setColor, onChange)} /> - ); + ); } let onChangeSpy = jest.fn(); - let {getByLabelText} = render(); + let {getByLabelText} = render(); let hexColorField = getByLabelText('Primary Color'); expect(hexColorField.value).toBe('#AABBCC'); @@ -213,7 +213,7 @@ describe('HexColorField', function () { typeText(hexColorField, 'cba'); expect(hexColorField.value).toBe('cba'); expect(onChangeSpy).toHaveBeenCalledTimes(2); - expect(onChangeSpy).toHaveBeenCalledWith(new Color('#cba')); + expect(onChangeSpy).toHaveBeenCalledWith(parseColor('#cba')); act(() => {hexColorField.blur();}); expect(hexColorField.value).toBe('#CCBBAA'); @@ -230,7 +230,7 @@ describe('HexColorField', function () { typeText(hexColorField, 'abc'); expect(hexColorField.value).toBe('abc'); expect(onChangeSpy).toHaveBeenCalledTimes(1); - expect(onChangeSpy).toHaveBeenCalledWith(new Color('#abc')); + expect(onChangeSpy).toHaveBeenCalledWith(parseColor('#abc')); typeText(hexColorField, 'xyz8b'); expect(hexColorField.value).toBe('abc8b'); @@ -250,7 +250,7 @@ describe('HexColorField', function () { typeText(hexColorField, 'fff'); expect(hexColorField.value).toBe('fff'); expect(onChangeSpy).toHaveBeenCalledTimes(1); - expect(onChangeSpy).toHaveBeenCalledWith(new Color('#fff')); + expect(onChangeSpy).toHaveBeenCalledWith(parseColor('#fff')); typeText(hexColorField, 'fff'); expect(hexColorField.value).toBe('ffffff'); @@ -259,8 +259,8 @@ describe('HexColorField', function () { it.each` Name | expected | key - ${'increment with arrow up key'} | ${new Color('#AAAAAE')} | ${'ArrowUp'} - ${'decrement with arrow down key'} | ${new Color('#AAAAA6')} | ${'ArrowDown'} + ${'increment with arrow up key'} | ${parseColor('#AAAAAE')} | ${'ArrowUp'} + ${'decrement with arrow down key'} | ${parseColor('#AAAAA6')} | ${'ArrowDown'} `('should handle $Name event', function ({expected, key}) { let onChangeSpy = jest.fn(); let {getByLabelText} = renderComponent({ @@ -279,8 +279,8 @@ describe('HexColorField', function () { it.each` Name | expected | deltaY - ${'increment with mouse wheel'} | ${new Color('#AAAAAE')} | ${-10} - ${'decrement with mouse wheel'} | ${new Color('#AAAAA6')} | ${10} + ${'increment with mouse wheel'} | ${parseColor('#AAAAAE')} | ${-10} + ${'decrement with mouse wheel'} | ${parseColor('#AAAAA6')} | ${10} `('should handle $Name event', function ({expected, deltaY}) { let onChangeSpy = jest.fn(); let {getByLabelText} = renderComponent({ @@ -307,7 +307,7 @@ describe('HexColorField', function () { let hexColorField = getByLabelText('Primary Color'); expect(hexColorField.value).toBe(initExpected); - let maxColor = new Color('#FFFFFF'); + let maxColor = parseColor('#FFFFFF'); fireEvent.keyDown(hexColorField, {key}); fireEvent.keyUp(hexColorField, {key}); expect(onChangeSpy).toHaveBeenCalledWith(maxColor); @@ -329,7 +329,7 @@ describe('HexColorField', function () { let hexColorField = getByLabelText('Primary Color'); expect(hexColorField.value).toBe(initExpected); - let minColor = new Color('#000000'); + let minColor = parseColor('#000000'); fireEvent.keyDown(hexColorField, {key}); fireEvent.keyUp(hexColorField, {key}); expect(onChangeSpy).toHaveBeenCalledWith(minColor); diff --git a/packages/@react-stately/color/src/Color.ts b/packages/@react-stately/color/src/Color.ts index 1cea91cae67..b0d4a0cacb2 100644 --- a/packages/@react-stately/color/src/Color.ts +++ b/packages/@react-stately/color/src/Color.ts @@ -11,113 +11,68 @@ */ import {clamp} from '@react-aria/utils'; -import {ColorChannel, ColorFormat} from '@react-types/color'; +import {ColorChannel, ColorFormat, Color as IColor} from '@react-types/color'; -export class Color { - private value: ColorValue; +interface ColorChannelRange { + minValue: number, + maxValue: number, + step: number +} - constructor(value: string) { - let parsed: ColorValue | void = RGBColor.parse(value) || HSBColor.parse(value) || HSLColor.parse(value); - if (parsed) { - this.value = parsed; - } else { - throw new Error('Invalid color value: ' + value); - } +export function parseColor(value: string): IColor { + let res = RGBColor.parse(value) || HSBColor.parse(value) || HSLColor.parse(value); + if (res) { + return res; } - private static fromColorValue(value: ColorValue): Color { - let x: Color = Object.create(Color.prototype); - x.value = value; - return x; - } + throw new Error('Invalid color value: ' + value); +} - toFormat(format: ColorFormat): Color { - switch (format) { - case 'hex': - case 'hexa': - case 'rgb': - case 'rgba': - return Color.fromColorValue(this.value.toRGB()); - case 'hsl': - case 'hsla': - return Color.fromColorValue(this.value.toHSL()); - case 'hsb': - case 'hsba': - return Color.fromColorValue(this.value.toHSB()); - default: - throw new Error('Invalid color format: ' + format); - } +export function getColorChannelRange(channel: ColorChannel): ColorChannelRange { + switch (channel) { + case 'hue': + return {minValue: 0, maxValue: 360, step: 1}; + case 'saturation': + case 'lightness': + case 'brightness': + return {minValue: 0, maxValue: 100, step: 1}; + case 'red': + case 'green': + case 'blue': + return {minValue: 0, maxValue: 255, step: 1}; + case 'alpha': + return {minValue: 0, maxValue: 1, step: 0.01}; + default: + throw new Error('Unknown color channel: ' + channel); } +} - toString(format: ColorFormat | 'css') { - switch (format) { - case 'css': - return this.value.toString('css'); - case 'hex': - case 'hexa': - case 'rgb': - case 'rgba': - return this.value.toRGB().toString(format); - case 'hsl': - case 'hsla': - return this.value.toHSL().toString(format); - case 'hsb': - case 'hsba': - return this.value.toHSB().toString(format); - default: - throw new Error('Invalid color format: ' + format); - } - } +abstract class Color implements IColor { + abstract toFormat(format: ColorFormat): IColor; + abstract toString(format: ColorFormat | 'css'): string; + abstract clone(): Color; toHexInt(): number { - return this.value.toRGB().toInt(); + return this.toFormat('rgb').toHexInt(); } getChannelValue(channel: ColorChannel): number { - if (channel in this.value) { - return this.value[channel]; + if (channel in this) { + return this[channel]; } throw new Error('Unsupported color channel: ' + channel); } - withChannelValue(channel: ColorChannel, value: number): Color { - if (channel in this.value) { - let x = Color.fromColorValue(this.value.clone()); - x.value[channel] = value; + withChannelValue(channel: ColorChannel, value: number): IColor { + if (channel in this) { + let x = this.clone(); + x[channel] = value; return x; } throw new Error('Unsupported color channel: ' + channel); } - - static getRange(channel: ColorChannel) { - switch (channel) { - case 'hue': - return {minValue: 0, maxValue: 360, step: 1}; - case 'saturation': - case 'lightness': - case 'brightness': - return {minValue: 0, maxValue: 100, step: 1}; - case 'red': - case 'green': - case 'blue': - return {minValue: 0, maxValue: 255, step: 1}; - case 'alpha': - return {minValue: 0, maxValue: 1, step: 0.01}; - default: - throw new Error('Unknown color channel: ' + channel); - } - } -} - -interface ColorValue { - toRGB(): ColorValue, - toHSB(): ColorValue, - toHSL(): ColorValue, - toInt(): number, - toString(format: ColorFormat | 'css'): string, - clone(): ColorValue } const HEX_REGEX = /^#(?:([0-9a-f]{3})|([0-9a-f]{6}))$/i; @@ -128,8 +83,10 @@ const HEX_REGEX = /^#(?:([0-9a-f]{3})|([0-9a-f]{6}))$/i; // - rgba(X, X, X, X) const RGB_REGEX = /rgb\(([-+]?\d+(?:.\d+)?\s*,\s*[-+]?\d+(?:.\d+)?\s*,\s*[-+]?\d+(?:.\d+)?)\)|rgba\(([-+]?\d+(?:.\d+)?\s*,\s*[-+]?\d+(?:.\d+)?\s*,\s*[-+]?\d+(?:.\d+)?\s*,\s*[-+]?\d(.\d+)?)\)/; -class RGBColor implements ColorValue { - constructor(private red: number, private green: number, private blue: number, private alpha: number) {} +class RGBColor extends Color { + constructor(private red: number, private green: number, private blue: number, private alpha: number) { + super(); + } static parse(value: string): RGBColor | void { let m; @@ -163,40 +120,41 @@ class RGBColor implements ColorValue { case 'rgba': return `rgba(${this.red}, ${this.green}, ${this.blue}, ${this.alpha})`; default: - throw new Error('Unsupported color format: ' + format); + return this.toFormat(format).toString(format); } } - toInt() { - return this.red << 16 | this.green << 8 | this.blue; - } - - toRGB(): ColorValue { - return this; - } - - toHSB(): ColorValue { - throw new Error('Not implemented'); + toFormat(format: ColorFormat): IColor { + switch (format) { + case 'hex': + case 'hexa': + case 'rgb': + case 'rgba': + return this; + default: + throw new Error('Unsupported color conversion: rgb -> ' + format); + } } - toHSL(): ColorValue { - throw new Error('Not implemented'); + toHexInt(): number { + return this.red << 16 | this.green << 8 | this.blue; } - clone(): ColorValue { + clone(): Color { return new RGBColor(this.red, this.green, this.blue, this.alpha); } } - // X = // before/after a comma, 0 or more whitespaces are allowed // - hsb(X, X%, X%) // - hsba(X, X%, X%, X) const HSB_REGEX = /hsb\(([-+]?\d+(?:.\d+)?\s*,\s*[-+]?\d+(?:.\d+)?%\s*,\s*[-+]?\d+(?:.\d+)?%)\)|hsba\(([-+]?\d+(?:.\d+)?\s*,\s*[-+]?\d+(?:.\d+)?%\s*,\s*[-+]?\d+(?:.\d+)?%\s*,\s*[-+]?\d(.\d+)?)\)/; -class HSBColor implements ColorValue { - constructor(private hue: number, private saturation: number, private brightness: number, private alpha: number) {} +class HSBColor extends Color { + constructor(private hue: number, private saturation: number, private brightness: number, private alpha: number) { + super(); + } static parse(value: string): HSBColor | void { let m: RegExpMatchArray | void; @@ -215,23 +173,24 @@ class HSBColor implements ColorValue { case 'hsba': return `hsba(${this.hue}, ${this.saturation}%, ${this.brightness}%, ${this.alpha})`; default: - throw new Error('Unsupported color format: ' + format); + return this.toFormat(format).toString(format); } } - toInt(): number { - throw new Error('Not implemented'); - } - - toRGB(): ColorValue { - throw new Error('Not implemented'); - } - - toHSB(): ColorValue { - return this; + toFormat(format: ColorFormat): IColor { + switch (format) { + case 'hsb': + case 'hsba': + return this; + case 'hsl': + case 'hsla': + return this.toHSL(); + default: + throw new Error('Unsupported color conversion: hsb -> ' + format); + } } - toHSL(): ColorValue { + private toHSL(): Color { // determine the lightness in the range [0,100] var l = (2 - this.saturation / 100) * this.brightness / 2; @@ -248,7 +207,7 @@ class HSBColor implements ColorValue { return new HSLColor(hue, saturation, lightness, this.alpha); } - clone(): ColorValue { + clone(): Color { return new HSBColor(this.hue, this.saturation, this.brightness, this.alpha); } } @@ -264,8 +223,10 @@ function mod(n, m) { } // eslint-disable-next-line @typescript-eslint/no-unused-vars -class HSLColor implements ColorValue { - constructor(private hue: number, private saturation: number, private lightness: number, private alpha: number) {} +class HSLColor extends Color { + constructor(private hue: number, private saturation: number, private lightness: number, private alpha: number) { + super(); + } // eslint-disable-next-line @typescript-eslint/no-unused-vars static parse(value: string): HSLColor | void { @@ -284,27 +245,21 @@ class HSLColor implements ColorValue { case 'hsla': return `hsla(${this.hue}, ${this.saturation}%, ${this.lightness}%, ${this.alpha})`; default: - throw new Error('Unsupported color format: ' + format); + return this.toFormat(format).toString(format); } } - toInt(): number { - throw new Error('Not implemented'); - } - - toRGB(): ColorValue { - throw new Error('Not implemented'); - } - - toHSB(): ColorValue { - throw new Error('Not implemented'); - } - - toHSL(): ColorValue { - return this; + toFormat(format: ColorFormat): IColor { + switch (format) { + case 'hsl': + case 'hsla': + return this; + default: + throw new Error('Unsupported color conversion: hsl -> ' + format); + } } - clone(): ColorValue { + clone(): Color { return new HSLColor(this.hue, this.saturation, this.lightness, this.alpha); } } diff --git a/packages/@react-stately/color/src/useColor.ts b/packages/@react-stately/color/src/useColor.ts index 85a4ab3274b..ea9ae3ee616 100644 --- a/packages/@react-stately/color/src/useColor.ts +++ b/packages/@react-stately/color/src/useColor.ts @@ -10,15 +10,15 @@ * governing permissions and limitations under the License. */ -import {Color} from './Color'; import {ColorInput} from '@react-types/color'; +import {parseColor} from './Color'; import {useMemo} from 'react'; export function useColor(value: ColorInput) { return useMemo(() => { if (typeof value === 'string') { try { - return new Color(value); + return parseColor(value); } catch (err) { return undefined; } diff --git a/packages/@react-stately/color/src/useColorSliderState.ts b/packages/@react-stately/color/src/useColorSliderState.ts index 60d90c25571..7eac4707479 100644 --- a/packages/@react-stately/color/src/useColorSliderState.ts +++ b/packages/@react-stately/color/src/useColorSliderState.ts @@ -10,16 +10,16 @@ * governing permissions and limitations under the License. */ -import {Color} from './Color'; -import {ColorSliderProps} from '@react-types/color'; +import {ColorSliderProps, Color as IColor} from '@react-types/color'; +import {getColorChannelRange, parseColor} from './Color'; import {SliderState, useSliderState} from '@react-stately/slider'; import {useControlledState} from '@react-stately/utils'; export interface ColorSliderState extends SliderState { - value: Color, - setValue(value: string | Color): void, + value: IColor, + setValue(value: string | IColor): void, /** Returns the color that should be displayed in the slider instead of `value` or the optional parameter. */ - getDisplayColor(c?: Color): Color + getDisplayColor(c?: IColor): IColor } @@ -27,9 +27,9 @@ interface ColorSliderStateOptions extends ColorSliderProps { numberFormatter: Intl.NumberFormat } -function normalizeColor(v: string | Color) { +function normalizeColor(v: string | IColor) { if (typeof v === 'string') { - return new Color(v); + return parseColor(v); } else { return v; } @@ -44,7 +44,7 @@ export function useColorSliderState(props: ColorSliderStateOptions): ColorSlider let [color, setColor] = useControlledState(value && normalizeColor(value), defaultValue && normalizeColor(defaultValue), onChange); let sliderState = useSliderState({ - ...Color.getRange(channel), + ...getColorChannelRange(channel), ...otherProps, numberFormatter, value: [color.getChannelValue(channel)], @@ -65,10 +65,10 @@ export function useColorSliderState(props: ColorSliderStateOptions): ColorSlider setValue(value) { setColor(normalizeColor(value)); }, - getDisplayColor(c: Color = color) { + getDisplayColor(c: IColor = color) { switch (channel) { case 'hue': - return new Color(`hsl(${c.getChannelValue('hue')}, 100%, 50%)`); + return parseColor(`hsl(${c.getChannelValue('hue')}, 100%, 50%)`); case 'lightness': c = c.withChannelValue('saturation', 0); case 'brightness': diff --git a/packages/@react-stately/color/src/useColorWheelState.ts b/packages/@react-stately/color/src/useColorWheelState.ts index 43be20891eb..24f04e9ce5e 100644 --- a/packages/@react-stately/color/src/useColorWheelState.ts +++ b/packages/@react-stately/color/src/useColorWheelState.ts @@ -10,34 +10,34 @@ * governing permissions and limitations under the License. */ -import {Color} from './Color'; -import {ColorWheelProps} from '@react-types/color'; +import {ColorWheelProps, Color as IColor} from '@react-types/color'; +import {parseColor} from './Color'; import {useControlledState} from '@react-stately/utils'; import {useState} from 'react'; export interface ColorWheelState { - readonly value: Color, - setValue(value: string | Color): void, + readonly value: IColor, + setValue(value: string | IColor): void, readonly hue: number, setHue(value: number): void, - increment(minStepSize?: number), - decrement(minStepSize?: number), + increment(minStepSize?: number): void, + decrement(minStepSize?: number): void, isDragging: boolean, setDragging(value: boolean): void } -function normalizeColor(v: string | Color) { +function normalizeColor(v: string | IColor) { if (typeof v === 'string') { - return new Color(v); + return parseColor(v); } else { return v; } } -const DEFAULT_COLOR = new Color('hsl(0, 100%, 50%)'); +const DEFAULT_COLOR = parseColor('hsl(0, 100%, 50%)'); function roundToStep(value: number, step: number): number { return Math.round(value / step) * step; diff --git a/packages/@react-stately/color/src/useHexColorFieldState.ts b/packages/@react-stately/color/src/useHexColorFieldState.ts index 41c06e67b4d..73acc0e6624 100644 --- a/packages/@react-stately/color/src/useHexColorFieldState.ts +++ b/packages/@react-stately/color/src/useHexColorFieldState.ts @@ -10,20 +10,20 @@ * governing permissions and limitations under the License. */ -import {Color} from './Color'; -import {HexColorFieldProps} from '@react-types/color'; +import {HexColorFieldProps, Color as IColor} from '@react-types/color'; import {NumberFieldState} from '@react-stately/numberfield'; +import {parseColor} from './Color'; import {useColor} from './useColor'; import {useControlledState} from '@react-stately/utils'; import {useEffect, useState} from 'react'; export interface HexColorFieldState extends Omit { - colorValue: Color, + colorValue: IColor, setInputValue: (value: string) => void } -const MIN_COLOR = new Color('#000000'); -const MAX_COLOR = new Color('#FFFFFF'); +const MIN_COLOR = parseColor('#000000'); +const MAX_COLOR = parseColor('#FFFFFF'); const MIN_COLOR_INT = MIN_COLOR.toHexInt(); const MAX_COLOR_INT = MAX_COLOR.toHexInt(); @@ -37,10 +37,10 @@ export function useHexColorFieldState( onChange, validationState } = props; - + let initialValue = useColor(value); let initialDefaultValue = useColor(defaultValue); - let [colorValue, setColorValue] = useControlledState(initialValue, initialDefaultValue, onChange); + let [colorValue, setColorValue] = useControlledState(initialValue, initialDefaultValue, onChange); let initialInputValue = (value || defaultValue) && colorValue ? colorValue.toString('hex') : ''; let [inputValue, setInputValue] = useState(initialInputValue); @@ -48,10 +48,10 @@ export function useHexColorFieldState( useEffect(() => { setInputValue(inputValue => { // Parse color from current inputValue. - // Only update the input value if the new colorValue is not equivalent. + // Only update the input value if the parseColorValue is not equivalent. if (!inputValue.length && colorValue) { return colorValue.toString('hex'); } try { - let currentColor = new Color(inputValue.startsWith('#') ? inputValue : `#${inputValue}`); + let currentColor = parseColor(inputValue.startsWith('#') ? inputValue : `#${inputValue}`); if (currentColor.toHexInt() !== colorValue?.toHexInt()) { return colorValue ? colorValue.toString('hex') : ''; } @@ -62,10 +62,10 @@ export function useHexColorFieldState( }); }, [inputValue, colorValue, setInputValue]); - let increment = () => setColorValue((prevColor: Color) => addColorValue(prevColor, step)); - let decrement = () => setColorValue((prevColor: Color) => addColorValue(prevColor, -step)); - let incrementToMax = () => setColorValue((prevColor: Color) => addColorValue(prevColor, MAX_COLOR_INT)); - let decrementToMin = () => setColorValue((prevColor: Color) => addColorValue(prevColor, -MAX_COLOR_INT)); + let increment = () => setColorValue((prevColor: IColor) => addColorValue(prevColor, step)); + let decrement = () => setColorValue((prevColor: IColor) => addColorValue(prevColor, -step)); + let incrementToMax = () => setColorValue((prevColor: IColor) => addColorValue(prevColor, MAX_COLOR_INT)); + let decrementToMin = () => setColorValue((prevColor: IColor) => addColorValue(prevColor, -MAX_COLOR_INT)); let setFieldInputValue = (value: string) => { value = value.match(/^#?[0-9a-f]{0,6}$/i)?.[0]; @@ -75,8 +75,8 @@ export function useHexColorFieldState( return; } try { - let newColor = new Color(value.startsWith('#') ? value : `#${value}`); - setColorValue((prevColor: Color) => { + let newColor = parseColor(value.startsWith('#') ? value : `#${value}`); + setColorValue((prevColor: IColor) => { setInputValue(value); return prevColor && prevColor.toHexInt() === newColor.toHexInt() ? prevColor : newColor; }); @@ -103,15 +103,15 @@ export function useHexColorFieldState( }; } -function addColorValue(color: Color, step: number) { +function addColorValue(color: IColor, step: number) { let newColor = color ? color : MIN_COLOR; let colorInt = newColor.toHexInt(); let newColorString = color ? color.toString('hex') : ''; - + let clampInt = Math.min(Math.max(colorInt + step, MIN_COLOR_INT), MAX_COLOR_INT); if (clampInt !== colorInt) { newColorString = `#${clampInt.toString(16).padStart(6, '0').toUpperCase()}`; - newColor = new Color(newColorString); + newColor = parseColor(newColorString); } return newColor; } diff --git a/packages/@react-stately/color/test/Color.test.js b/packages/@react-stately/color/test/Color.test.js index 50181b2833d..86354f59ed2 100644 --- a/packages/@react-stately/color/test/Color.test.js +++ b/packages/@react-stately/color/test/Color.test.js @@ -10,12 +10,12 @@ * governing permissions and limitations under the License. */ -import {Color} from '../src/Color'; +import {parseColor} from '../src/Color'; describe('Color', function () { describe('hex', function () { it('should parse a short hex color', function () { - let color = new Color('#abc'); + let color = parseColor('#abc'); expect(color.getChannelValue('red')).toBe(170); expect(color.getChannelValue('green')).toBe(187); expect(color.getChannelValue('blue')).toBe(204); @@ -27,7 +27,7 @@ describe('Color', function () { }); it('should parse a long hex color', function () { - let color = new Color('#abcdef'); + let color = parseColor('#abcdef'); expect(color.getChannelValue('red')).toBe(171); expect(color.getChannelValue('green')).toBe(205); expect(color.getChannelValue('blue')).toBe(239); @@ -39,18 +39,18 @@ describe('Color', function () { }); it('should throw on invalid hex value', function () { - expect(() => new Color('#ggg')).toThrow('Invalid color value: #ggg'); + expect(() => parseColor('#ggg')).toThrow('Invalid color value: #ggg'); }); }); it('should convert a color to its equivalent hex value in decimal format', function () { - const color = new Color('#abcdef'); + const color = parseColor('#abcdef'); expect(color.toHexInt()).toBe(0xABCDEF); }); describe('rgb', function () { it('should parse a rgb color', function () { - let color = new Color('rgb(128, 128, 0)'); + let color = parseColor('rgb(128, 128, 0)'); expect(color.getChannelValue('red')).toBe(128); expect(color.getChannelValue('green')).toBe(128); expect(color.getChannelValue('blue')).toBe(0); @@ -61,7 +61,7 @@ describe('Color', function () { }); it('should parse a rgba color', function () { - let color = new Color('rgba(128, 128, 0, 0.5)'); + let color = parseColor('rgba(128, 128, 0, 0.5)'); expect(color.getChannelValue('red')).toBe(128); expect(color.getChannelValue('green')).toBe(128); expect(color.getChannelValue('blue')).toBe(0); @@ -72,7 +72,7 @@ describe('Color', function () { }); it('normalizes rgba value by clamping', function () { - let color = new Color('rgba(300, -10, 0, 4)'); + let color = parseColor('rgba(300, -10, 0, 4)'); expect(color.getChannelValue('red')).toBe(255); expect(color.getChannelValue('green')).toBe(0); expect(color.getChannelValue('blue')).toBe(0); @@ -83,7 +83,7 @@ describe('Color', function () { describe('hsl', function () { it('should parse a hsl color', function () { - let color = new Color('hsl(120, 100%, 50%)'); + let color = parseColor('hsl(120, 100%, 50%)'); expect(color.getChannelValue('hue')).toBe(120); expect(color.getChannelValue('saturation')).toBe(100); expect(color.getChannelValue('lightness')).toBe(50); @@ -94,7 +94,7 @@ describe('Color', function () { }); it('should parse a hsla color', function () { - let color = new Color('hsla(120, 100%, 50%, 0.5)'); + let color = parseColor('hsla(120, 100%, 50%, 0.5)'); expect(color.getChannelValue('hue')).toBe(120); expect(color.getChannelValue('saturation')).toBe(100); expect(color.getChannelValue('lightness')).toBe(50); @@ -105,7 +105,7 @@ describe('Color', function () { }); it('normalizes hsla value by clamping', function () { - let color = new Color('hsla(-400, 120%, -4%, -1)'); + let color = parseColor('hsla(-400, 120%, -4%, -1)'); expect(color.getChannelValue('hue')).toBe(320); expect(color.getChannelValue('saturation')).toBe(100); expect(color.getChannelValue('lightness')).toBe(0); @@ -115,7 +115,7 @@ describe('Color', function () { }); it('withChannelValue', () => { - let color = new Color('hsl(120, 100%, 50%)'); + let color = parseColor('hsl(120, 100%, 50%)'); let newColor = color.withChannelValue('hue', 200); expect(newColor.getChannelValue('hue')).toBe(200); expect(newColor.getChannelValue('saturation')).toBe(color.getChannelValue('saturation')); @@ -125,7 +125,7 @@ describe('Color', function () { describe('hsb', function () { it('should parse a hsb color', function () { - let color = new Color('hsb(120, 100%, 50%)'); + let color = parseColor('hsb(120, 100%, 50%)'); expect(color.getChannelValue('hue')).toBe(120); expect(color.getChannelValue('saturation')).toBe(100); expect(color.getChannelValue('brightness')).toBe(50); @@ -135,7 +135,7 @@ describe('Color', function () { }); it('should parse a hsba color', function () { - let color = new Color('hsba(120, 100%, 50%, 0.5)'); + let color = parseColor('hsba(120, 100%, 50%, 0.5)'); expect(color.getChannelValue('hue')).toBe(120); expect(color.getChannelValue('saturation')).toBe(100); expect(color.getChannelValue('brightness')).toBe(50); @@ -145,7 +145,7 @@ describe('Color', function () { }); it('normalizes hsba value by clamping', function () { - let color = new Color('hsba(-400, 120%, -4%, -1)'); + let color = parseColor('hsba(-400, 120%, -4%, -1)'); expect(color.getChannelValue('hue')).toBe(320); expect(color.getChannelValue('saturation')).toBe(100); expect(color.getChannelValue('brightness')).toBe(0); @@ -155,7 +155,7 @@ describe('Color', function () { }); it('withChannelValue', () => { - let color = new Color('hsl(120, 100%, 50%)'); + let color = parseColor('hsl(120, 100%, 50%)'); let newColor = color.withChannelValue('hue', 200); expect(newColor.getChannelValue('hue')).toBe(200); expect(newColor.getChannelValue('saturation')).toBe(color.getChannelValue('saturation')); diff --git a/packages/@react-stately/color/test/useColor.test.js b/packages/@react-stately/color/test/useColor.test.js index 864f2891efa..692b779743c 100644 --- a/packages/@react-stately/color/test/useColor.test.js +++ b/packages/@react-stately/color/test/useColor.test.js @@ -10,14 +10,13 @@ * governing permissions and limitations under the License. */ -import {Color} from '../src/Color'; +import {parseColor} from '../src/Color'; import {renderHook} from '@testing-library/react-hooks'; import {useColor} from '../'; describe('useColor tests', function () { it('should accept string value', function () { let {result} = renderHook(() => useColor('#abc')); - expect(result.current).toBeInstanceOf(Color); expect(result.current.getChannelValue('red')).toBe(170); expect(result.current.getChannelValue('green')).toBe(187); expect(result.current.getChannelValue('blue')).toBe(204); @@ -25,7 +24,7 @@ describe('useColor tests', function () { }); it('should return the same Color object if provided as argument', function () { - let color = new Color('#abc'); + let color = parseColor('#abc'); let {result} = renderHook(() => useColor(color)); expect(result.current).toBe(color); }); diff --git a/packages/@react-stately/color/test/useHexColorFieldState.test.js b/packages/@react-stately/color/test/useHexColorFieldState.test.js index a8dd7ac0bd3..146ec64fe50 100644 --- a/packages/@react-stately/color/test/useHexColorFieldState.test.js +++ b/packages/@react-stately/color/test/useHexColorFieldState.test.js @@ -11,7 +11,7 @@ */ import {act, renderHook} from '@testing-library/react-hooks'; -import {Color} from '../src/Color'; +import {parseColor} from '../src/Color'; import {useHexColorFieldState} from '../'; describe('useHexColorFieldState tests', function () { @@ -29,10 +29,10 @@ describe('useHexColorFieldState tests', function () { Name | props ${'6-length hex string'} | ${{defaultValue: '#aabbcc'}} ${'3-length hex string'} | ${{defaultValue: '#abc'}} - ${'Color object'} | ${{defaultValue: new Color('#aabbcc')}} + ${'Color object'} | ${{defaultValue: parseColor('#aabbcc')}} ${'6-length hex string (controlled)'} | ${{value: '#aabbcc'}} ${'3-length hex string (controlled)'} | ${{value: '#abc'}} - ${'Color object (controlled)'} | ${{value: new Color('#aabbcc')}} + ${'Color object (controlled)'} | ${{value: parseColor('#aabbcc')}} `('should accept $Name as value', function ({props}) { let {result} = renderHook(() => useHexColorFieldState(props)); expect(result.current.colorValue.getChannelValue('red')).toBe(170); @@ -127,7 +127,7 @@ describe('useHexColorFieldState tests', function () { expect(result.current.colorValue.getChannelValue('alpha')).toBe(1); expect(result.current.inputValue).toBe('#AABBCC'); - let newColor = new Color('#cba'); + let newColor = parseColor('#cba'); act(() => result.current.setInputValue('#cba')); expect(onChangeSpy).toHaveBeenCalledWith(newColor); expect(result.current.colorValue.getChannelValue('red')).toBe(204); @@ -147,7 +147,7 @@ describe('useHexColorFieldState tests', function () { expect(result.current.colorValue.getChannelValue('alpha')).toBe(1); expect(result.current.inputValue).toBe('#AABBCC'); - let newColor = new Color('#cba'); + let newColor = parseColor('#cba'); act(() => result.current.setInputValue('#cba')); expect(onChangeSpy).toHaveBeenCalledWith(newColor); expect(result.current.colorValue.getChannelValue('red')).toBe(170); diff --git a/packages/@react-types/color/package.json b/packages/@react-types/color/package.json index 8fe9afa8acd..8dfa03b9d71 100644 --- a/packages/@react-types/color/package.json +++ b/packages/@react-types/color/package.json @@ -10,7 +10,6 @@ "url": "https://github.com/adobe/react-spectrum" }, "dependencies": { - "@react-stately/color": "3.0.0-alpha.1", "@react-types/shared": "^3.1.0", "@react-types/slider": "^3.0.0" }, diff --git a/packages/@react-types/color/src/index.d.ts b/packages/@react-types/color/src/index.d.ts index e2410043989..7d29cc24fad 100644 --- a/packages/@react-types/color/src/index.d.ts +++ b/packages/@react-types/color/src/index.d.ts @@ -25,7 +25,6 @@ import { TextInputDOMProps, Validation } from '@react-types/shared'; -import {Color} from '@react-stately/color'; import {SliderProps} from '@react-types/slider'; /** A list of supported color formats. */ @@ -34,6 +33,14 @@ export type ColorFormat = 'hex' | 'hexa' | 'rgb' | 'rgba' | 'hsl' | 'hsla' | 'hs /** A list of color channels. */ export type ColorChannel = 'hue' | 'saturation' | 'brightness' | 'lightness' | 'red' | 'green' | 'blue' | 'alpha'; +export interface Color { + toFormat(format: ColorFormat): Color, + toString(format: ColorFormat | 'css'): string, + toHexInt(): number, + getChannelValue(channel: ColorChannel): number, + withChannelValue(channel: ColorChannel, value: number): Color +} + export type ColorInput = string | Color; export interface HexColorFieldProps extends InputBase, Validation, FocusableProps, TextInputBase, LabelableProps {