Skip to content

Commit

Permalink
Save editor hidden status to Playroom url (#369)
Browse files Browse the repository at this point in the history
  • Loading branch information
felixhabib authored Oct 10, 2024
1 parent d74a3a8 commit 7aaa6d0
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 11 deletions.
7 changes: 7 additions & 0 deletions .changeset/ninety-pugs-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'playroom': minor
---

Save the state of the editor visibility to the Playroom URL.

This allows you to share a Playroom link with the editor either open or closed on load.
27 changes: 27 additions & 0 deletions cypress/e2e/urlHandling.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,23 @@ describe('URL handling', () => {

cy.title().should('eq', 'Test | Playroom');
});

it('editor hidden', () => {
cy.visit(
'http://localhost:9000/#?code=N4IgpgJglgLg9gJwBJQhMA7EAuGCCuYAvkA'
);

cy.get('textarea').should('not.be.focused');

// Todo - write a test that checks the CodeMirror element is not visible
/*
The CodeMirror element is not visible, but it is in the DOM
This test fails because the element doesn't meet Cypress's requirements for being hidden
Not sure why Cypress's hidden requirement isn't met
https://docs.cypress.io/guides/core-concepts/interacting-with-elements#An-element-is-considered-hidden-if
*/
// cy.get('.CodeMirror-code').should('be.hidden');
});
});

describe('where paramType is search', () => {
Expand Down Expand Up @@ -62,5 +79,15 @@ describe('URL handling', () => {

cy.title().should('eq', 'Test | Playroom');
});

it('editor hidden', () => {
cy.visit(
'http://localhost:9001/?code=N4IgpgJglgLg9gJwBJQhMA7EAuGCCuYAvkA'
);

cy.get('textarea').should('not.be.focused');

// Todo - write a test that checks the CodeMirror element is not visible
});
});
});
25 changes: 20 additions & 5 deletions src/Playroom/CodeEditor/CodeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import 'codemirror/addon/dialog/dialog.css';
import 'codemirror/theme/neo.css';

import {
StoreContext,
type CursorPosition,
StoreContext,
} from '../../StoreContext/StoreContext';
import { formatCode as format, isMac } from '../../utils/formatting';
import { validateCode } from '../../utils/compileJsx';
Expand Down Expand Up @@ -69,11 +69,18 @@ interface Hint {
interface Props {
code: string;
onChange: (code: string) => void;
editorHidden: boolean;
previewCode?: string;
hints?: Record<string, Hint>;
}

export const CodeEditor = ({ code, onChange, previewCode, hints }: Props) => {
export const CodeEditor = ({
code,
editorHidden,
onChange,
previewCode,
hints,
}: Props) => {
const editorInstanceRef = useRef<Editor | null>(null);
const insertionPointRef = useRef<ReturnType<Editor['addLineClass']> | null>(
null
Expand Down Expand Up @@ -168,17 +175,23 @@ export const CodeEditor = ({ code, onChange, previewCode, hints }: Props) => {
) {
return;
}

editorInstanceRef.current.setValue(code);
validateCodeInEditor(editorInstanceRef.current, code);
}
}, [code, previewCode]);

const mounted = useRef(false);

useEffect(() => {
if (!mounted.current) {
mounted.current = true;
return;
}

if (editorInstanceRef.current && !editorInstanceRef.current.hasFocus()) {
setCursorPosition(cursorPosition);
}
}, [cursorPosition, setCursorPosition]);
}, [cursorPosition, previewCode, setCursorPosition]);

