Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save editor hidden status to Playroom url #369

Merged
merged 4 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
felixhabib marked this conversation as resolved.
Show resolved Hide resolved
/*
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be worth clarifying that we don't know what's causing it to fail the hidden requirement either.

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