Skip to content

Commit

Permalink
Refactors the Palette comopnent in ColorPicker to remove the tailwind…
Browse files Browse the repository at this point in the history
… dependency. (#2392)

* Updated the color palette component to support hex values and remove dependency with tailwind.

* Updated the storybook to support the current changes.

* Fixed the failing tests.

* Updated the propTypes and type definition.

* Review suggestion implementation.
  • Loading branch information
deepakjosp authored Dec 5, 2024
1 parent 119c1d9 commit 3cc6448
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 98 deletions.
52 changes: 37 additions & 15 deletions src/components/ColorPicker/Palette.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,52 @@ import React from "react";
import classnames from "classnames";
import PropTypes from "prop-types";

import { TRANSPARENT } from "./constants";

const Palette = ({ color, colorList = [], onChange }) => (
<div className="neeto-ui-flex neeto-ui-flex-row neeto-ui-flex-wrap neeto-ui-items-start neeto-ui-justify-start neeto-ui-color-palette neeto-ui-gap-1">
{colorList.map((item, index) => (
<div
data-testid="color-palette-item"
key={index}
className={classnames("neeto-ui-color-palette__item neeto-ui-border", {
active: color && color.from === item.from && color.to === item.to,
})}
onClick={() => onChange(item.from, item.to)}
>
<div className={`bg-gradient-to-r from-${item.from} to-${item.to}`} />
</div>
))}
{colorList.map(item => {
const { hex, colorClassName } = item;
const isTransparent = hex === TRANSPARENT;

const isActive = Boolean(
(color?.hex && color.hex === hex) ||
(color?.colorClassName && color.colorClassName === colorClassName)
);

return (
<div
data-testid="color-palette-item"
key={hex ?? colorClassName}
className={classnames(
"neeto-ui-color-palette__item neeto-ui-border",
{ active: isActive }
)}
onClick={() => onChange(item)}
>
<div
style={{ backgroundColor: hex }}
className={classnames({
"transparent-bg-pattern": isTransparent,
colorClassName: !isTransparent,
})}
/>
</div>
);
})}
</div>
);

Palette.propTypes = {
color: PropTypes.shape({
from: PropTypes.string,
to: PropTypes.string,
hex: PropTypes.string,
colorClassName: PropTypes.string,
}),
colorList: PropTypes.arrayOf(
PropTypes.shape({ from: PropTypes.string, to: PropTypes.string })
PropTypes.shape({
hex: PropTypes.string,
colorClassName: PropTypes.string,
})
),
onChange: PropTypes.func,
};
Expand Down
1 change: 1 addition & 0 deletions src/components/ColorPicker/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const TRANSPARENT = "transparent";
8 changes: 4 additions & 4 deletions src/components/ColorPicker/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,13 @@ ColorPicker.propTypes = {
*/
colorPaletteProps: PropTypes.shape({
color: PropTypes.shape({
from: PropTypes.string,
to: PropTypes.string,
hex: PropTypes.string,
colorClassName: PropTypes.string,
}),
colorList: PropTypes.arrayOf(
PropTypes.shape({
from: PropTypes.string,
to: PropTypes.string,
hex: PropTypes.string,
colorClassName: PropTypes.string,
})
),
onChange: PropTypes.func,
Expand Down
93 changes: 32 additions & 61 deletions stories/Components/ColorPicker.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,21 @@ import { PALETTE_PICKER_CODE } from "./constants";
import ColorPickerCSSCustomization from "!raw-loader!./ColorPickerStoriesDocs/ColorPickerCSSCustomization.mdx";
import ColorPickerDocs from "!raw-loader!./ColorPickerStoriesDocs/ColorPickerDocs.mdx";

const DEFAULT_COLORS = {
"red-500": "#f22d2d",
"yellow-500": "#f57c00",
"green-500": "#00ba88",
"blue-500": "#276ef1",
"indigo-500": "#4c6ef5",
"purple-500": "#7c3aed",
"pink-500": "#f22d9e",
"gray-500": "#6b7280",
"gray-600": "#4b5563",
"gray-700": "#374151",
"gray-800": "#1f2937",
"gray-900": "#111827",
};
const DEFAULT_COLORS = [
{ hex: "#f22d2d" },
{ hex: "#f57c00" },
{ hex: "#00ba88" },
{ hex: "#276ef1" },
{ hex: "#4c6ef5" },
{ hex: "#7c3aed" },
{ hex: "#4558F9" },
{ hex: "#f22d9e" },
{ hex: "#6b7280" },
{ hex: "#4b5563" },
{ hex: "#374151" },
{ hex: "#1f2937" },
{ hex: "#111827" },
];

const metadata = {
title: "Components/ColorPicker",
Expand Down Expand Up @@ -96,45 +97,31 @@ Sizes.storyName = "Sizes";
Sizes.args = { color: "#4558F9" };

const WithColorPalette = args => {
const [color, setColor] = useState("#4558F9");
const [activeColor] = DEFAULT_COLORS;
const [selectedColor, setSelectedColor] = useState(activeColor);

const onChange = value => {
action("onChange")(value);
setColor(value.hex);
};

const colorList = Object.keys(DEFAULT_COLORS).map(key => ({
from: key,
to: key,
}));

const findColorByHex = hex => {
const colorClass = Object.keys(DEFAULT_COLORS).find(
key => hex === DEFAULT_COLORS[key]
);

return { from: colorClass, to: colorClass };
setSelectedColor(value);
};

const selectedColor = findColorByHex(color);

const handleColorChange = (fromValue, toValue) => {
action("colorPaletteProps.onChange")(fromValue, toValue);
const fromColor = DEFAULT_COLORS[fromValue];
onChange({ hex: fromColor });
const handleColorChange = color => {
action("colorPaletteProps.onChange")(color);
onChange(color);
};

useEffect(() => {
setColor(args.color || "#4558F9");
setSelectedColor({ hex: args.color });
}, [args.color]);

return (
<div className="h-60 w-40">
<ColorPicker
{...{ color, onChange }}
{...{ onChange }}
color={selectedColor.hex}
colorPaletteProps={{
color: selectedColor,
colorList,
colorList: DEFAULT_COLORS,
onChange: handleColorChange,
}}
/>
Expand Down Expand Up @@ -208,41 +195,25 @@ ShowTransparencyControl.storyName = "Show transparency control";
ShowTransparencyControl.args = { color: "#4558F9c9" };

const OnlyPalettePicker = args => {
const [color, setColor] = useState("#4558F9");
const [selectedColor, setSelectedColor] = useState("#4558F9");

useEffect(() => {
setColor(args.color || "#4558F9c9");
setSelectedColor({ hex: args.color || "#4558F9c9" });
}, [args.color]);

const colorList = Object.keys(DEFAULT_COLORS).map(key => ({
from: key,
to: key,
}));

const findColorByHex = hex => {
const colorClass = Object.keys(DEFAULT_COLORS).find(
key => hex === DEFAULT_COLORS[key]
);

return { from: colorClass, to: colorClass };
};

const selectedColor = findColorByHex(color);

const handleColorChange = (fromValue, toValue) => {
action("colorPaletteProps.onChange")(fromValue, toValue);
const fromColor = DEFAULT_COLORS[fromValue];
setColor(fromColor);
const handleColorChange = color => {
action("colorPaletteProps.onChange")(color);
setSelectedColor(color);
};

return (
<div className="h-60 w-40">
<ColorPicker
{...{ color }}
color={selectedColor.hex}
showPicker={false}
colorPaletteProps={{
color: selectedColor,
colorList,
colorList: DEFAULT_COLORS,
onChange: handleColorChange,
}}
/>
Expand Down
27 changes: 12 additions & 15 deletions tests/ColorPicker.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe("ColorPicker", () => {
expect(color.rgb).toEqual(rgb);
});

render(<ColorPicker onChange={onChange} />);
render(<ColorPicker {...{ onChange }} />);
expect(screen.getByTestId("neeto-color-picker")).toBeInTheDocument();
await userEvent.click(screen.getByTestId("neeto-color-picker"));
await (await screen.findByRole("textbox")).focus();
Expand All @@ -41,22 +41,19 @@ describe("ColorPicker", () => {

it("should trigger onChange when a color is selected from palette", async () => {
const selectedColor = "#ffffff";
const DEFAULT_COLORS = {
"red-500": "#f22d2d",
"yellow-500": "#f57c00",
"green-500": "#00ba88",
"blue-500": "#276ef1",
};
const DEFAULT_COLORS = [
{ hex: "#f22d2d" },
{ hex: "#f57c00" },
{ hex: "#00ba88" },
{ hex: "#276ef1" },
];
const onChange = jest.fn();
render(
<ColorPicker
color={selectedColor}
colorPaletteProps={{
color: { from: "red-500", to: "red-500" },
colorList: Object.keys(DEFAULT_COLORS).map(key => ({
from: key,
to: key,
})),
color: { hex: "#f57c00" },
colorList: DEFAULT_COLORS,
onChange,
}}
/>
Expand All @@ -65,12 +62,12 @@ describe("ColorPicker", () => {
const paletteItems = await screen.findAllByTestId("color-palette-item");
await userEvent.click(paletteItems[0]);
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith("red-500", "red-500");
expect(onChange).toHaveBeenCalledWith({ hex: "#f22d2d" });
});

it("should call onChange when user touches Hue slider", async () => {
const onChange = jest.fn();
render(<ColorPicker color="#ffffff" onChange={onChange} />);
render(<ColorPicker {...{ onChange }} color="#ffffff" />);
await userEvent.click(screen.getByTestId("neeto-color-picker"));
const hueSlider = await screen.findByLabelText("Hue");
await userEvent.click(hueSlider, { clientX: 0 });
Expand All @@ -88,7 +85,7 @@ describe("ColorPicker", () => {
expect(color.rgb).toEqual(rgb);
});

render(<ColorPicker color="#ffffff" onChange={onChange} />);
render(<ColorPicker {...{ onChange }} color="#ffffff" />);
await userEvent.click(screen.getByTestId("neeto-color-picker"));

await waitFor(() =>
Expand Down
11 changes: 8 additions & 3 deletions types/ColorPicker.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import React from "react";
import { DropdownProps } from "./Dropdown";

type PaletteColor = {
hex?: string;
colorClassName?: string;
}

export interface ColorPickerProps {
color: string;
size: "small" | "medium" | "large";
Expand All @@ -9,9 +14,9 @@ export interface ColorPickerProps {
rgb: { r: number; g: number; b: number, a: number };
}) => void;
colorPaletteProps?: {
color: { from: string; to: string };
colorList: { from: string; to: string }[];
onChange: (from: string, to: string) => void;
color: PaletteColor;
colorList: PaletteColor[];
onChange: (color: PaletteColor) => void;
};
showEyeDropper?: boolean;
showHexValue?: boolean;
Expand Down

0 comments on commit 3cc6448

Please sign in to comment.