diff --git a/src/components/advanced-color-picker/advanced-color-picker-cell.style.js b/src/components/advanced-color-picker/advanced-color-picker-cell.style.ts similarity index 100% rename from src/components/advanced-color-picker/advanced-color-picker-cell.style.js rename to src/components/advanced-color-picker/advanced-color-picker-cell.style.ts diff --git a/src/components/advanced-color-picker/advanced-color-picker.component.js b/src/components/advanced-color-picker/advanced-color-picker.component.tsx similarity index 74% rename from src/components/advanced-color-picker/advanced-color-picker.component.js rename to src/components/advanced-color-picker/advanced-color-picker.component.tsx index d5833209d2..68dfb5f75a 100644 --- a/src/components/advanced-color-picker/advanced-color-picker.component.js +++ b/src/components/advanced-color-picker/advanced-color-picker.component.tsx @@ -1,6 +1,5 @@ import React, { useState, useEffect, useCallback, useRef } from "react"; -import PropTypes from "prop-types"; -import styledSystemPropTypes from "@styled-system/prop-types"; +import { MarginProps } from "styled-system"; import { StyledAdvancedColorPickerWrapper, StyledAdvancedColorPickerCell, @@ -11,54 +10,89 @@ import { SimpleColorPicker, SimpleColor } from "../simple-color-picker"; import Events from "../../__internal__/utils/helpers/events"; import { filterStyledSystemMarginProps } from "../../style/utils"; -const marginPropTypes = filterStyledSystemMarginProps( - styledSystemPropTypes.space -); +export interface AdvancedColor { + label: string; + value: string; +} + +export interface AdvancedColorPickerProps extends MarginProps { + /** Prop to specify the aria-describedby property of the component */ + "aria-describedby"?: string; + /** + * Prop to specify the aria-label of the component. + * To be used only when the title prop is not defined, and the component is not labelled by any internal element. + */ + "aria-label"?: string; + /** + * Prop to specify the aria-labeledby property of the component + * To be used when the title prop is a custom React Node, + * or the component is labelled by an internal element other than the title. + */ + "aria-labelledby"?: string; + /** Prop for `availableColors` containing array of objects of colors */ + availableColors: AdvancedColor[]; + /** Prop for `defaultColor` containing the default color for `uncontrolled` use */ + defaultColor: string; + /** Specifies the name prop to be applied to each color in the group */ + name: string; + /** Prop for `onBlur` event */ + onBlur?: (ev: React.FocusEvent) => void; + /** Prop for `onChange` event */ + onChange?: (ev: React.ChangeEvent) => void; + /** Prop for `onClose` event */ + onClose?: (ev: React.MouseEvent) => void; + /** Prop for `onOpen` event */ + onOpen?: (ev: React.MouseEvent) => void; + /** Prop for `open` status */ + open?: boolean; + /** The ARIA role to be applied to the component container */ + role?: string; + /** Prop for `selectedColor` containing pre-selected color for `controlled` use */ + selectedColor?: string; +} const AdvancedColorPicker = ({ "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, + availableColors, + defaultColor, name, - open, onOpen, onClose, onChange, onBlur, - availableColors, - defaultColor, - selectedColor, + open = false, role, + selectedColor, ...props -}) => { - const isOpen = open || false; - const [colors, setColors] = useState(); - const [dialogOpen, setDialogOpen] = useState(); +}: AdvancedColorPickerProps) => { + const [dialogOpen, setDialogOpen] = useState(); const currentColor = selectedColor || defaultColor; - const [selectedColorRef, setSelectedColorRef] = useState(null); - - const simpleColorPickerData = useRef(); - - useEffect( - () => - setColors( - availableColors.map(({ value, label }, index) => { - return { - value, - label, - getRef: () => - /* istanbul ignore next */ - simpleColorPickerData.current - ? simpleColorPickerData.current.gridItemRefs[index] - : null, - }; - }) - ), - [availableColors] - ); + const [ + selectedColorRef, + setSelectedColorRef, + ] = useState(null); + + const simpleColorPickerData = useRef<{ + gridItemRefs: Array; + }>(); + + const colors = availableColors.map(({ value, label }, index) => { + return { + value, + label, + getRef: () => + /* Fallback to null to satisfy the TypeScript compiler */ + /* istanbul ignore next */ + simpleColorPickerData.current + ? simpleColorPickerData.current.gridItemRefs[index] + : null, + }; + }); useEffect(() => { - if (dialogOpen || isOpen) { + if (dialogOpen || open) { const newColor = colors?.find((c) => currentColor === c.value); /* istanbul ignore else */ @@ -66,15 +100,13 @@ const AdvancedColorPicker = ({ setSelectedColorRef(newColor.getRef()); } } - }, [colors, currentColor, dialogOpen, isOpen]); + }, [colors, currentColor, dialogOpen, open]); const handleFocus = useCallback( (e, firstFocusableElement) => { /* istanbul ignore else */ if (e.key === "Tab") { - /* istanbul ignore else */ if (e.shiftKey) { - /* istanbul ignore else */ if ( document.activeElement === firstFocusableElement && selectedColorRef @@ -83,7 +115,6 @@ const AdvancedColorPicker = ({ e.preventDefault(); } } else if (document.activeElement === selectedColorRef) { - /* istanbul ignore else */ firstFocusableElement.focus(); e.preventDefault(); } @@ -120,7 +151,7 @@ const AdvancedColorPicker = ({ /* istanbul ignore else */ if (newColor) { - setSelectedColorRef(newColor.ref); + setSelectedColorRef(newColor.getRef()); } /* istanbul ignore else */ @@ -170,13 +201,13 @@ const AdvancedColorPicker = ({ onClick={handleOnOpen} onKeyDown={handleOnKeyDown} color={currentColor} - tabIndex="0" + tabIndex={0} /> ) => void; - /** Prop for `onChange` event */ - onChange?: (ev: React.ChangeEvent) => void; - /** Prop for `onClose` event */ - onClose?: (ev: React.MouseEvent) => void; - /** Prop for `onOpen` event */ - onOpen?: (ev: React.MouseEvent) => void; - /** Prop for `open` status */ - open?: boolean; - /** The ARIA role to be applied to the component container */ - role?: string; - /** Prop for `selectedColor` containing pre-selected color for `controlled` use */ - selectedColor?: string; -} - -declare function AdvancedColorPicker( - props: AdvancedColorPickerPropTypes -): JSX.Element; - -export default AdvancedColorPicker; diff --git a/src/components/advanced-color-picker/advanced-color-picker.spec.js b/src/components/advanced-color-picker/advanced-color-picker.spec.tsx similarity index 89% rename from src/components/advanced-color-picker/advanced-color-picker.spec.js rename to src/components/advanced-color-picker/advanced-color-picker.spec.tsx index 05f3352be4..83d6afe9ef 100644 --- a/src/components/advanced-color-picker/advanced-color-picker.spec.js +++ b/src/components/advanced-color-picker/advanced-color-picker.spec.tsx @@ -1,18 +1,22 @@ import React, { useState } from "react"; import { config } from "react-transition-group"; -import { mount } from "enzyme"; +import { mount, ReactWrapper } from "enzyme"; import { act } from "react-test-renderer"; -import AdvancedColorPicker from "./advanced-color-picker.component"; +import AdvancedColorPicker, { + AdvancedColorPickerProps, +} from "./advanced-color-picker.component"; import Dialog from "../dialog/dialog.component"; import { SimpleColor } from "../simple-color-picker"; import guid from "../../__internal__/utils/helpers/guid"; import { testStyledSystemMargin } from "../../__spec_helper__/test-utils"; import { StyledAdvancedColorPickerPreview } from "./advanced-color-picker.style"; +const mockedGuid = "mocked-guid"; + config.disabled = true; jest.mock("../../__internal__/utils/helpers/guid"); -guid.mockImplementation(() => "guid-12345"); +(guid as jest.MockedFunction).mockReturnValue(mockedGuid); const element = document.createElement("div"); const defaultColor = "#EBAEDE"; @@ -38,18 +42,18 @@ const requiredProps = { document.body.appendChild(element); -function render(props) { +function render(props: AdvancedColorPickerProps) { return mount(); } -function renderInDocument(props) { +function renderInDocument(props: AdvancedColorPickerProps) { return mount(, { attachTo: element, }); } -function getElements(wrapper) { - const dialogCloseButton = wrapper +function getElements(wrapper: ReactWrapper) { + const dialogCloseButton: HTMLButtonElement = wrapper .find(`button[data-element="close"]`) .getDOMNode(); const defaultSimpleColor = wrapper @@ -77,15 +81,15 @@ describe("AdvancedColorPicker", () => { [" ", true], ["a", false], ]; - let colorPickerCell; - let wrapper; + let colorPickerCell: ReactWrapper; + let wrapper: ReactWrapper; beforeEach(() => { wrapper = render({ ...requiredProps }); colorPickerCell = wrapper .find('[data-element="color-picker-cell"]') .first(); - colorPickerCell.getDOMNode().focus(); + colorPickerCell.getDOMNode().focus(); }); afterEach(() => { @@ -132,7 +136,7 @@ describe("AdvancedColorPicker", () => { describe("when the closeButton is clicked", () => { const onClose = jest.fn(); - let wrapper; + let wrapper: ReactWrapper; beforeEach(() => { wrapper = render({ onClose, open: true, ...requiredProps }); @@ -153,7 +157,7 @@ describe("AdvancedColorPicker", () => { describe("when dialog is open", () => { jest.useFakeTimers(); - let wrapper; + let wrapper: ReactWrapper; beforeEach(() => { wrapper = renderInDocument({ ...requiredProps, open: true }); @@ -197,7 +201,7 @@ describe("AdvancedColorPicker", () => { onChange, onBlur, }; - let wrapper; + let wrapper: ReactWrapper; beforeEach(() => { wrapper = renderInDocument(extraProps); @@ -205,7 +209,7 @@ describe("AdvancedColorPicker", () => { const color = wrapper.find(SimpleColor).at(8); - color.find("input").first().getDOMNode().click(); + color.find("input").first().getDOMNode().click(); }); afterEach(() => { @@ -230,9 +234,9 @@ describe("AdvancedColorPicker", () => { describe("when the component value is controlled, and a color is selected", () => { // eslint-disable-next-line react/prop-types const MockComponent = () => { - const [color, setColor] = useState(); + const [color, setColor] = useState(); - function handleOnChange(e) { + function handleOnChange(e: React.ChangeEvent) { setColor(e.target.value); } @@ -245,14 +249,14 @@ describe("AdvancedColorPicker", () => { /> ); }; - let wrapper; + let wrapper: ReactWrapper; beforeEach(() => { wrapper = mount(); jest.runAllTimers(); const color = wrapper.find(SimpleColor).at(1); - color.find("input").first().getDOMNode().click(); + color.find("input").first().getDOMNode().click(); wrapper.update(); }); @@ -264,7 +268,7 @@ describe("AdvancedColorPicker", () => { }); describe("when closeButton is clicked", () => { - let wrapper; + let wrapper: ReactWrapper; const onClose = jest.fn(); beforeEach(() => { @@ -301,7 +305,7 @@ describe("AdvancedColorPicker", () => { describe.each(keys)( "and a %p key is pressed while focused on a color button", (key, expectedResult) => { - let wrapper; + let wrapper: ReactWrapper; const extraProps = { ...requiredProps, @@ -337,7 +341,7 @@ describe("AdvancedColorPicker", () => { describe("when the color picker cell button is clicked", () => { const onOpen = jest.fn(); - let wrapper; + let wrapper: ReactWrapper; beforeEach(() => { onOpen.mockClear(); @@ -370,7 +374,7 @@ describe("AdvancedColorPicker", () => { }); describe("when the 'open' prop is specified", () => { - let wrapper; + let wrapper: ReactWrapper; beforeEach(() => { wrapper = render({ diff --git a/src/components/advanced-color-picker/advanced-color-picker.style.js b/src/components/advanced-color-picker/advanced-color-picker.style.ts similarity index 100% rename from src/components/advanced-color-picker/advanced-color-picker.style.js rename to src/components/advanced-color-picker/advanced-color-picker.style.ts diff --git a/src/components/advanced-color-picker/index.d.ts b/src/components/advanced-color-picker/index.d.ts deleted file mode 100644 index e37617ce94..0000000000 --- a/src/components/advanced-color-picker/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./advanced-color-picker"; diff --git a/src/components/advanced-color-picker/index.js b/src/components/advanced-color-picker/index.js deleted file mode 100644 index d48c8ea5ad..0000000000 --- a/src/components/advanced-color-picker/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./advanced-color-picker.component"; diff --git a/src/components/advanced-color-picker/index.ts b/src/components/advanced-color-picker/index.ts new file mode 100644 index 0000000000..4b9ae05866 --- /dev/null +++ b/src/components/advanced-color-picker/index.ts @@ -0,0 +1,2 @@ +export { default } from "./advanced-color-picker.component"; +export type { AdvancedColorPickerProps } from "./advanced-color-picker.component";