feat: color picker redesign (#6216)

Co-authored-by: Maielo <maielo.mv@gmail.com>
Co-authored-by: dwelle <luzar.david@gmail.com>
Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
This commit is contained in:
Barnabás Molnár 2023-05-18 16:06:27 +02:00 committed by GitHub
parent 6977c32631
commit 5b7596582f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 4010 additions and 2699 deletions

View file

@ -29,6 +29,7 @@ describe("Test MobileMenu", () => {
expect(h.app.device).toMatchInlineSnapshot(`
Object {
"canDeviceFitSidebar": false,
"isLandscape": true,
"isMobile": true,
"isSmScreen": false,
"isTouchScreen": false,

File diff suppressed because it is too large Load diff

View file

@ -35,7 +35,7 @@ Object {
"seed": 337897,
"startArrowhead": null,
"startBinding": null,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "arrow",
@ -68,7 +68,7 @@ Object {
"type": 2,
},
"seed": 337897,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "diamond",
@ -101,7 +101,7 @@ Object {
"type": 2,
},
"seed": 337897,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "ellipse",
@ -147,7 +147,7 @@ Object {
"seed": 337897,
"startArrowhead": null,
"startBinding": null,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "line",
@ -180,7 +180,7 @@ Object {
"type": 3,
},
"seed": 337897,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",

View file

@ -5,7 +5,7 @@ exports[`Test Linear Elements Test bound text element should match styles for te
class="excalidraw-wysiwyg"
data-type="wysiwyg"
dir="auto"
style="position: absolute; display: inline-block; min-height: 1em; margin: 0px; padding: 0px; border: 0px; outline: 0; resize: none; background: transparent; overflow: hidden; z-index: var(--zIndex-wysiwyg); word-break: break-word; white-space: pre-wrap; overflow-wrap: break-word; box-sizing: content-box; width: 10.5px; height: 25px; left: 35px; top: 7.5px; transform: translate(0px, 0px) scale(1) rotate(0deg); text-align: center; vertical-align: middle; color: rgb(0, 0, 0); opacity: 1; filter: var(--theme-filter); max-height: -7.5px; font: Emoji 20px 20px; line-height: 1.25; font-family: Virgil, Segoe UI Emoji;"
style="position: absolute; display: inline-block; min-height: 1em; margin: 0px; padding: 0px; border: 0px; outline: 0; resize: none; background: transparent; overflow: hidden; z-index: var(--zIndex-wysiwyg); word-break: break-word; white-space: pre-wrap; overflow-wrap: break-word; box-sizing: content-box; width: 10.5px; height: 25px; left: 35px; top: 7.5px; transform: translate(0px, 0px) scale(1) rotate(0deg); text-align: center; vertical-align: middle; color: rgb(30, 30, 30); opacity: 1; filter: var(--theme-filter); max-height: -7.5px; font: Emoji 20px 20px; line-height: 1.25; font-family: Virgil, Segoe UI Emoji;"
tabindex="0"
wrap="off"
/>

View file

@ -18,7 +18,7 @@ Object {
"type": 3,
},
"seed": 401146281,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
@ -49,7 +49,7 @@ Object {
"type": 3,
},
"seed": 337897,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
@ -80,7 +80,7 @@ Object {
"type": 3,
},
"seed": 337897,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
@ -116,7 +116,7 @@ Object {
"type": 3,
},
"seed": 337897,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
@ -152,7 +152,7 @@ Object {
"type": 3,
},
"seed": 449462985,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
@ -206,7 +206,7 @@ Object {
"focus": -0.6000000000000001,
"gap": 10,
},
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "line",

View file

@ -40,7 +40,7 @@ Object {
"seed": 337897,
"startArrowhead": null,
"startBinding": null,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "arrow",
@ -93,7 +93,7 @@ Object {
"seed": 337897,
"startArrowhead": null,
"startBinding": null,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "line",

File diff suppressed because it is too large Load diff

View file

@ -33,7 +33,7 @@ Object {
"seed": 337897,
"startArrowhead": null,
"startBinding": null,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "arrow",
@ -79,7 +79,7 @@ Object {
"seed": 337897,
"startArrowhead": null,
"startBinding": null,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "line",
@ -110,7 +110,7 @@ Object {
"type": 2,
},
"seed": 337897,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "diamond",
@ -141,7 +141,7 @@ Object {
"type": 2,
},
"seed": 337897,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "ellipse",
@ -172,7 +172,7 @@ Object {
"type": 3,
},
"seed": 337897,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",

View file

@ -9,6 +9,7 @@ import {
queryByText,
queryAllByText,
waitFor,
togglePopover,
} from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import * as Renderer from "../renderer/renderScene";
@ -19,7 +20,6 @@ import { ShortcutName } from "../actions/shortcuts";
import { copiedStyles } from "../actions/actionStyles";
import { API } from "./helpers/api";
import { setDateTimeForTests } from "../utils";
import { t } from "../i18n";
import { LibraryItem } from "../types";
const checkpoint = (name: string) => {
@ -303,10 +303,10 @@ describe("contextMenu element", () => {
mouse.up(20, 20);
// Change some styles of second rectangle
UI.clickLabeledElement("Stroke");
UI.clickLabeledElement(t("colors.c92a2a"));
UI.clickLabeledElement("Background");
UI.clickLabeledElement(t("colors.e64980"));
togglePopover("Stroke");
UI.clickOnTestId("color-red");
togglePopover("Background");
UI.clickOnTestId("color-blue");
// Fill style
fireEvent.click(screen.getByTitle("Cross-hatch"));
// Stroke width
@ -320,13 +320,20 @@ describe("contextMenu element", () => {
target: { value: "60" },
});
// closing the background popover as this blocks
// context menu from rendering after we started focussing
// the popover once rendered :/
togglePopover("Background");
mouse.reset();
// Copy styles of second rectangle
fireEvent.contextMenu(GlobalTestState.canvas, {
button: 2,
clientX: 40,
clientY: 40,
});
let contextMenu = UI.queryContextMenu();
fireEvent.click(queryByText(contextMenu!, "Copy styles")!);
const secondRect = JSON.parse(copiedStyles)[0];
@ -344,8 +351,8 @@ describe("contextMenu element", () => {
const firstRect = API.getSelectedElement();
expect(firstRect.id).toBe(h.elements[0].id);
expect(firstRect.strokeColor).toBe("#c92a2a");
expect(firstRect.backgroundColor).toBe("#e64980");
expect(firstRect.strokeColor).toBe("#e03131");
expect(firstRect.backgroundColor).toBe("#a5d8ff");
expect(firstRect.fillStyle).toBe("cross-hatch");
expect(firstRect.strokeWidth).toBe(2); // Bold: 2
expect(firstRect.strokeStyle).toBe("dotted");

View file

@ -33,7 +33,7 @@ Object {
"seed": Any<Number>,
"startArrowhead": null,
"startBinding": null,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "arrow",
@ -173,7 +173,7 @@ Object {
},
"seed": Any<Number>,
"simulatePressure": true,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "freedraw",
@ -219,7 +219,7 @@ Object {
"seed": Any<Number>,
"startArrowhead": null,
"startBinding": null,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "line",
@ -265,7 +265,7 @@ Object {
"seed": Any<Number>,
"startArrowhead": null,
"startBinding": null,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "line",
@ -302,7 +302,7 @@ Object {
"type": 3,
},
"seed": Any<Number>,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"text": "text",
@ -342,7 +342,7 @@ Object {
"type": 3,
},
"seed": Any<Number>,
"strokeColor": "#000000",
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 1,
"text": "",

View file

@ -237,6 +237,15 @@ export class UI {
fireEvent.click(element);
};
static clickOnTestId = (testId: string) => {
const element = document.querySelector(`[data-testid='${testId}']`);
// const element = GlobalTestState.renderResult.queryByTestId(testId);
if (!element) {
throw new Error(`No element with testid "${testId}" found`);
}
fireEvent.click(element);
};
/**
* Creates an Excalidraw element, and returns a proxy that wraps it so that
* accessing props will return the latest ones from the object existing in

View file

@ -7,7 +7,7 @@ exports[`<Excalidraw/> <MainMenu/> should render main menu with host menu items
>
<div
class="Island dropdown-menu-container"
style="--padding: 2; z-index: 1;"
style="--padding: 2; z-index: 2;"
>
<button
class="dropdown-menu-item dropdown-menu-item-base"
@ -115,7 +115,7 @@ exports[`<Excalidraw/> Test UIOptions prop Test canvasActions should render menu
>
<div
class="Island dropdown-menu-container"
style="--padding: 2; z-index: 1;"
style="--padding: 2; z-index: 2;"
>
<button
aria-label="Open"
@ -523,38 +523,84 @@ exports[`<Excalidraw/> Test UIOptions prop Test canvasActions should render menu
<div
style="padding: 0px 0.625rem;"
>
<div
style="position: relative;"
>
<div>
<div>
<div
aria-modal="true"
class="color-picker-container"
role="dialog"
>
<div
class="color-picker-control-container"
class="color-picker__top-picks"
>
<div
class="color-picker-label-swatch-container"
>
<button
aria-label="Canvas background"
class="color-picker-label-swatch"
style="--swatch-color: #ffffff;"
/>
</div>
<label
class="color-input-container"
<button
class="color-picker__button active"
style="--swatch-color: #ffffff;"
title="#ffffff"
type="button"
>
<div
class="color-picker-hash"
>
#
</div>
<input
aria-label="Canvas background"
class="color-picker-input"
spellcheck="false"
value="ffffff"
class="color-picker__button-outline"
/>
</label>
</button>
<button
class="color-picker__button"
style="--swatch-color: #f8f9fa;"
title="#f8f9fa"
type="button"
>
<div
class="color-picker__button-outline"
/>
</button>
<button
class="color-picker__button"
style="--swatch-color: #f5faff;"
title="#f5faff"
type="button"
>
<div
class="color-picker__button-outline"
/>
</button>
<button
class="color-picker__button"
style="--swatch-color: #fffce8;"
title="#fffce8"
type="button"
>
<div
class="color-picker__button-outline"
/>
</button>
<button
class="color-picker__button"
style="--swatch-color: #fdf8f6;"
title="#fdf8f6"
type="button"
>
<div
class="color-picker__button-outline"
/>
</button>
</div>
<div
style="width: 1px; height: 100%; margin: 0px auto;"
/>
<button
aria-controls="radix-:r0:"
aria-expanded="false"
aria-haspopup="dialog"
aria-label="Canvas background"
class="color-picker__button active-color"
data-state="closed"
style="--swatch-color: #ffffff;"
title="Show background color picker"
type="button"
>
<div
class="color-picker__button-outline"
/>
</button>
</div>
</div>
</div>

View file

@ -20,7 +20,7 @@ Object {
"currentItemRoughness": 1,
"currentItemRoundness": "round",
"currentItemStartArrowhead": null,
"currentItemStrokeColor": "#000000",
"currentItemStrokeColor": "#1e1e1e",
"currentItemStrokeStyle": "solid",
"currentItemStrokeWidth": 1,
"currentItemTextAlign": "left",

View file

@ -12,11 +12,11 @@ import {
fireEvent,
render,
screen,
togglePopover,
waitFor,
} from "./test-utils";
import { defaultLang } from "../i18n";
import { FONT_FAMILY } from "../constants";
import { t } from "../i18n";
const { h } = window;
@ -42,7 +42,6 @@ const checkpoint = (name: string) => {
expect(element).toMatchSnapshot(`[${name}] element ${i}`),
);
};
beforeEach(async () => {
// Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
@ -159,13 +158,14 @@ describe("regression tests", () => {
UI.clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
togglePopover("Background");
UI.clickOnTestId("color-yellow");
UI.clickOnTestId("color-red");
UI.clickLabeledElement("Background");
UI.clickLabeledElement(t("colors.fa5252"));
UI.clickLabeledElement("Stroke");
UI.clickLabeledElement(t("colors.5f3dc4"));
expect(API.getSelectedElement().backgroundColor).toBe("#fa5252");
expect(API.getSelectedElement().strokeColor).toBe("#5f3dc4");
togglePopover("Stroke");
UI.clickOnTestId("color-blue");
expect(API.getSelectedElement().backgroundColor).toBe("#ffc9c9");
expect(API.getSelectedElement().strokeColor).toBe("#1971c2");
});
it("click on an element and drag it", () => {
@ -988,8 +988,8 @@ describe("regression tests", () => {
UI.clickTool("rectangle");
// change background color since default is transparent
// and transparent elements can't be selected by clicking inside of them
UI.clickLabeledElement("Background");
UI.clickLabeledElement(t("colors.fa5252"));
togglePopover("Background");
UI.clickOnTestId("color-red");
mouse.down();
mouse.up(1000, 1000);
@ -1088,15 +1088,14 @@ describe("regression tests", () => {
assertSelectedElements(rect3);
});
it("should show fill icons when element has non transparent background", () => {
it("should show fill icons when element has non transparent background", async () => {
UI.clickTool("rectangle");
expect(screen.queryByText(/fill/i)).not.toBeNull();
mouse.down();
mouse.up(10, 10);
expect(screen.queryByText(/fill/i)).toBeNull();
UI.clickLabeledElement("Background");
UI.clickLabeledElement(t("colors.fa5252"));
togglePopover("Background");
UI.clickOnTestId("color-red");
// select rectangle
mouse.reset();
mouse.click();

View file

@ -16,6 +16,7 @@ import { STORAGE_KEYS } from "../excalidraw-app/app_constants";
import { SceneData } from "../types";
import { getSelectedElements } from "../scene/selection";
import { ExcalidrawElement } from "../element/types";
import { UI } from "./helpers/ui";
const customQueries = {
...queries,
@ -186,11 +187,6 @@ export const assertSelectedElements = (
expect(selectedElementIds).toEqual(expect.arrayContaining(ids));
};
export const toggleMenu = (container: HTMLElement) => {
// open menu
fireEvent.click(container.querySelector(".dropdown-menu-button")!);
};
export const createPasteEvent = (
text:
| string
@ -211,3 +207,24 @@ export const createPasteEvent = (
},
);
};
export const toggleMenu = (container: HTMLElement) => {
// open menu
fireEvent.click(container.querySelector(".dropdown-menu-button")!);
};
export const togglePopover = (label: string) => {
// Needed for radix-ui/react-popover as tests fail due to resize observer not being present
(global as any).ResizeObserver = class ResizeObserver {
constructor(cb: any) {
(this as any).cb = cb;
}
observe() {}
unobserve() {}
disconnect() {}
};
UI.clickLabeledElement(label);
};