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:
Aakansha Doshi 2021-02-06 21:22:28 +05:30 committed by GitHub
parent d6ca981f7a
commit 066560311b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 144 additions and 5 deletions

View File

@ -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,8 +3746,10 @@ class App extends React.Component<ExcalidrawProps, AppState> {
separator, separator,
actionSelectAll, actionSelectAll,
separator, separator,
actionToggleGridMode, typeof this.props.gridModeEnabled === "undefined" &&
actionToggleZenMode, actionToggleGridMode,
typeof this.props.zenModeEnabled === "undefined" &&
actionToggleZenMode,
typeof this.props.viewModeEnabled === "undefined" && typeof this.props.viewModeEnabled === "undefined" &&
actionToggleViewMode, actionToggleViewMode,
actionToggleStats, actionToggleStats,

View File

@ -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}
> >

View File

@ -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).

View File

@ -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.

View File

@ -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>

View 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);
});
});
});

View File

@ -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 = {