feat: add props zenModeEnabled and gridModeEnabled so host can control completely (#2901)
* feat: add props zenModeEnabled and gridModeEnabled so host can control completely * dnt show exit zenmode button when prop present * fix * update when props change * Add tests * Add tests * update changelog and readme * update * Update src/tests/excalidrawPackage.test.tsx * Update src/packages/excalidraw/README.md Co-authored-by: Lipis <lipiridis@gmail.com> * Update src/packages/excalidraw/README.md Co-authored-by: David Luzar <luzar.david@gmail.com> * Apply suggestions from code review Co-authored-by: David Luzar <luzar.david@gmail.com> * fix specs Co-authored-by: Lipis <lipiridis@gmail.com> Co-authored-by: David Luzar <luzar.david@gmail.com>
This commit is contained in:
parent
d6ca981f7a
commit
066560311b
@ -48,6 +48,7 @@ import {
|
|||||||
ELEMENT_TRANSLATE_AMOUNT,
|
ELEMENT_TRANSLATE_AMOUNT,
|
||||||
ENV,
|
ENV,
|
||||||
EVENT,
|
EVENT,
|
||||||
|
GRID_SIZE,
|
||||||
LINE_CONFIRM_THRESHOLD,
|
LINE_CONFIRM_THRESHOLD,
|
||||||
MIME_TYPES,
|
MIME_TYPES,
|
||||||
POINTER_BUTTON,
|
POINTER_BUTTON,
|
||||||
@ -299,6 +300,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
offsetTop,
|
offsetTop,
|
||||||
excalidrawRef,
|
excalidrawRef,
|
||||||
viewModeEnabled = false,
|
viewModeEnabled = false,
|
||||||
|
zenModeEnabled = false,
|
||||||
|
gridModeEnabled = false,
|
||||||
} = props;
|
} = props;
|
||||||
this.state = {
|
this.state = {
|
||||||
...defaultAppState,
|
...defaultAppState,
|
||||||
@ -307,6 +310,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
height,
|
height,
|
||||||
...this.getCanvasOffsets({ offsetLeft, offsetTop }),
|
...this.getCanvasOffsets({ offsetLeft, offsetTop }),
|
||||||
viewModeEnabled,
|
viewModeEnabled,
|
||||||
|
zenModeEnabled,
|
||||||
|
gridSize: gridModeEnabled ? GRID_SIZE : null,
|
||||||
};
|
};
|
||||||
if (excalidrawRef) {
|
if (excalidrawRef) {
|
||||||
const readyPromise =
|
const readyPromise =
|
||||||
@ -453,6 +458,9 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
onExportToBackend={onExportToBackend}
|
onExportToBackend={onExportToBackend}
|
||||||
renderCustomFooter={renderFooter}
|
renderCustomFooter={renderFooter}
|
||||||
viewModeEnabled={viewModeEnabled}
|
viewModeEnabled={viewModeEnabled}
|
||||||
|
showExitZenModeBtn={
|
||||||
|
typeof this.props?.zenModeEnabled === "undefined" && zenModeEnabled
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<div className="excalidraw-textEditorContainer" />
|
<div className="excalidraw-textEditorContainer" />
|
||||||
{this.state.showStats && (
|
{this.state.showStats && (
|
||||||
@ -511,11 +519,21 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let viewModeEnabled = actionResult?.appState?.viewModeEnabled || false;
|
let viewModeEnabled = actionResult?.appState?.viewModeEnabled || false;
|
||||||
|
let zenModeEnabled = actionResult?.appState?.zenModeEnabled || false;
|
||||||
|
let gridSize = actionResult?.appState?.gridSize || null;
|
||||||
|
|
||||||
if (typeof this.props.viewModeEnabled !== "undefined") {
|
if (typeof this.props.viewModeEnabled !== "undefined") {
|
||||||
viewModeEnabled = this.props.viewModeEnabled;
|
viewModeEnabled = this.props.viewModeEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof this.props.zenModeEnabled !== "undefined") {
|
||||||
|
zenModeEnabled = this.props.zenModeEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof this.props.gridModeEnabled !== "undefined") {
|
||||||
|
gridSize = this.props.gridModeEnabled ? GRID_SIZE : null;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState(
|
this.setState(
|
||||||
(state) => ({
|
(state) => ({
|
||||||
...actionResult.appState,
|
...actionResult.appState,
|
||||||
@ -526,6 +544,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
offsetTop: state.offsetTop,
|
offsetTop: state.offsetTop,
|
||||||
offsetLeft: state.offsetLeft,
|
offsetLeft: state.offsetLeft,
|
||||||
viewModeEnabled,
|
viewModeEnabled,
|
||||||
|
zenModeEnabled,
|
||||||
|
gridSize,
|
||||||
}),
|
}),
|
||||||
() => {
|
() => {
|
||||||
if (actionResult.syncHistory) {
|
if (actionResult.syncHistory) {
|
||||||
@ -845,6 +865,15 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
this.addEventListeners();
|
this.addEventListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prevProps.zenModeEnabled !== this.props.zenModeEnabled) {
|
||||||
|
this.setState({ zenModeEnabled: !!this.props.zenModeEnabled });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevProps.gridModeEnabled !== this.props.gridModeEnabled) {
|
||||||
|
this.setState({
|
||||||
|
gridSize: this.props.gridModeEnabled ? GRID_SIZE : null,
|
||||||
|
});
|
||||||
|
}
|
||||||
document
|
document
|
||||||
.querySelector(".excalidraw")
|
.querySelector(".excalidraw")
|
||||||
?.classList.toggle("Appearance_dark", this.state.appearance === "dark");
|
?.classList.toggle("Appearance_dark", this.state.appearance === "dark");
|
||||||
@ -3717,7 +3746,9 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
separator,
|
separator,
|
||||||
actionSelectAll,
|
actionSelectAll,
|
||||||
separator,
|
separator,
|
||||||
|
typeof this.props.gridModeEnabled === "undefined" &&
|
||||||
actionToggleGridMode,
|
actionToggleGridMode,
|
||||||
|
typeof this.props.zenModeEnabled === "undefined" &&
|
||||||
actionToggleZenMode,
|
actionToggleZenMode,
|
||||||
typeof this.props.viewModeEnabled === "undefined" &&
|
typeof this.props.viewModeEnabled === "undefined" &&
|
||||||
actionToggleViewMode,
|
actionToggleViewMode,
|
||||||
|
@ -52,6 +52,7 @@ interface LayerUIProps {
|
|||||||
onLockToggle: () => void;
|
onLockToggle: () => void;
|
||||||
onInsertElements: (elements: readonly NonDeletedExcalidrawElement[]) => void;
|
onInsertElements: (elements: readonly NonDeletedExcalidrawElement[]) => void;
|
||||||
zenModeEnabled: boolean;
|
zenModeEnabled: boolean;
|
||||||
|
showExitZenModeBtn: boolean;
|
||||||
toggleZenMode: () => void;
|
toggleZenMode: () => void;
|
||||||
langCode: Language["code"];
|
langCode: Language["code"];
|
||||||
isCollaborating: boolean;
|
isCollaborating: boolean;
|
||||||
@ -296,6 +297,7 @@ const LayerUI = ({
|
|||||||
onLockToggle,
|
onLockToggle,
|
||||||
onInsertElements,
|
onInsertElements,
|
||||||
zenModeEnabled,
|
zenModeEnabled,
|
||||||
|
showExitZenModeBtn,
|
||||||
toggleZenMode,
|
toggleZenMode,
|
||||||
isCollaborating,
|
isCollaborating,
|
||||||
onExportToBackend,
|
onExportToBackend,
|
||||||
@ -579,7 +581,7 @@ const LayerUI = ({
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
className={clsx("disable-zen-mode", {
|
className={clsx("disable-zen-mode", {
|
||||||
"disable-zen-mode--visible": zenModeEnabled,
|
"disable-zen-mode--visible": showExitZenModeBtn,
|
||||||
})}
|
})}
|
||||||
onClick={toggleZenMode}
|
onClick={toggleZenMode}
|
||||||
>
|
>
|
||||||
|
@ -18,6 +18,7 @@ Please add the latest change on the top under the correct section.
|
|||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
|
- Add `zenModeEnabled` and `gridModeEnabled` prop which enables zen mode and grid mode respectively [#2901](https://github.com/excalidraw/excalidraw/pull/2901). When this prop is used, the zen mode / grid mode will be fully controlled by the host app.
|
||||||
- Add `viewModeEnabled` prop which enabled the view mode [#2840](https://github.com/excalidraw/excalidraw/pull/2840). When this prop is used, the view mode will not show up in context menu is so it is fully controlled by host.
|
- Add `viewModeEnabled` prop which enabled the view mode [#2840](https://github.com/excalidraw/excalidraw/pull/2840). When this prop is used, the view mode will not show up in context menu is so it is fully controlled by host.
|
||||||
- Expose `getAppState` on `excalidrawRef` [#2834](https://github.com/excalidraw/excalidraw/pull/2834).
|
- Expose `getAppState` on `excalidrawRef` [#2834](https://github.com/excalidraw/excalidraw/pull/2834).
|
||||||
|
|
||||||
|
@ -138,7 +138,9 @@ export default function App() {
|
|||||||
| [`onExportToBackend`](#onExportToBackend) | Function | | Callback triggered when link button is clicked on export dialog |
|
| [`onExportToBackend`](#onExportToBackend) | Function | | Callback triggered when link button is clicked on export dialog |
|
||||||
| [`langCode`](#langCode) | string | `en` | Language code string |
|
| [`langCode`](#langCode) | string | `en` | Language code string |
|
||||||
| [`renderFooter `](#renderFooter) | Function | | Function that renders custom UI footer |
|
| [`renderFooter `](#renderFooter) | Function | | Function that renders custom UI footer |
|
||||||
| [`viewModeEnabled`](#viewModeEnabled) | boolean | false | This implies if the app is in view mode. |
|
| [`viewModeEnabled`](#viewModeEnabled) | boolean | | This implies if the app is in view mode. |
|
||||||
|
| [`zenModeEnabled`](#zenModeEnabled) | boolean | | This implies if the zen mode is enabled |
|
||||||
|
| [`gridModeEnabled`](#gridModeEnabled) | boolean | | This implies if the grid mode is enabled |
|
||||||
|
|
||||||
### `Extra API's`
|
### `Extra API's`
|
||||||
|
|
||||||
@ -334,4 +336,12 @@ A function that renders (returns JSX) custom UI footer. For example, you can use
|
|||||||
|
|
||||||
#### `viewModeEnabled`
|
#### `viewModeEnabled`
|
||||||
|
|
||||||
This prop indicates if the app is in `view mode`. When this prop is used, the `view mode` will not show up in context menu is so it is fully controlled by host. Also the value of this prop if passed will be used over the value of `intialData.appState.viewModeEnabled`
|
This prop indicates whether the app is in `view mode`. When supplied, the value takes precedence over `intialData.appState.viewModeEnabled`, the `view mode` will be fully controlled by the host app, and users won't be able to toggle it from within the app.
|
||||||
|
|
||||||
|
#### `zenModeEnabled`
|
||||||
|
|
||||||
|
This prop indicates whether the app is in `zen mode`. When supplied, the value takes precedence over `intialData.appState.zenModeEnabled`, the `zen mode` will be fully controlled by the host app, and users won't be able to toggle it from within the app.
|
||||||
|
|
||||||
|
#### `gridModeEnabled`
|
||||||
|
|
||||||
|
This prop indicates whether the shows the grid. When supplied, the value takes precedence over `intialData.appState.gridModeEnabled`, the grid will be fully controlled by the host app, and users won't be able to toggle it from within the app.
|
||||||
|
@ -27,6 +27,8 @@ const Excalidraw = (props: ExcalidrawProps) => {
|
|||||||
renderFooter,
|
renderFooter,
|
||||||
langCode = defaultLang.code,
|
langCode = defaultLang.code,
|
||||||
viewModeEnabled,
|
viewModeEnabled,
|
||||||
|
zenModeEnabled,
|
||||||
|
gridModeEnabled,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -66,6 +68,8 @@ const Excalidraw = (props: ExcalidrawProps) => {
|
|||||||
renderFooter={renderFooter}
|
renderFooter={renderFooter}
|
||||||
langCode={langCode}
|
langCode={langCode}
|
||||||
viewModeEnabled={viewModeEnabled}
|
viewModeEnabled={viewModeEnabled}
|
||||||
|
zenModeEnabled={zenModeEnabled}
|
||||||
|
gridModeEnabled={gridModeEnabled}
|
||||||
/>
|
/>
|
||||||
</IsMobileProvider>
|
</IsMobileProvider>
|
||||||
</InitializeApp>
|
</InitializeApp>
|
||||||
|
89
src/tests/excalidrawPackage.test.tsx
Normal file
89
src/tests/excalidrawPackage.test.tsx
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { fireEvent, GlobalTestState, render } from "./test-utils";
|
||||||
|
import Excalidraw from "../packages/excalidraw/index";
|
||||||
|
import { queryByText } from "@testing-library/react";
|
||||||
|
import { GRID_SIZE } from "../constants";
|
||||||
|
|
||||||
|
const { h } = window;
|
||||||
|
|
||||||
|
describe("<Excalidraw/>", () => {
|
||||||
|
describe("Test zenModeEnabled prop", () => {
|
||||||
|
it('should show exit zen mode button when zen mode is set and zen mode option in context menu when zenModeEnabled is "undefined"', async () => {
|
||||||
|
const { container } = await render(<Excalidraw />);
|
||||||
|
expect(
|
||||||
|
container.getElementsByClassName("disable-zen-mode--visible").length,
|
||||||
|
).toBe(0);
|
||||||
|
expect(h.state.zenModeEnabled).toBe(false);
|
||||||
|
|
||||||
|
fireEvent.contextMenu(GlobalTestState.canvas, {
|
||||||
|
button: 2,
|
||||||
|
clientX: 1,
|
||||||
|
clientY: 1,
|
||||||
|
});
|
||||||
|
const contextMenu = document.querySelector(".context-menu");
|
||||||
|
fireEvent.click(queryByText(contextMenu as HTMLElement, "Zen mode")!);
|
||||||
|
expect(h.state.zenModeEnabled).toBe(true);
|
||||||
|
expect(
|
||||||
|
container.getElementsByClassName("disable-zen-mode--visible").length,
|
||||||
|
).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not show exit zen mode button and zen mode option in context menu when zenModeEnabled is set", async () => {
|
||||||
|
const { container } = await render(<Excalidraw zenModeEnabled={true} />);
|
||||||
|
expect(
|
||||||
|
container.getElementsByClassName("disable-zen-mode--visible").length,
|
||||||
|
).toBe(0);
|
||||||
|
expect(h.state.zenModeEnabled).toBe(true);
|
||||||
|
|
||||||
|
fireEvent.contextMenu(GlobalTestState.canvas, {
|
||||||
|
button: 2,
|
||||||
|
clientX: 1,
|
||||||
|
clientY: 1,
|
||||||
|
});
|
||||||
|
const contextMenu = document.querySelector(".context-menu");
|
||||||
|
expect(queryByText(contextMenu as HTMLElement, "Zen mode")).toBe(null);
|
||||||
|
expect(h.state.zenModeEnabled).toBe(true);
|
||||||
|
expect(
|
||||||
|
container.getElementsByClassName("disable-zen-mode--visible").length,
|
||||||
|
).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Test gridModeEnabled prop", () => {
|
||||||
|
it('should show grid mode in context menu when gridModeEnabled is "undefined"', async () => {
|
||||||
|
const { container } = await render(<Excalidraw />);
|
||||||
|
expect(h.state.gridSize).toBe(null);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
container.getElementsByClassName("disable-zen-mode--visible").length,
|
||||||
|
).toBe(0);
|
||||||
|
fireEvent.contextMenu(GlobalTestState.canvas, {
|
||||||
|
button: 2,
|
||||||
|
clientX: 1,
|
||||||
|
clientY: 1,
|
||||||
|
});
|
||||||
|
const contextMenu = document.querySelector(".context-menu");
|
||||||
|
fireEvent.click(queryByText(contextMenu as HTMLElement, "Show grid")!);
|
||||||
|
expect(h.state.gridSize).toBe(GRID_SIZE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not show grid mode in context menu when gridModeEnabled is not "undefined"', async () => {
|
||||||
|
const { container } = await render(
|
||||||
|
<Excalidraw gridModeEnabled={false} />,
|
||||||
|
);
|
||||||
|
expect(h.state.gridSize).toBe(null);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
container.getElementsByClassName("disable-zen-mode--visible").length,
|
||||||
|
).toBe(0);
|
||||||
|
fireEvent.contextMenu(GlobalTestState.canvas, {
|
||||||
|
button: 2,
|
||||||
|
clientX: 1,
|
||||||
|
clientY: 1,
|
||||||
|
});
|
||||||
|
const contextMenu = document.querySelector(".context-menu");
|
||||||
|
expect(queryByText(contextMenu as HTMLElement, "Show grid")).toBe(null);
|
||||||
|
expect(h.state.gridSize).toBe(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -185,6 +185,8 @@ export interface ExcalidrawProps {
|
|||||||
renderFooter?: (isMobile: boolean) => JSX.Element;
|
renderFooter?: (isMobile: boolean) => JSX.Element;
|
||||||
langCode?: Language["code"];
|
langCode?: Language["code"];
|
||||||
viewModeEnabled?: boolean;
|
viewModeEnabled?: boolean;
|
||||||
|
zenModeEnabled?: boolean;
|
||||||
|
gridModeEnabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SceneData = {
|
export type SceneData = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user