Skip to content

Commit

Permalink
Save editor hidden status to playroom url
Browse files Browse the repository at this point in the history
  • Loading branch information
felixhabib committed Oct 8, 2024
1 parent 0c80366 commit 9323cbb
Show file tree
Hide file tree
Showing 9 changed files with 96 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.
32 changes: 32 additions & 0 deletions cypress/e2e/urlHandling.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ 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
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 +78,21 @@ 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
/*
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
https://docs.cypress.io/guides/core-concepts/interacting-with-elements#An-element-is-considered-hidden-if
*/
// cy.get('.CodeMirror-code').should('be.hidden');
});
});
});
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 9323cbb

Please sign in to comment.