useEffect(() => {
if (editorInstanceRef.current) {
Expand Down Expand Up @@ -212,7 +225,9 @@ export const CodeEditor = ({ code, onChange, previewCode, hints }: Props) => {
editorDidMount={(editorInstance) => {
editorInstanceRef.current = editorInstance;
validateCodeInEditor(editorInstance, code);
setCursorPosition(cursorPosition);
if (!editorHidden) {
setCursorPosition(cursorPosition);
}
}}
onChange={(editorInstance, data, newCode) => {
if (editorInstance.hasFocus() && !previewCode) {
Expand Down
1 change: 1 addition & 0 deletions src/Playroom/Playroom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export default ({ components, themes, widths, snippets }: PlayroomProps) => {
<div className={styles.editorContainer}>
<CodeEditor
code={code}
editorHidden={editorHidden}
onChange={(newCode: string) =>
dispatch({ type: 'updateCode', payload: { code: newCode } })
}
Expand Down
9 changes: 9 additions & 0 deletions src/StoreContext/StoreContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ interface DebounceUpdateUrl {
themes?: string[];
widths?: number[];
title?: string;
editorHidden?: boolean;
}

export interface CursorPosition {
Expand Down Expand Up @@ -478,6 +479,7 @@ export const StoreProvider = ({
let themesFromQuery: State['visibleThemes'];
let widthsFromQuery: State['visibleWidths'];
let titleFromQuery: State['title'];
let editorHiddenFromQuery: State['editorHidden'];

const paramsCode = params.get('code');
if (paramsCode) {
Expand All @@ -486,11 +488,13 @@ export const StoreProvider = ({
themes: parsedThemes,
widths: parsedWidths,
title: parsedTitle,
editorHidden: parsedEditorHidden,
} = JSON.parse(
lzString.decompressFromEncodedURIComponent(String(paramsCode)) ?? ''
);

codeFromQuery = parsedCode;
editorHiddenFromQuery = parsedEditorHidden;
themesFromQuery = parsedThemes;
widthsFromQuery = parsedWidths;
titleFromQuery = parsedTitle;
Expand Down Expand Up @@ -527,6 +531,8 @@ export const StoreProvider = ({
? convertAndStoreSizeAsPercentage('width', storedWidth)
: storedWidth) || defaultEditorSize;

const editorHidden = editorHiddenFromQuery === true;

const visibleWidths =
widthsFromQuery ||
storedVisibleWidths ||
Expand All @@ -547,6 +553,7 @@ export const StoreProvider = ({
...(editorPosition ? { editorPosition } : {}),
...(editorHeight ? { editorHeight } : {}),
...(editorWidth ? { editorWidth } : {}),
...(editorHidden ? { editorHidden } : {}),
...(visibleThemes ? { visibleThemes } : {}),
...(visibleWidths ? { visibleWidths } : {}),
...(colorScheme ? { colorScheme } : {}),
Expand Down Expand Up @@ -582,12 +589,14 @@ export const StoreProvider = ({
themes: state.visibleThemes,
widths: state.visibleWidths,
title: state.title,
editorHidden: state.editorHidden,
});
}, [
state.code,
state.visibleThemes,
state.visibleWidths,
state.title,
state.editorHidden,
debouncedCodeUpdate,
]);

Expand Down
1 change: 1 addition & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ interface PlayroomConfig {
components: string;
outputPath: string;
title?: string;
editorHidden?: boolean;
themes?: string;
widths?: number[];
snippets?: Snippet[];
Expand Down
3 changes: 2 additions & 1 deletion src/utils/usePreviewUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const baseUrl = window.location.href
.split('index.html')[0];

export default (theme: string) => {
const [{ code, title }] = useContext(StoreContext);
const [{ code, title, editorHidden }] = useContext(StoreContext);

const isThemed = theme !== '__PLAYROOM__NO_THEME__';

Expand All @@ -19,5 +19,6 @@ export default (theme: string) => {
theme: isThemed ? theme : undefined,
paramType: playroomConfig.paramType,
title,
editorHidden,
});
};
3 changes: 3 additions & 0 deletions utils/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface CompressParamsOptions {
widths?: number[];
theme?: string;
title?: string;
editorHidden?: boolean;
}
export const compressParams: (options: CompressParamsOptions) => string;

Expand All @@ -24,6 +25,7 @@ interface CreateUrlOptions {
widths?: number[];
paramType?: ParamType;
title?: string;
editorHidden?: boolean;
}

export const createUrl: (options: CreateUrlOptions) => string;
Expand All @@ -34,6 +36,7 @@ interface CreatePreviewUrlOptions {
theme?: string;
paramType?: ParamType;
title?: string;
editorHidden?: boolean;
}

export const createPreviewUrl: (options: CreatePreviewUrlOptions) => string;
26 changes: 21 additions & 5 deletions utils/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
const lzString = require('lz-string');

const compressParams = ({ code, themes, widths, theme, title }) => {
const compressParams = ({
code,
themes,
widths,
theme,
title,
editorHidden,
}) => {
const data = JSON.stringify({
...(code ? { code } : {}),
...(themes ? { themes } : {}),
...(widths ? { widths } : {}),
...(theme ? { theme } : {}),
...(title ? { title } : {}),
...(editorHidden ? { editorHidden } : {}),
});

return lzString.compressToEncodedURIComponent(data);
Expand All @@ -18,12 +26,19 @@ const createUrl = ({
themes,
widths,
title,
editorHidden,
paramType = 'hash',
}) => {
let path = '';

if (code || themes || widths || title) {
const compressedData = compressParams({ code, themes, widths, title });
if (code || themes || widths || title || editorHidden) {
const compressedData = compressParams({
code,
themes,
widths,
title,
editorHidden,
});

path = `${paramType === 'hash' ? '#' : ''}?code=${compressedData}`;
}
Expand All @@ -42,12 +57,13 @@ const createPreviewUrl = ({
code,
theme,
title,
editorHidden,
paramType = 'hash',
}) => {
let path = '';

if (code || theme || title) {
const compressedData = compressParams({ code, theme, title });
if (code || theme || title || editorHidden) {
const compressedData = compressParams({ code, theme, title, editorHidden });

path = `/preview/${paramType === 'hash' ? '#' : ''}?code=${compressedData}`;
}
Expand Down

0 comments on commit 7aaa6d0

Please sign in to comment.