Skip to content

Commit

Permalink
feat(color-picker): add clearable prop and deprecate allowEmpty (#…
Browse files Browse the repository at this point in the history
…8910)

**Related Issue:** #6880

## Summary
Added clearable to contribute to continuity within the codebase - the
logic allows for allowEmpty & clearable to be utilized to allow null
values in the color picker.
  • Loading branch information
brandentheintern authored Mar 19, 2024
1 parent 27b1a76 commit f036ac2
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 46 deletions.
14 changes: 8 additions & 6 deletions packages/calcite-components/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -882,9 +882,6 @@ export namespace Components {
"setFocus": () => Promise<void>;
}
interface CalciteColorPicker {
/**
* When `true`, an empty color (`null`) will be allowed as a `value`. When `false`, a color value is enforced, and clearing the input or blurring will restore the last valid `value`.
*/
"allowEmpty": boolean;
/**
* When `true`, the component will allow updates to the color's alpha value.
Expand All @@ -894,6 +891,10 @@ export namespace Components {
* When `true`, hides the RGB/HSV channel inputs.
*/
"channelsDisabled": boolean;
/**
* When `true`, an empty color (`null`) will be allowed as a `value`. When `false`, a color value is enforced, and clearing the input or blurring will restore the last valid `value`.
*/
"clearable": boolean;
/**
* Internal prop for advanced use-cases.
*/
Expand Down Expand Up @@ -8277,9 +8278,6 @@ declare namespace LocalJSX {
>;
}
interface CalciteColorPicker {
/**
* When `true`, an empty color (`null`) will be allowed as a `value`. When `false`, a color value is enforced, and clearing the input or blurring will restore the last valid `value`.
*/
"allowEmpty"?: boolean;
/**
* When `true`, the component will allow updates to the color's alpha value.
Expand All @@ -8289,6 +8287,10 @@ declare namespace LocalJSX {
* When `true`, hides the RGB/HSV channel inputs.
*/
"channelsDisabled"?: boolean;
/**
* When `true`, an empty color (`null`) will be allowed as a `value`. When `false`, a color value is enforced, and clearing the input or blurring will restore the last valid `value`.
*/
"clearable"?: boolean;
/**
* Internal prop for advanced use-cases.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe("calcite-color-picker", () => {

describe("accessible", () => {
accessible("calcite-color-picker");
accessible("<calcite-color-picker allow-empty value=''></calcite-color-picker>");
accessible("<calcite-color-picker clearable value=''></calcite-color-picker>");
});

describe("honors hidden attribute", () => {
Expand Down Expand Up @@ -84,6 +84,10 @@ describe("calcite-color-picker", () => {
propertyName: "channelsDisabled",
defaultValue: false,
},
{
propertyName: "clearable",
defaultValue: false,
},
{
propertyName: "format",
defaultValue: "auto",
Expand Down Expand Up @@ -1094,7 +1098,7 @@ describe("calcite-color-picker", () => {
describe("when no-color", () => {
it("color gets propagated to hex, RGB & HSV inputs", async () => {
const page = await newE2EPage();
await page.setContent(html`<calcite-color-picker allow-empty value=""></calcite-color-picker>`);
await page.setContent(html`<calcite-color-picker clearable value=""></calcite-color-picker>`);

const hexInput = await page.find(`calcite-color-picker >>> calcite-color-picker-hex-input`);

Expand Down Expand Up @@ -1125,9 +1129,7 @@ describe("calcite-color-picker", () => {

beforeEach(async () => {
page = await newE2EPage();
await page.setContent(
`<calcite-color-picker allow-empty value='${initialValue}'></calcite-color-picker>`,
);
await page.setContent(`<calcite-color-picker clearable value='${initialValue}'></calcite-color-picker>`);
});

it("restores previous color to RGB inputs", async () => {
Expand Down Expand Up @@ -1185,9 +1187,7 @@ describe("calcite-color-picker", () => {

it("changes the value to the specified format after being empty", async () => {
const page = await newE2EPage();
await page.setContent(
html`<calcite-color-picker allow-empty value="" format="rgb"></calcite-color-picker>`,
);
await page.setContent(html`<calcite-color-picker clearable value="" format="rgb"></calcite-color-picker>`);
const color = await page.find("calcite-color-picker");

const hexInput = await page.find(`calcite-color-picker >>> calcite-color-picker-hex-input`);
Expand All @@ -1199,7 +1199,7 @@ describe("calcite-color-picker", () => {
describe("clearing color via supporting inputs", () => {
it("clears color via hex input", async () => {
const page = await newE2EPage();
await page.setContent(html`<calcite-color-picker allow-empty value="#c0ff33"></calcite-color-picker>`);
await page.setContent(html`<calcite-color-picker clearable value="#c0ff33"></calcite-color-picker>`);
const picker = await page.find("calcite-color-picker");
const hexInput = await page.find(`calcite-color-picker >>> calcite-color-picker-hex-input`);

Expand All @@ -1210,7 +1210,7 @@ describe("calcite-color-picker", () => {

it("clears color via RGB channel inputs", async () => {
const page = await newE2EPage();
await page.setContent(html`<calcite-color-picker allow-empty value="#c0ff33"></calcite-color-picker>`);
await page.setContent(html`<calcite-color-picker clearable value="#c0ff33"></calcite-color-picker>`);
const picker = await page.find("calcite-color-picker");

const [rgbModeButton] = await page.findAll(`calcite-color-picker >>> .${CSS.colorMode}`);
Expand All @@ -1229,7 +1229,7 @@ describe("calcite-color-picker", () => {

it("clears color via HSV channel inputs", async () => {
const page = await newE2EPage();
await page.setContent(html`<calcite-color-picker allow-empty value="#c0ff33"></calcite-color-picker>`);
await page.setContent(html`<calcite-color-picker clearable value="#c0ff33"></calcite-color-picker>`);
const picker = await page.find("calcite-color-picker");

const [, hsvModeButton] = await page.findAll(`calcite-color-picker >>> .${CSS.colorMode}`);
Expand Down Expand Up @@ -1647,9 +1647,7 @@ describe("calcite-color-picker", () => {
describe("when no-color", () => {
it("color gets propagated to hex, RGB, HSV & opacity inputs", async () => {
const page = await newE2EPage();
await page.setContent(
html`<calcite-color-picker alpha-channel allow-empty value=""></calcite-color-picker>`,
);
await page.setContent(html`<calcite-color-picker alpha-channel clearable value=""></calcite-color-picker>`);

const hexInput = await page.find(`calcite-color-picker >>> calcite-color-picker-hex-input`);

Expand Down Expand Up @@ -1683,7 +1681,7 @@ describe("calcite-color-picker", () => {
beforeEach(async () => {
page = await newE2EPage();
await page.setContent(
`<calcite-color-picker alpha-channel allow-empty value='${initialValue}'></calcite-color-picker>`,
`<calcite-color-picker alpha-channel clearable value='${initialValue}'></calcite-color-picker>`,
);
});

Expand Down Expand Up @@ -1776,7 +1774,7 @@ describe("calcite-color-picker", () => {
it("changes the value to the specified format after being empty", async () => {
const page = await newE2EPage();
await page.setContent(
"<calcite-color-picker alpha-channel allow-empty value='' format='rgba'></calcite-color-picker>",
"<calcite-color-picker alpha-channel clearable value='' format='rgba'></calcite-color-picker>",
);
const color = await page.find("calcite-color-picker");

Expand All @@ -1790,7 +1788,7 @@ describe("calcite-color-picker", () => {
it("clears color via hex input", async () => {
const page = await newE2EPage();
await page.setContent(
"<calcite-color-picker alpha-channel allow-empty value='#c0ff3333'></calcite-color-picker>",
"<calcite-color-picker alpha-channel clearable value='#c0ff3333'></calcite-color-picker>",
);
const picker = await page.find("calcite-color-picker");

Expand All @@ -1803,7 +1801,7 @@ describe("calcite-color-picker", () => {
it("clears color via RGB channel inputs", async () => {
const page = await newE2EPage();
await page.setContent(
"<calcite-color-picker alpha-channel allow-empty value='#c0ff3333'></calcite-color-picker>",
"<calcite-color-picker alpha-channel clearable value='#c0ff3333'></calcite-color-picker>",
);
const picker = await page.find("calcite-color-picker");

Expand All @@ -1827,7 +1825,7 @@ describe("calcite-color-picker", () => {
it("clears color via HSV channel inputs", async () => {
const page = await newE2EPage();
await page.setContent(
"<calcite-color-picker alpha-channel allow-empty value='#c0ff3333'></calcite-color-picker>",
"<calcite-color-picker alpha-channel clearable value='#c0ff3333'></calcite-color-picker>",
);
const picker = await page.find("calcite-color-picker");

Expand Down Expand Up @@ -1963,7 +1961,7 @@ describe("calcite-color-picker", () => {

it("does not allow saving/removing when no-color is set", async () => {
const page = await newE2EPage();
await page.setContent(`<calcite-color-picker allow-empty value=""></calcite-color-picker>`);
await page.setContent(`<calcite-color-picker clearable value=""></calcite-color-picker>`);

const saveColor = await page.find(`calcite-color-picker >>> .${CSS.saveColor}`);
const removeColor = await page.find(`calcite-color-picker >>> .${CSS.deleteColor}`);
Expand Down Expand Up @@ -2063,7 +2061,7 @@ describe("calcite-color-picker", () => {

it("does not allow saving/removing when no-color is set", async () => {
const page = await newE2EPage();
await page.setContent(`<calcite-color-picker alpha-channel allow-empty value=""></calcite-color-picker>`);
await page.setContent(`<calcite-color-picker alpha-channel clearable value=""></calcite-color-picker>`);

const saveColor = await page.find(`calcite-color-picker >>> .${CSS.saveColor}`);
const removeColor = await page.find(`calcite-color-picker >>> .${CSS.deleteColor}`);
Expand All @@ -2076,7 +2074,7 @@ describe("calcite-color-picker", () => {

it("allows setting no-color", async () => {
const page = await newE2EPage();
await page.setContent(`<calcite-color-picker allow-empty></calcite-color-picker>`);
await page.setContent(`<calcite-color-picker clearable></calcite-color-picker>`);

const color = await page.find("calcite-color-picker");

Expand Down Expand Up @@ -2152,7 +2150,7 @@ describe("calcite-color-picker", () => {
describe("keyboard", () => {
it("allows editing color field via keyboard", async () => {
const page = await newE2EPage();
await page.setContent(`<calcite-color-picker allow-empty value=""></calcite-color-picker>`);
await page.setContent(`<calcite-color-picker clearable value=""></calcite-color-picker>`);

const picker = await page.find("calcite-color-picker");
const scope = await page.find(`calcite-color-picker >>> .${CSS.colorFieldScope}`);
Expand Down Expand Up @@ -2232,7 +2230,7 @@ describe("calcite-color-picker", () => {

it("allows editing hue slider via keyboard", async () => {
const page = await newE2EPage();
await page.setContent(`<calcite-color-picker allow-empty value=""></calcite-color-picker>`);
await page.setContent(`<calcite-color-picker clearable value=""></calcite-color-picker>`);

const picker = await page.find("calcite-color-picker");
const hueScope = await page.find(`calcite-color-picker >>> .${CSS.hueScope}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ const createColorAttributes: (options?: { exceptions: string[] }) => Attributes
export const simple = (): string =>
create("calcite-color-picker", [
{
name: "allow-empty",
value: boolean("allow-empty", false),
name: "clearable",
value: boolean("clearable", false),
},
...createColorAttributes(),
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,17 @@ export class ColorPicker
* When `true`, an empty color (`null`) will be allowed as a `value`.
*
* When `false`, a color value is enforced, and clearing the input or blurring will restore the last valid `value`.
*
* @deprecated Use `clearable` instead
*/
@Prop({ reflect: true }) allowEmpty = false;

@Watch("allowEmpty")
@Watch("clearable")
handleAllowEmptyOrClearableChange(): void {
this.isClearable = this.clearable || this.allowEmpty;
}

/**
* When `true`, the component will allow updates to the color's alpha value.
*/
Expand All @@ -121,6 +129,13 @@ export class ColorPicker
/** When `true`, hides the RGB/HSV channel inputs. */
@Prop() channelsDisabled = false;

/**
* When `true`, an empty color (`null`) will be allowed as a `value`.
*
* When `false`, a color value is enforced, and clearing the input or blurring will restore the last valid `value`.
*/
@Prop({ reflect: true }) clearable = false;

/**
* Internal prop for advanced use-cases.
*
Expand Down Expand Up @@ -225,8 +240,8 @@ export class ColorPicker

@Watch("value")
handleValueChange(value: ColorValue | null, oldValue: ColorValue | null): void {
const { allowEmpty, format } = this;
const checkMode = !allowEmpty || value;
const { isClearable, format } = this;
const checkMode = !isClearable || value;
let modeChanged = false;

if (checkMode) {
Expand Down Expand Up @@ -258,7 +273,7 @@ export class ColorPicker
}

const color =
allowEmpty && !value
isClearable && !value
? null
: Color(
value != null && typeof value === "object" && alphaCompatible(this.mode)
Expand Down Expand Up @@ -305,6 +320,8 @@ export class ColorPicker

private internalColorUpdateContext: "internal" | "initial" | "user-interaction" | null = null;

private isClearable: boolean;

private mode: SupportedMode = CSSColorMode.HEX;

private opacityScopeNode: HTMLDivElement;
Expand Down Expand Up @@ -417,11 +434,11 @@ export class ColorPicker

private handleHexInputChange = (event: Event): void => {
event.stopPropagation();
const { allowEmpty, color } = this;
const { isClearable, color } = this;
const input = event.target as HTMLCalciteColorPickerHexInputElement;
const hex = input.value;

if (allowEmpty && !hex) {
if (isClearable && !hex) {
this.internalColorSet(null);
return;
}
Expand Down Expand Up @@ -451,7 +468,7 @@ export class ColorPicker

let inputValue: string;

if (this.allowEmpty && !input.value) {
if (this.isClearable && !input.value) {
inputValue = "";
} else {
const value = Number(input.value);
Expand Down Expand Up @@ -508,7 +525,7 @@ export class ColorPicker
const channelIndex = Number(input.getAttribute("data-channel-index"));
const channels = [...this.channels] as this["channels"];

const shouldClearChannels = this.allowEmpty && !input.value;
const shouldClearChannels = this.isClearable && !input.value;

if (shouldClearChannels) {
this.channels = [null, null, null, null];
Expand Down Expand Up @@ -666,9 +683,10 @@ export class ColorPicker
async componentWillLoad(): Promise<void> {
setUpLoadableComponent(this);

const { allowEmpty, color, format, value } = this;
this.handleAllowEmptyOrClearableChange();

const willSetNoColor = allowEmpty && !value;
const { isClearable, color, format, value } = this;
const willSetNoColor = isClearable && !value;
const parsedMode = parseMode(value);
const valueIsCompatible =
willSetNoColor || (format === "auto" && parsedMode) || format === parsedMode;
Expand All @@ -677,7 +695,6 @@ export class ColorPicker
if (!valueIsCompatible) {
this.showIncompatibleColorWarning(value, format);
}

this.setMode(format, false);
this.internalColorSet(initialColor, false, "initial");

Expand Down Expand Up @@ -722,7 +739,6 @@ export class ColorPicker

render(): VNode {
const {
allowEmpty,
channelsDisabled,
color,
colorFieldScopeLeft,
Expand Down Expand Up @@ -862,7 +878,7 @@ export class ColorPicker
{noHex ? null : (
<div class={CSS.hexOptions}>
<calcite-color-picker-hex-input
allowEmpty={allowEmpty}
allowEmpty={this.isClearable}
alphaChannel={alphaChannel}
class={CSS.control}
messages={messages}
Expand Down Expand Up @@ -972,7 +988,7 @@ export class ColorPicker
};

private renderChannelsTab = (channelMode: this["channelMode"]): VNode => {
const { allowEmpty, channelMode: activeChannelMode, channels, messages, alphaChannel } = this;
const { isClearable, channelMode: activeChannelMode, channels, messages, alphaChannel } = this;
const selected = channelMode === activeChannelMode;
const isRgb = channelMode === "rgb";
const channelAriaLabels = isRgb
Expand All @@ -990,7 +1006,7 @@ export class ColorPicker

if (isAlphaChannel) {
channelValue =
allowEmpty && !channelValue ? channelValue : alphaToOpacity(channelValue);
isClearable && !channelValue ? channelValue : alphaToOpacity(channelValue);
}

/* the channel container is ltr, so we apply the host's direction */
Expand Down

0 comments on commit f036ac2

Please sign in to comment.