Skip to content
Snippets Groups Projects
Unverified Commit 20b856fa authored by Alexander Polyankin's avatar Alexander Polyankin Committed by GitHub
Browse files

Use new ColorSelector for single color chart settings (#22974)

parent f7d8cb31
No related branches found
No related tags found
No related merge requests found
Showing
with 86 additions and 179 deletions
......@@ -5,8 +5,8 @@ module.exports = {
builder: "webpack5",
},
stories: [
"../(frontend|enterprise)/**/*.stories.mdx",
"../(frontend|enterprise)/**/*.stories.@(js|jsx|ts|tsx)",
"../frontend/**/*.stories.mdx",
"../frontend/**/*.stories.@(js|jsx|ts|tsx)",
],
addons: ["@storybook/addon-essentials", "@storybook/addon-links"],
webpackFinal: storybookConfig => ({
......
import React, { memo, useCallback, useMemo, useRef } from "react";
import React, { memo, useCallback, useMemo } from "react";
import { t } from "ttag";
import { set, omit } from "lodash";
import { omit, set } from "lodash";
import { useCurrentRef } from "metabase/hooks/use-current-ref";
import ColorPicker from "metabase/core/components/ColorPicker";
import { getBrandColorOptions } from "./utils";
import { ColorOption } from "./types";
......@@ -24,12 +25,8 @@ const BrandColorSettings = ({
originalColors,
onChange,
}: BrandColorSettingsProps): JSX.Element => {
const colorsRef = useRef(colors);
colorsRef.current = colors;
const options = useMemo(() => {
return getBrandColorOptions();
}, []);
const colorsRef = useCurrentRef(colors);
const options = useMemo(getBrandColorOptions, []);
const handleChange = useCallback(
(colorName: string, color?: string) => {
......@@ -39,7 +36,7 @@ const BrandColorSettings = ({
onChange?.(omit({ ...colorsRef.current }, colorName));
}
},
[onChange],
[colorsRef, onChange],
);
return (
......@@ -112,10 +109,8 @@ const BrandColorRow = memo(function BrandColorRow({
<TableBodyRow>
<TableBodyCell>
<ColorPicker
color={color ?? originalColor}
isBordered
isSelected
isDefault={color == null || color === originalColor}
value={color ?? originalColor}
isAuto={color == null || color === originalColor}
onChange={handleChange}
/>
</TableBodyCell>
......
import React, { memo, useCallback, useMemo, useRef } from "react";
import React, { memo, useCallback, useMemo } from "react";
import { t } from "ttag";
import { flatten, omit, set } from "lodash";
import { useCurrentRef } from "metabase/hooks/use-current-ref";
import ColorPicker from "metabase/core/components/ColorPicker";
import { getChartColorGroups } from "./utils";
import {
......@@ -23,12 +24,8 @@ const ChartColorSettings = ({
originalColors,
onChange,
}: ChartColorSettingsProps): JSX.Element => {
const colorsRef = useRef(colors);
colorsRef.current = colors;
const colorGroups = useMemo(() => {
return getChartColorGroups();
}, []);
const colorsRef = useCurrentRef(colors);
const colorGroups = useMemo(getChartColorGroups, []);
const handleChange = useCallback(
(colorName: string, color?: string) => {
......@@ -38,12 +35,12 @@ const ChartColorSettings = ({
onChange?.(omit({ ...colorsRef.current }, colorName));
}
},
[onChange],
[colorsRef, onChange],
);
const handleReset = useCallback(() => {
onChange?.(omit({ ...colorsRef.current }, flatten(colorGroups)));
}, [colorGroups, onChange]);
}, [colorsRef, colorGroups, onChange]);
return (
<ChartColorTable
......@@ -119,10 +116,8 @@ const ChartColorCell = memo(function ChartColorCell({
return (
<TableBodyCell>
<ColorPicker
color={color ?? originalColor}
isBordered
isSelected
isDefault={color == null || color === originalColor}
value={color ?? originalColor}
isAuto={color == null || color === originalColor}
onChange={handleChange}
/>
</TableBodyCell>
......
import React from "react";
import { ComponentStory } from "@storybook/react";
import { color } from "metabase/lib/colors";
import ColorSettings from "./ColorSettings";
export default {
title: "Whitelabel/ColorSettings",
component: ColorSettings,
};
const Template: ComponentStory<typeof ColorSettings> = args => {
return <ColorSettings {...args} />;
};
export const Default = Template.bind({});
Default.args = {
initialColors: {
brand: color("brand"),
},
originalColors: {
brand: color("brand"),
accent1: color("accent1"),
accent7: color("accent7"),
},
};
......@@ -13,7 +13,6 @@ import SettingRadio from "./widgets/SettingRadio";
import SettingToggle from "./widgets/SettingToggle";
import SettingSelect from "./widgets/SettingSelect";
import SettingText from "./widgets/SettingText";
import SettingColor from "./widgets/SettingColor";
import { settingToFormFieldId } from "./../../settings/utils";
const SETTING_WIDGET_MAP = {
......@@ -24,7 +23,6 @@ const SETTING_WIDGET_MAP = {
radio: SettingRadio,
boolean: SettingToggle,
text: SettingText,
color: SettingColor,
};
const updatePlaceholderForEnvironmentVars = props => {
......
/* eslint-disable react/prop-types */
import React from "react";
import ColorPicker from "metabase/components/ColorPicker";
const SettingColor = ({ setting, onChange }) => (
<ColorPicker value={setting.value || setting.default} onChange={onChange} />
);
export default SettingColor;
......@@ -9,13 +9,13 @@ export default {
};
const Template: ComponentStory<typeof ColorInput> = args => {
const [{ color }, updateArgs] = useArgs();
const [{ value }, updateArgs] = useArgs();
const handleChange = (color?: string) => {
updateArgs({ color });
const handleChange = (value?: string) => {
updateArgs({ value });
};
return <ColorInput {...args} color={color} onChange={handleChange} />;
return <ColorInput {...args} value={value} onChange={handleChange} />;
};
export const Default = Template.bind({});
......@@ -17,16 +17,16 @@ export type ColorInputAttributes = Omit<
>;
export interface ColorInputProps extends ColorInputAttributes {
color?: string;
value?: string;
fullWidth?: boolean;
onChange?: (value?: string) => void;
}
const ColorInput = forwardRef(function ColorInput(
{ color, onFocus, onBlur, onChange, ...props }: ColorInputProps,
{ value, onFocus, onBlur, onChange, ...props }: ColorInputProps,
ref: Ref<HTMLDivElement>,
) {
const colorText = useMemo(() => getColorHex(color) ?? "", [color]);
const colorText = useMemo(() => getColorHex(value) ?? "", [value]);
const [inputText, setInputText] = useState(colorText);
const [isFocused, setIsFocused] = useState(false);
......@@ -69,9 +69,9 @@ const ColorInput = forwardRef(function ColorInput(
);
});
const getColorHex = (color?: string) => {
const getColorHex = (value?: string) => {
try {
return color ? Color(color).hex() : undefined;
return value ? Color(value).hex() : undefined;
} catch (e) {
return undefined;
}
......
......@@ -10,17 +10,17 @@ export default {
};
const Template: ComponentStory<typeof ColorPicker> = args => {
const [{ color }, updateArgs] = useArgs();
const [{ value }, updateArgs] = useArgs();
const handleChange = (color?: string) => {
updateArgs({ color });
const handleChange = (value?: string) => {
updateArgs({ value });
};
return <ColorPicker {...args} color={color} onChange={handleChange} />;
return <ColorPicker {...args} value={value} onChange={handleChange} />;
};
export const Default = Template.bind({});
Default.args = {
color: color("brand"),
value: color("brand"),
placeholder: color("brand"),
};
......@@ -9,24 +9,14 @@ export type ColorPickerAttributes = Omit<
>;
export interface ColorPickerProps extends ColorPickerAttributes {
color: string;
value: string;
placeholder?: string;
isBordered?: boolean;
isSelected?: boolean;
isDefault?: boolean;
isAuto?: boolean;
onChange?: (color?: string) => void;
}
const ColorPicker = forwardRef(function ColorPicker(
{
color,
placeholder,
isBordered,
isSelected,
isDefault,
onChange,
...props
}: ColorPickerProps,
{ value, placeholder, isAuto, onChange, ...props }: ColorPickerProps,
ref: Ref<HTMLDivElement>,
) {
return (
......@@ -36,16 +26,14 @@ const ColorPicker = forwardRef(function ColorPicker(
<ColorPickerTrigger
{...props}
ref={ref}
color={color}
value={value}
placeholder={placeholder}
isBordered={isBordered}
isSelected={isSelected}
isDefault={isDefault}
isAuto={isAuto}
onClick={onClick}
onChange={onChange}
/>
)}
popoverContent={<ColorPickerContent color={color} onChange={onChange} />}
popoverContent={<ColorPickerContent value={value} onChange={onChange} />}
/>
);
});
......
......@@ -5,10 +5,10 @@ import userEvent from "@testing-library/user-event";
import ColorPicker from "./ColorPicker";
const TestColorPicker = () => {
const [color, setColor] = useState("white");
const handleChange = (color?: string) => setColor(color ?? "white");
const [value, setValue] = useState("white");
const handleChange = (value?: string) => setValue(value ?? "white");
return <ColorPicker color={color} onChange={handleChange} />;
return <ColorPicker value={value} onChange={handleChange} />;
};
describe("ColorPicker", () => {
......
......@@ -10,12 +10,12 @@ export type ColorPickerContentAttributes = Omit<
>;
export interface ColorPickerContentProps extends ColorPickerContentAttributes {
color?: string;
onChange?: (color?: string) => void;
value?: string;
onChange?: (value?: string) => void;
}
const ColorPickerContent = forwardRef(function ColorPickerContent(
{ color, onChange, ...props }: ColorPickerContentProps,
{ value, onChange, ...props }: ColorPickerContentProps,
ref: Ref<HTMLDivElement>,
) {
const handleChange = useCallback(
......@@ -25,8 +25,8 @@ const ColorPickerContent = forwardRef(function ColorPickerContent(
return (
<ContentContainer {...props} ref={ref}>
<ColorPickerControls color={color} onChange={handleChange} />
<ColorInput color={color} fullWidth onChange={onChange} />
<ColorPickerControls color={value} onChange={handleChange} />
<ColorInput value={value} fullWidth onChange={onChange} />
</ContentContainer>
);
});
......
......@@ -5,21 +5,17 @@ import { TriggerContainer } from "./ColorPicker.styled";
export interface ColorPickerTriggerProps
extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
color: string;
value: string;
placeholder?: string;
isBordered?: boolean;
isSelected?: boolean;
isDefault?: boolean;
onChange?: (color?: string) => void;
isAuto?: boolean;
onChange?: (value?: string) => void;
}
const ColorPickerTrigger = forwardRef(function ColorPickerTrigger(
{
color,
value,
placeholder,
isBordered,
isSelected,
isDefault,
isAuto,
onClick,
onChange,
...props
......@@ -28,15 +24,9 @@ const ColorPickerTrigger = forwardRef(function ColorPickerTrigger(
) {
return (
<TriggerContainer {...props} ref={ref}>
<ColorPill
color={color}
isBordered={isBordered}
isSelected={isSelected}
isDefault={isDefault}
onClick={onClick}
/>
<ColorPill color={value} isAuto={isAuto} onClick={onClick} />
<ColorInput
color={color}
value={value}
placeholder={placeholder}
fullWidth
onChange={onChange}
......
......@@ -16,3 +16,9 @@ export const Default = Template.bind({});
Default.args = {
color: color("brand"),
};
export const Auto = Template.bind({});
Auto.args = {
color: color("brand"),
isAuto: true,
};
......@@ -2,30 +2,23 @@ import styled from "@emotion/styled";
import { color } from "metabase/lib/colors";
export interface ColorPillRootProps {
isBordered?: boolean;
isSelected?: boolean;
isDefault?: boolean;
isAuto: boolean;
isSelected: boolean;
}
export const ColorPillRoot = styled.div<ColorPillRootProps>`
display: inline-block;
width: 2rem;
height: 2rem;
padding: ${props => props.isBordered && "0.1875rem"};
border-width: ${props => (props.isBordered ? "0.0625rem" : "0")};
padding: 0.1875rem;
border-width: 0.0625rem;
border-color: ${props =>
props.isSelected ? color("text-light") : "transparent"};
border-style: ${props => (props.isDefault ? "dashed" : "solid")};
border-style: ${props => (props.isAuto ? "dashed" : "solid")};
border-radius: 50%;
cursor: pointer;
`;
export interface ColorPillContentProps {
isBordered?: boolean;
}
export const ColorPillContent = styled.div<ColorPillContentProps>`
width: ${props => (props.isBordered ? "1.5rem" : "2rem")};
height: ${props => (props.isBordered ? "1.5rem" : "2rem")};
export const ColorPillContent = styled.div`
width: 1.5rem;
height: 1.5rem;
border-radius: 50%;
`;
......@@ -3,17 +3,15 @@ import { ColorPillContent, ColorPillRoot } from "./ColorPill.styled";
export interface ColorPillProps extends HTMLAttributes<HTMLDivElement> {
color: string;
isBordered?: boolean;
isAuto?: boolean;
isSelected?: boolean;
isDefault?: boolean;
}
const ColorPill = forwardRef(function ColorPill(
{
color,
isBordered,
isSelected,
isDefault,
isAuto = false,
isSelected = true,
"aria-label": ariaLabel = color,
...props
}: ColorPillProps,
......@@ -23,15 +21,11 @@ const ColorPill = forwardRef(function ColorPill(
<ColorPillRoot
{...props}
ref={ref}
isBordered={isBordered}
isAuto={isAuto}
isSelected={isSelected}
isDefault={isDefault}
aria-label={ariaLabel}
>
<ColorPillContent
isBordered={isBordered}
style={{ backgroundColor: color }}
/>
<ColorPillContent style={{ backgroundColor: color }} />
</ColorPillRoot>
);
});
......
......@@ -10,17 +10,17 @@ export default {
};
const Template: ComponentStory<typeof ColorSelector> = args => {
const [{ color }, updateArgs] = useArgs();
const [{ value }, updateArgs] = useArgs();
const handleChange = (color: string) => {
updateArgs({ color });
const handleChange = (value: string) => {
updateArgs({ value });
};
return <ColorSelector {...args} color={color} onChange={handleChange} />;
return <ColorSelector {...args} value={value} onChange={handleChange} />;
};
export const Default = Template.bind({});
Default.args = {
color: color("brand"),
value: color("brand"),
colors: [color("brand"), color("summarize"), color("filter")],
};
......@@ -9,39 +9,23 @@ export type ColorSelectorAttributes = Omit<
>;
export interface ColorSelectorProps extends ColorSelectorAttributes {
color: string;
value: string;
colors: string[];
isBordered?: boolean;
isSelected?: boolean;
onChange?: (color: string) => void;
}
const ColorSelector = forwardRef(function ColorSelector(
{
color,
colors,
isBordered,
isSelected,
onChange,
...props
}: ColorSelectorProps,
{ value, colors, onChange, ...props }: ColorSelectorProps,
ref: Ref<HTMLDivElement>,
) {
return (
<TippyPopoverWithTrigger
renderTrigger={({ onClick }) => (
<ColorPill
{...props}
ref={ref}
color={color}
isBordered={isBordered}
isSelected={isSelected}
onClick={onClick}
/>
<ColorPill {...props} ref={ref} color={value} onClick={onClick} />
)}
popoverContent={
<ColorSelectorContent
color={color}
value={value}
colors={colors}
onChange={onChange}
/>
......
......@@ -9,7 +9,7 @@ describe("ColorSelector", () => {
render(
<ColorSelector
color="white"
value="white"
colors={["blue", "green"]}
onChange={onChange}
/>,
......
......@@ -4,13 +4,13 @@ import { ColorSelectorRoot } from "./ColorSelector.styled";
export interface ColorSelectorContentProps
extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
color?: string;
value?: string;
colors: string[];
onChange?: (color: string) => void;
onChange?: (value: string) => void;
}
const ColorSelectorContent = forwardRef(function ColorSelector(
{ color, colors, onChange, ...props }: ColorSelectorContentProps,
{ value, colors, onChange, ...props }: ColorSelectorContentProps,
ref: Ref<HTMLDivElement>,
) {
return (
......@@ -19,8 +19,7 @@ const ColorSelectorContent = forwardRef(function ColorSelector(
<ColorPill
key={index}
color={option}
isBordered
isSelected={color === option}
isSelected={value === option}
onClick={() => onChange?.(option)}
/>
))}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment